diff --git a/.gitignore b/.gitignore index 4b6949e268..c1eef3817f 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,9 @@ TAGS *.sw[po] *.qmlc +# ignore QML compilation output +*.qmlc + # ignore node files for the console node_modules npm-debug.log diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 3db48602b1..4cc5a0fe4f 100644 Binary files a/interface/resources/fonts/hifi-glyphs.ttf and b/interface/resources/fonts/hifi-glyphs.ttf differ diff --git a/interface/resources/images/lowerKeyboard.png b/interface/resources/images/lowerKeyboard.png new file mode 100644 index 0000000000..d379b028ab Binary files /dev/null and b/interface/resources/images/lowerKeyboard.png differ diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index 54834549ae..6c6d2a4350 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -5,10 +5,16 @@ Item { id: keyItem width: 45 height: 50 + + property int contentPadding: 4 property string glyph: "a" property bool toggle: false // does this button have the toggle behaivor? property bool toggled: false // is this button currently toggled? property alias mouseArea: mouseArea1 + property alias fontFamily: letter.font.family; + property alias fontPixelSize: letter.font.pixelSize + property alias verticalAlignment: letter.verticalAlignment + property alias letterAnchors: letter.anchors function resetToggledMode(mode) { toggled = mode; @@ -105,14 +111,8 @@ Item { color: "#121212" radius: 2 border.color: "#00000000" - anchors.right: parent.right - anchors.rightMargin: 4 - anchors.left: parent.left - anchors.leftMargin: 4 - anchors.bottom: parent.bottom - anchors.bottomMargin: 4 - anchors.top: parent.top - anchors.topMargin: 4 + anchors.fill: parent + anchors.margins: contentPadding } Text { diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 66a61742c9..76b66178d4 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -8,7 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.0 +import QtQuick 2.7 +import QtGraphicalEffects 1.0 import "." Rectangle { @@ -55,6 +56,8 @@ Rectangle { return ">"; } else if (str === "/") { return "?"; + } else if (str === "-") { + return "_"; } else { return str.toUpperCase(str); } @@ -67,6 +70,8 @@ Rectangle { return "."; } else if (str === "?") { return "/"; + } else if (str === "_") { + return "-"; } else { return str.toLowerCase(str); } @@ -85,7 +90,7 @@ Rectangle { onShiftModeChanged: { forEachKey(function (key) { - if (/[a-z]/i.test(key.glyph)) { + if (/[a-z-_]/i.test(key.glyph)) { if (shiftMode) { key.glyph = keyboardBase.toUpper(key.glyph); } else { @@ -112,8 +117,6 @@ Rectangle { } Rectangle { - y: 0 - x: 0 height: showMirrorText ? mirrorTextHeight : 0 width: keyboardWidth color: "#252525" @@ -122,13 +125,18 @@ Rectangle { TextInput { id: mirrorText visible: showMirrorText - FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } - font.family: ralewaySemiBold.name - font.pointSize: 13.5 + FontLoader { id: font; source: "../../fonts/FiraSans-Regular.ttf"; } + font.family: font.name + font.pixelSize: 20 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - color: "#FFFFFF"; - anchors.fill: parent + color: "#00B4EF"; + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + wrapMode: Text.WordWrap readOnly: false // we need this to allow control to accept QKeyEvent selectByMouse: false @@ -140,16 +148,15 @@ Rectangle { event.accepted = true; } } - } - MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus - anchors.fill: parent + MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus + anchors.fill: parent + } } } Rectangle { id: keyboardRect - x: 0 y: showMirrorText ? mirrorTextHeight : 0 width: keyboardWidth height: raisedHeight @@ -158,6 +165,8 @@ Rectangle { anchors.bottom: parent.bottom anchors.bottomMargin: 0 + FontLoader { id: hiFiGlyphs; source: pathToFonts + "fonts/hifi-glyphs.ttf"; } + Column { id: columnAlpha width: keyboardWidth @@ -221,7 +230,7 @@ Rectangle { Key { width: 43; glyph: "b"; } Key { width: 43; glyph: "n"; } Key { width: 43; glyph: "m"; } - Key { width: 43; glyph: "_"; } + Key { width: 43; glyph: "-"; } Key { width: 43; glyph: "/"; } Key { width: 43; glyph: "?"; } } @@ -240,8 +249,13 @@ Rectangle { Key { width: 231; glyph: " "; } Key { width: 43; glyph: ","; } Key { width: 43; glyph: "."; } - Key { width: 43; glyph: "\u276C"; } - Key { width: 43; glyph: "\u276D"; } + Key { + fontFamily: hiFiGlyphs.name; + fontPixelSize: 48; + letterAnchors.topMargin: -4; + verticalAlignment: Text.AlignVCenter; + width: 86; glyph: "\ue02b"; + } } } @@ -328,8 +342,13 @@ Rectangle { Key { width: 231; glyph: " "; } Key { width: 43; glyph: ","; } Key { width: 43; glyph: "."; } - Key { width: 43; glyph: "\u276C"; } - Key { width: 43; glyph: "\u276D"; } + Key { + fontFamily: hiFiGlyphs.name; + fontPixelSize: 48; + letterAnchors.topMargin: -4; + verticalAlignment: Text.AlignVCenter; + width: 86; glyph: "\ue02b"; + } } } } diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 9e1751ee51..de6bf66db3 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -27,6 +27,12 @@ Item { id: hifi } + function unfocus() { + webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) { + console.log('unfocus completed: ', result); + }); + } + function onLoadingChanged(loadRequest) { if (WebEngineView.LoadStartedStatus === loadRequest.status) { diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index e06ff51569..501e321f0d 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -10,6 +10,11 @@ Item { property alias urlTag: webroot.urlTag property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false property bool keyboardRaised: false + onKeyboardRaisedChanged: { + if(!keyboardRaised) { + webroot.unfocus(); + } + } property bool punctuationMode: false // FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 8cd61bc90b..477422cfa1 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -15,6 +15,11 @@ Item { property string scriptURL property bool keyboardEnabled: false property bool keyboardRaised: false + onKeyboardRaisedChanged: { + if(!keyboardRaised) { + webroot.unfocus(); + } + } property bool punctuationMode: false property bool passwordField: false property bool isDesktop: false diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 923c8f3fa1..931c64e1ef 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -12,6 +12,11 @@ Item { property alias urlTag: webroot.urlTag property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false property bool keyboardRaised: false + onKeyboardRaisedChanged: { + if(!keyboardRaised) { + webroot.unfocus(); + } + } property bool punctuationMode: false property bool passwordField: false property alias flickable: webroot.interactive diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 8a5efb93f8..b7b3090ae3 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -476,9 +476,7 @@ Rectangle { Commerce.buy(itemId, itemPrice, true); } } else { - if (urlHandler.canHandleUrl(itemHref)) { - urlHandler.handleUrl(itemHref); - } + sendToScript({method: 'checkout_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); } } } diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index fb42865ba4..15ebada0c4 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -346,9 +346,7 @@ Item { enabled: (root.canRezCertifiedItems || root.isWearable) && root.purchaseStatus !== "invalidated"; onClicked: { - if (urlHandler.canHandleUrl(root.itemHref)) { - urlHandler.handleUrl(root.itemHref); - } + sendToPurchases({method: 'purchases_rezClicked', itemHref: root.itemHref, isWearable: root.isWearable}); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index b88710d19c..a3ed04a8f5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -442,6 +442,8 @@ Rectangle { onSendToPurchases: { if (msg.method === 'purchases_itemInfoClicked') { sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId}); + } else if (msg.method === "purchases_rezClicked") { + sendToScript({method: 'purchases_rezClicked', itemHref: itemHref, isWearable: isWearable}); } else if (msg.method === 'purchases_itemCertificateClicked') { inspectionCertificate.visible = true; inspectionCertificate.isLightbox = true; diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 80c1b58444..83f91c78c5 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -32,6 +32,8 @@ Rectangle { color: hifi.colors.baseGray + property bool keyboardEnabled: HMD.active + property bool keyboardRaised: false LetterboxMessage { id: letterBoxMessage @@ -380,7 +382,7 @@ Rectangle { Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i") onActiveFocusChanged: { // raise the keyboard - keyboard.raised = activeFocus; + root.keyboardRaised = activeFocus; // scroll to the bottom of the content area. if (activeFocus) { @@ -481,7 +483,7 @@ Rectangle { HifiControls.Keyboard { id: keyboard - raised: false + raised: parent.keyboardEnabled && parent.keyboardRaised numeric: false anchors { bottom: parent.bottom diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 4d9a83817a..649a8e6259 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -366,7 +366,7 @@ StackView { HifiControls.Keyboard { id: keyboard - raised: parent.keyboardEnabled + raised: parent.keyboardEnabled && parent.keyboardRaised numeric: parent.punctuationMode anchors { bottom: parent.bottom diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 32dd74279b..71a547533b 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -15,7 +15,7 @@ #include "RayPickScriptingInterface.h" LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) : + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) : _renderingEnabled(enabled), _renderStates(renderStates), _defaultRenderStates(defaultRenderStates), @@ -23,6 +23,7 @@ LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& rende _centerEndY(centerEndY), _lockEnd(lockEnd), _distanceScaleEnd(distanceScaleEnd), + _scaleWithAvatar(scaleWithAvatar), _rayPickUID(DependencyManager::get()->createRayPick(rayProps)) { @@ -94,6 +95,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta if (endDim.isValid()) { _renderStates[state].setEndDim(vec3FromVariant(endDim)); } + QVariant lineWidth = pathProps.toMap()["lineWidth"]; + if (lineWidth.isValid()) { + _renderStates[state].setLineWidth(lineWidth.toFloat()); + } }); } @@ -152,6 +157,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter } } } + QVariant end = vec3toVariant(endVec); if (!renderState.getPathID().isNull()) { QVariantMap pathProps; @@ -159,6 +165,9 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter pathProps.insert("end", end); pathProps.insert("visible", true); pathProps.insert("ignoreRayIntersection", renderState.doesPathIgnoreRays()); + if (_scaleWithAvatar) { + pathProps.insert("lineWidth", renderState.getLineWidth() * DependencyManager::get()->getMyAvatar()->getSensorToWorldScale()); + } qApp->getOverlays().editOverlay(renderState.getPathID(), pathProps); } if (!renderState.getEndID().isNull()) { @@ -166,7 +175,7 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter glm::quat faceAvatarRotation = DependencyManager::get()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f))); glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value); if (_distanceScaleEnd) { - dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); + dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec); endProps.insert("dimensions", vec3toVariant(dim)); } if (_centerEndY) { @@ -258,6 +267,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons } if (!_pathID.isNull()) { _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool(); + _lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat(); } if (!_endID.isNull()) { _endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value); diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 896752a96e..d38070c05c 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -43,6 +43,9 @@ public: void setEndDim(const glm::vec3& endDim) { _endDim = endDim; } const glm::vec3& getEndDim() const { return _endDim; } + void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; } + const float& getLineWidth() const { return _lineWidth; } + void deleteOverlays(); private: @@ -54,6 +57,7 @@ private: bool _endIgnoreRays; glm::vec3 _endDim; + float _lineWidth; }; @@ -66,7 +70,7 @@ public: typedef std::unordered_map> DefaultRenderStateMap; LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled); ~LaserPointer(); QUuid getRayUID() { return _rayPickUID; } @@ -97,6 +101,7 @@ private: bool _centerEndY; bool _lockEnd; bool _distanceScaleEnd; + bool _scaleWithAvatar; LockEndObject _lockEndObject; const QUuid _rayPickUID; diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index 45420d1488..143451d0d3 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -11,9 +11,9 @@ #include "LaserPointerManager.h" QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) { + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled) { QUuid result; - std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled); + std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled); if (!laserPointer->getRayUID().isNull()) { result = QUuid::createUuid(); withWriteLock([&] { _laserPointers[result] = laserPointer; }); diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index 25089a291a..3f8f962679 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -25,7 +25,7 @@ class LaserPointerManager : protected ReadWriteLockable { public: QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool scaleWithAvatar, const bool enabled); void removeLaserPointer(const QUuid& uid); void enableLaserPointer(const QUuid& uid) const; diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index e8d28bfab2..eb69d610ad 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -51,6 +51,11 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert enabled = propertyMap["enabled"].toBool(); } + bool scaleWithAvatar = false; + if (propertyMap["scaleWithAvatar"].isValid()) { + scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool(); + } + LaserPointer::RenderStateMap renderStates; if (propertyMap["renderStates"].isValid()) { QList renderStateVariants = propertyMap["renderStates"].toList(); @@ -80,7 +85,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert } } - return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled); + return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled); } void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const { diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index d157e29959..4412014eb1 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -16,13 +16,11 @@ #include "Application.h" -const float DEFAULT_LINE_WIDTH = 1.0f; const bool DEFAULT_IS_SOLID = false; const bool DEFAULT_IS_DASHED_LINE = false; Base3DOverlay::Base3DOverlay() : SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), - _lineWidth(DEFAULT_LINE_WIDTH), _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), _ignoreRayIntersection(false), @@ -34,7 +32,6 @@ Base3DOverlay::Base3DOverlay() : Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : Overlay(base3DOverlay), SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), - _lineWidth(base3DOverlay->_lineWidth), _isSolid(base3DOverlay->_isSolid), _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), @@ -153,12 +150,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { setLocalOrientation(quatFromVariant(properties["orientation"])); needRenderItemUpdate = true; } - - if (properties["lineWidth"].isValid()) { - setLineWidth(properties["lineWidth"].toFloat()); - needRenderItemUpdate = true; - } - if (properties["isSolid"].isValid()) { setIsSolid(properties["isSolid"].toBool()); } @@ -225,9 +216,6 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "localRotation" || property == "localOrientation") { return quatToVariant(getLocalOrientation()); } - if (property == "lineWidth") { - return _lineWidth; - } if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") { return _isSolid; } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 83c5873260..2f6e6db9dc 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -38,7 +38,6 @@ public: // TODO: consider implementing registration points in this class glm::vec3 getCenter() const { return getPosition(); } - float getLineWidth() const { return _lineWidth; } bool getIsSolid() const { return _isSolid; } bool getIsDashedLine() const { return _isDashedLine; } bool getIsSolidLine() const { return !_isDashedLine; } @@ -47,7 +46,6 @@ public: bool getDrawHUDLayer() const { return _drawHUDLayer; } bool getIsGrabbable() const { return _isGrabbable; } - void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; } @@ -85,7 +83,6 @@ protected: void setRenderVisible(bool visible); const Transform& getRenderTransform() const { return _renderTransform; } - float _lineWidth; bool _isSolid; bool _isDashedLine; bool _ignoreRayIntersection; diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 5ef820b2e0..d59e382613 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -33,8 +33,8 @@ Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : _length = line3DOverlay->getLength(); _endParentID = line3DOverlay->getEndParentID(); _endParentJointIndex = line3DOverlay->getEndJointIndex(); + _lineWidth = line3DOverlay->getLineWidth(); _glow = line3DOverlay->getGlow(); - _glowWidth = line3DOverlay->getGlowWidth(); } Line3DOverlay::~Line3DOverlay() { @@ -145,7 +145,7 @@ void Line3DOverlay::render(RenderArgs* args) { geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID); } else { // renderGlowLine handles both glow = 0 and glow > 0 cases - geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _glowWidth, _geometryCacheID); + geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID); } } } @@ -239,11 +239,10 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { } } - auto glowWidth = properties["glowWidth"]; - if (glowWidth.isValid()) { - setGlowWidth(glowWidth.toFloat()); + auto lineWidth = properties["lineWidth"]; + if (lineWidth.isValid()) { + setLineWidth(lineWidth.toFloat()); } - } QVariant Line3DOverlay::getProperty(const QString& property) { @@ -262,6 +261,9 @@ QVariant Line3DOverlay::getProperty(const QString& property) { if (property == "length") { return QVariant(getLength()); } + if (property == "lineWidth") { + return _lineWidth; + } return Base3DOverlay::getProperty(property); } diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index bcb65b1f1e..79af937f23 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -31,8 +31,8 @@ public: // getters glm::vec3 getStart() const; glm::vec3 getEnd() const; + const float& getLineWidth() const { return _lineWidth; } const float& getGlow() const { return _glow; } - const float& getGlowWidth() const { return _glowWidth; } // setters void setStart(const glm::vec3& start); @@ -41,8 +41,8 @@ public: void setLocalStart(const glm::vec3& localStart) { setLocalPosition(localStart); } void setLocalEnd(const glm::vec3& localEnd); + void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; } void setGlow(const float& glow) { _glow = glow; } - void setGlowWidth(const float& glowWidth) { _glowWidth = glowWidth; } void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -70,8 +70,9 @@ private: glm::vec3 _direction; // in parent frame float _length { 1.0 }; // in parent frame + const float DEFAULT_LINE_WIDTH = 0.02f; + float _lineWidth { DEFAULT_LINE_WIDTH }; float _glow { 0.0 }; - float _glowWidth { 0.0 }; int _geometryCacheID; }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 7c96f00ede..54cc888c35 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -40,22 +40,26 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity if (_stage) { if (!LightStage::isIndexInvalid(_sunIndex)) { _stage->removeLight(_sunIndex); + _sunIndex = INVALID_INDEX; + _shadowIndex = INVALID_INDEX; } if (!LightStage::isIndexInvalid(_ambientIndex)) { _stage->removeLight(_ambientIndex); - + _ambientIndex = INVALID_INDEX; } } if (_backgroundStage) { if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) { _backgroundStage->removeBackground(_backgroundIndex); + _backgroundIndex = INVALID_INDEX; } } if (_hazeStage) { if (!HazeStage::isIndexInvalid(_hazeIndex)) { _hazeStage->removeHaze(_hazeIndex); + _hazeIndex = INVALID_INDEX; } } } diff --git a/libraries/gpu/src/gpu/DrawColor.slf b/libraries/gpu/src/gpu/DrawColor.slf new file mode 100644 index 0000000000..c24d69d29f --- /dev/null +++ b/libraries/gpu/src/gpu/DrawColor.slf @@ -0,0 +1,19 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Draw with color uniform +// +// Created by Olivier Prat on 25/10/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +uniform vec4 color; + +out vec4 outFragColor; + +void main(void) { + outFragColor = color; +} diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp index 7143242618..0d8d131e0b 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.cpp +++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp @@ -22,6 +22,7 @@ const char DrawNada_frag[] = "void main(void) {}"; // DrawNada is really simple... #include "DrawWhite_frag.h" +#include "DrawColor_frag.h" #include "DrawTexture_frag.h" #include "DrawTextureMirroredX_frag.h" #include "DrawTextureOpaque_frag.h" @@ -37,6 +38,7 @@ ShaderPointer StandardShaderLib::_drawVertexPositionVS; ShaderPointer StandardShaderLib::_drawTransformVertexPositionVS; ShaderPointer StandardShaderLib::_drawNadaPS; ShaderPointer StandardShaderLib::_drawWhitePS; +ShaderPointer StandardShaderLib::_drawColorPS; ShaderPointer StandardShaderLib::_drawTexturePS; ShaderPointer StandardShaderLib::_drawTextureMirroredXPS; ShaderPointer StandardShaderLib::_drawTextureOpaquePS; @@ -125,6 +127,13 @@ ShaderPointer StandardShaderLib::getDrawWhitePS() { return _drawWhitePS; } +ShaderPointer StandardShaderLib::getDrawColorPS() { + if (!_drawColorPS) { + _drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag)); + } + return _drawColorPS; +} + ShaderPointer StandardShaderLib::getDrawTexturePS() { if (!_drawTexturePS) { _drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag)); diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h index 94885b8ca0..9c11f6cc3a 100755 --- a/libraries/gpu/src/gpu/StandardShaderLib.h +++ b/libraries/gpu/src/gpu/StandardShaderLib.h @@ -46,6 +46,7 @@ public: static ShaderPointer getDrawNadaPS(); static ShaderPointer getDrawWhitePS(); + static ShaderPointer getDrawColorPS(); static ShaderPointer getDrawTexturePS(); static ShaderPointer getDrawTextureMirroredXPS(); static ShaderPointer getDrawTextureOpaquePS(); @@ -67,6 +68,7 @@ protected: static ShaderPointer _drawNadaPS; static ShaderPointer _drawWhitePS; + static ShaderPointer _drawColorPS; static ShaderPointer _drawTexturePS; static ShaderPointer _drawTextureMirroredXPS; static ShaderPointer _drawTextureOpaquePS; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 2187cb70b1..646b19198b 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -105,13 +105,13 @@ void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Bat PerformanceTimer perfTimer("DLE->setupBatch()"); model::LightPointer keySunLight; auto lightStage = args->_scene->getStage(); - if (lightStage && lightStage->_currentFrame._sunLights.size()) { - keySunLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front()); + if (lightStage) { + keySunLight = lightStage->getCurrentKeyLight(); } model::LightPointer keyAmbiLight; - if (lightStage && lightStage->_currentFrame._ambientLights.size()) { - keyAmbiLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front()); + if (lightStage) { + keyAmbiLight = lightStage->getCurrentAmbientLight(); } if (keySunLight) { @@ -620,7 +620,7 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext auto& lightIndices = lightClusters->_visibleLightIndices; if (!lightIndices.empty() && lightIndices[0] > 0) { // Bind the global list of lights and the visible lights this frame - batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->_lightArrayBuffer); + batch.setUniformBuffer(deferredLightingEffect->_localLightLocations->lightBufferUnit, lightClusters->_lightStage->getLightArrayBuffer()); batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer); batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer); diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index f694a93033..4431c1bbc3 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -175,9 +175,9 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu batch.setUniformBuffer(HazeEffect_TransformBufferSlot, transformBuffer->getFrameTransformBuffer()); auto lightStage = args->_scene->getStage(); - if (lightStage && lightStage->_currentFrame._sunLights.size() > 0) { - model::LightPointer keyLight; - keyLight = lightStage->getLight(lightStage->_currentFrame._sunLights.front()); + if (lightStage) { + model::LightPointer keyLight; + keyLight = lightStage->getCurrentKeyLight(); if (keyLight != nullptr) { batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer()); } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index fa00737e3c..ebf0f13d97 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1931,9 +1931,10 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const vec4 p1; vec4 p2; vec4 color; + float width; }; - LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color }; + LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth }; details.uniformBuffer->resize(sizeof(LineData)); details.uniformBuffer->setSubData(0, lineData); } diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index ab1e194498..eedb9053c7 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -727,7 +727,7 @@ void DebugLightClusters::run(const render::RenderContextPointer& renderContext, batch.setModelTransform(Transform()); // Bind the Light CLuster data strucutre - batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->_lightArrayBuffer); + batch.setUniformBuffer(LIGHT_GPU_SLOT, lightClusters->_lightStage->getLightArrayBuffer()); batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer); batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, lightClusters->_clusterGridBuffer); batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, lightClusters->_clusterContentBuffer); diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index c280abfeaf..ba705e56cb 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -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() } { - framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); - map = framebuffer->getDepthStencilBuffer(); - Schema schema; - _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); +LightStage::Shadow::Schema::Schema() : + bias{ 0.005f }, + scale{ 1.0f / MAP_SIZE } { + } -void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) { +gpu::FramebufferPointer LightStage::Shadow::framebuffer; +gpu::TexturePointer LightStage::Shadow::map; + +LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } { + Schema schema; + _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); + + if (!framebuffer) { + framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); + map = framebuffer->getDepthStencilBuffer(); + } +} + +void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, + float viewMinShadowDistance, float viewMaxShadowDistance, + float nearDepth, float farDepth) { + assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); // Orient the keylight frustum @@ -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(min.x, max.x, min.y, max.y, -max.z, -min.z); + // Re-adjust near shadow distance + auto near = glm::max(max.z, -nearDepth); + auto far = -min.z; + glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); _frustum->setProjection(ortho); // Calculate the frustum's internal state @@ -85,6 +103,16 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); } +void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { + const Transform view{ shadowFrustum.getView() }; + const Transform viewInverse{ view.getInverseMatrix() }; + + *_frustum = shadowFrustum; + // Update the buffer + _schemaBuffer.edit().projection = shadowFrustum.getProjection(); + _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); +} + const glm::mat4& LightStage::Shadow::getView() const { return _frustum->getView(); } @@ -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(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) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index c26f504658..fa581c8315 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -48,8 +48,9 @@ public: Shadow(model::LightPointer light); - void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth); + void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + void setFrustum(const ViewFrustum& shadowFrustum); const std::shared_ptr getFrustum() const { return _frustum; } const glm::mat4& getView() const; @@ -57,32 +58,36 @@ public: const UniformBufferView& getBuffer() const { return _schemaBuffer; } - gpu::FramebufferPointer framebuffer; - gpu::TexturePointer map; + // Shadow maps are shared among all lights for the moment as only one key light + // is used. + static gpu::FramebufferPointer framebuffer; + static gpu::TexturePointer map; + + const model::LightPointer& getLight() const { return _light; } + protected: + model::LightPointer _light; std::shared_ptr _frustum; class Schema { public: + + Schema(); + glm::mat4 projection; glm::mat4 viewInverse; - glm::float32 bias = 0.005f; - glm::float32 scale = 1 / MAP_SIZE; + glm::float32 bias; + glm::float32 scale; }; UniformBufferView _schemaBuffer = nullptr; - friend class Light; }; + using ShadowPointer = std::shared_ptr; using Shadows = render::indexed_container::IndexedPointerVector; - struct Desc { - Index shadowId { INVALID_INDEX }; - }; - using Descs = std::vector; - Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); @@ -100,50 +105,29 @@ public: return _lights.get(lightId); } - Index getShadowId(Index lightId) const { - if (checkLightId(lightId)) { - return _descs[lightId].shadowId; - } else { - return INVALID_INDEX; - } - } + Index getShadowId(Index lightId) const; + ShadowPointer getShadow(Index lightId) const { return _shadows.get(getShadowId(lightId)); } using LightAndShadow = std::pair; LightAndShadow getLightAndShadow(Index lightId) const { - return LightAndShadow(getLight(lightId), getShadow(lightId)); + auto light = getLight(lightId); + auto shadow = getShadow(lightId); + assert(shadow == nullptr || shadow->getLight() == light); + return LightAndShadow(light, shadow); } - LightPointer getCurrentKeyLight() const { - Index keyLightId{ 0 }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); - } - return _lights.get(keyLightId); - } - - ShadowPointer getCurrentKeyShadow() const { - Index keyLightId{ 0 }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); - } - return getShadow(keyLightId); - } - - LightAndShadow getCurrentKeyLightAndShadow() const { - Index keyLightId{ 0 }; - if (!_currentFrame._sunLights.empty()) { - keyLightId = _currentFrame._sunLights.front(); - } - return LightAndShadow(getLight(keyLightId), getShadow(keyLightId)); - } + LightPointer getCurrentKeyLight() const; + LightPointer getCurrentAmbientLight() const; + ShadowPointer getCurrentKeyShadow() const; + LightAndShadow getCurrentKeyLightAndShadow() const; LightStage(); - Lights _lights; - LightMap _lightMap; - Descs _descs; + + gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; } + void updateLightArrayBuffer(Index lightId); class Frame { public: @@ -172,15 +156,24 @@ public: Frame _currentFrame; - gpu::BufferPointer _lightArrayBuffer; - void updateLightArrayBuffer(Index lightId); +protected: + struct Desc { + Index shadowId{ INVALID_INDEX }; + }; + using Descs = std::vector; + + gpu::BufferPointer _lightArrayBuffer; + + Lights _lights; Shadows _shadows; + Descs _descs; + LightMap _lightMap; + }; using LightStagePointer = std::shared_ptr; - class LightStageSetup { public: using JobModel = render::Job::Model; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 1f839b25eb..4261b14a9b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -44,8 +44,6 @@ #include "DrawHaze.h" #include "HighlightEffect.h" -#include - #include using namespace render; @@ -193,13 +191,18 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("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("DrawMetaBounds", metas); task.addJob("DrawOpaqueBounds", opaques); task.addJob("DrawTransparentBounds", transparents); task.addJob("DrawLightBounds", lights); task.addJob("DrawZones", zones); + const auto frustums = task.addJob("ExtractFrustums"); + const auto viewFrustum = frustums.getN(ExtractFrustums::VIEW_FRUSTUM); + const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_FRUSTUM); + task.addJob("DrawViewFrustum", viewFrustum, glm::vec3(1.0f, 1.0f, 0.0f)); + task.addJob("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, 0.0f, 1.0f)); // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true task.addJob("DrawSelectionBounds", selectedItems); @@ -448,6 +451,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 +535,32 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer }); } +void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { + assert(renderContext->args); + assert(renderContext->args->_context); + + RenderArgs* args = renderContext->args; + + // Return view frustum + auto& viewFrustum = output[VIEW_FRUSTUM].edit(); + if (!viewFrustum) { + viewFrustum = std::make_shared(args->getViewFrustum()); + } else { + *viewFrustum = args->getViewFrustum(); + } + + // Return shadow frustum + auto& shadowFrustum = output[SHADOW_FRUSTUM].edit(); + auto lightStage = args->_scene->getStage(LightStage::getName()); + if (lightStage) { + auto globalShadow = lightStage->getCurrentKeyShadow(); + + if (globalShadow) { + shadowFrustum = globalShadow->getFrustum(); + } else { + shadowFrustum.reset(); + } + } else { + shadowFrustum.reset(); + } +} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 567e7f6ccd..40ae503fb7 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -170,6 +170,22 @@ public: void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); }; +class ExtractFrustums { +public: + + enum Frustum { + VIEW_FRUSTUM, + SHADOW_FRUSTUM, + + FRUSTUM_COUNT + }; + + using Output = render::VaryingArray; + using JobModel = render::Job::ModelO; + + void run(const render::RenderContextPointer& renderContext, Output& output); +}; + class RenderDeferredTaskConfig : public render::Task::Config { Q_OBJECT Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7171543abc..7a6e3dc74f 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -22,25 +22,136 @@ #include "DeferredLightingEffect.h" #include "FramebufferCache.h" +// These values are used for culling the objects rendered in the shadow map +// but are readjusted afterwards +#define SHADOW_FRUSTUM_NEAR 1.0f +#define SHADOW_FRUSTUM_FAR 500.0f + using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); -void RenderShadowMap::run(const render::RenderContextPointer& renderContext, - const render::ShapeBounds& inShapes) { +static void computeNearFar(const Triangle& triangle, const Plane shadowClipPlanes[4], float& near, float& far) { + static const int MAX_TRIANGLE_COUNT = 16; + Triangle clippedTriangles[MAX_TRIANGLE_COUNT]; + auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT); + + for (auto i = 0; i < clippedTriangleCount; i++) { + const auto& clippedTriangle = clippedTriangles[i]; + + near = glm::min(near, -clippedTriangle.v0.z); + near = glm::min(near, -clippedTriangle.v1.z); + near = glm::min(near, -clippedTriangle.v2.z); + + far = glm::max(far, -clippedTriangle.v0.z); + far = glm::max(far, -clippedTriangle.v1.z); + far = glm::max(far, -clippedTriangle.v2.z); + } +} + +static void computeNearFar(const glm::vec3 sceneBoundVertices[8], const Plane shadowClipPlanes[4], float& near, float& far) { + // This code is inspired from Microsoft's CascadedShadowMaps11 sample which is under MIT licence. + // See https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Shadow-Win32-2d72a4f2/sourcecode?fileId=121915&pathId=1645833187 + // Basically it decomposes the object bounding box in triangles and clips each triangle with the shadow + // frustum planes. Finally it computes the minimum and maximum depth of the clipped triangle vertices + // in shadow space to extract the near and far distances of the shadow frustum. + static const std::array boxQuadVertexIndices = { { + { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR }, + { TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR }, + { TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR }, + { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR }, + { BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR }, + { TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR } + } }; + Triangle triangle; + + for (auto quadVertexIndices : boxQuadVertexIndices) { + triangle.v0 = sceneBoundVertices[quadVertexIndices[0]]; + triangle.v1 = sceneBoundVertices[quadVertexIndices[1]]; + triangle.v2 = sceneBoundVertices[quadVertexIndices[2]]; + computeNearFar(triangle, shadowClipPlanes, near, far); + triangle.v1 = sceneBoundVertices[quadVertexIndices[3]]; + computeNearFar(triangle, shadowClipPlanes, near, far); + } +} + +static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum) { + const Transform shadowView{ shadowFrustum.getView() }; + const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; + + glm::vec3 sceneBoundVertices[8]; + // Keep only the left, right, top and bottom shadow frustum planes as we wish to determine + // the near and far + Plane shadowClipPlanes[4]; + int i; + + // The vertices of the scene bounding box are expressed in the shadow frustum's local space + for (i = 0; i < 8; i++) { + sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast(i))); + } + // This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum + // changes order especially as we don't need to test the NEAR and FAR planes. + static const ViewFrustum::PlaneIndex planeIndices[4] = { + ViewFrustum::TOP_PLANE, + ViewFrustum::BOTTOM_PLANE, + ViewFrustum::LEFT_PLANE, + ViewFrustum::RIGHT_PLANE + }; + // Same goes for the shadow frustum planes. + for (i = 0; i < 4; i++) { + const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]]; + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse, which should be the + // case for a view matrix. + auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal()); + auto planePoint = shadowViewInverse.transform(worldPlane.getPoint()); + shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint); + } + + float near = std::numeric_limits::max(); + float far = 0.0f; + + computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far); + // Limit the far range to the one used originally. There's no point in rendering objects + // that are not in the view frustum. + far = glm::min(far, shadowFrustum.getFarClip()); + + const auto depthEpsilon = 0.1f; + auto projMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, near - depthEpsilon, far + depthEpsilon); + auto shadowProjection = shadowFrustum.getProjection(); + + shadowProjection[2][2] = projMatrix[2][2]; + shadowProjection[3][2] = projMatrix[3][2]; + shadowFrustum.setProjection(shadowProjection); + shadowFrustum.calculate(); +} + +void RenderShadowMap::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); + const auto& inShapes = inputs.get0(); + const auto& inShapeBounds = inputs.get1(); + auto lightStage = renderContext->_scene->getStage(); assert(lightStage); - const auto shadow = lightStage->getCurrentKeyShadow(); + auto shadow = lightStage->getCurrentKeyShadow(); if (!shadow) return; const auto& fbo = shadow->framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; + auto adjustedShadowFrustum = args->getViewFrustum(); + + // Adjust the frustum near and far depths based on the rendered items bounding box to have + // the minimal Z range. + adjustNearFar(inShapeBounds, adjustedShadowFrustum); + // Reapply the frustum as it has been adjusted + shadow->setFrustum(adjustedShadowFrustum); + args->popViewFrustum(); + args->pushViewFrustum(adjustedShadowFrustum); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -55,8 +166,13 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true); - batch.setProjectionTransform(shadow->getProjection()); - batch.setViewTransform(shadow->getView(), false); + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, false); auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); @@ -87,7 +203,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, } void RenderShadowTask::build(JobModel& task, const render::Varying& input, render::Varying& output, CullFunctor cullFunctor) { - cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&){ return true; }; + cullFunctor = cullFunctor ? cullFunctor : [](const RenderArgs*, const AABox&) { return true; }; // Prepare the ShapePipeline ShapePlumberPointer shapePlumber = std::make_shared(); @@ -109,10 +225,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // Sort const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapes = task.addJob("DepthSortShadowMap", sortedPipelines); + const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapes, shapePlumber); + task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber); task.addJob("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())); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 031f44a42d..7b2bbeb306 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -21,11 +21,11 @@ class ViewFrustum; class RenderShadowMap { public: - using JobModel = render::Job::ModelI; + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; 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; diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index fceaf7b5b9..1085a1148c 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -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(); - task.addJob("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", nullptr); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 7b86b9b660..e844db43dd 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -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; } diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf index 73e14bd319..6a7a6157a4 100644 --- a/libraries/render-utils/src/glowLine.slf +++ b/libraries/render-utils/src/glowLine.slf @@ -10,8 +10,8 @@ // in vec4 _color; -in float distanceFromCenter; +in float distanceFromCenter; out vec4 _fragColor; void main(void) { diff --git a/libraries/render-utils/src/glowLine.slv b/libraries/render-utils/src/glowLine.slv index fd3a85d254..4532ed7b9f 100644 --- a/libraries/render-utils/src/glowLine.slv +++ b/libraries/render-utils/src/glowLine.slv @@ -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; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 710507bd79..0f4137e38d 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -20,7 +20,7 @@ #include #include #include - +#include #include #include @@ -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(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(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 >(); + 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(); +} diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 896ccef842..5d98c37c21 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -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(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 diff --git a/libraries/render/src/render/SortTask.cpp b/libraries/render/src/render/SortTask.cpp index 00146e393d..63673a71a5 100644 --- a/libraries/render/src/render/SortTask.cpp +++ b/libraries/render/src/render/SortTask.cpp @@ -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); } diff --git a/libraries/render/src/render/SortTask.h b/libraries/render/src/render/SortTask.h index dfeb22d540..de670b1676 100644 --- a/libraries/render/src/render/SortTask.h +++ b/libraries/render/src/render/SortTask.h @@ -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; + using JobModel = Job::ModelIO; + + 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; diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index eef83974ea..24485eaad6 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -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); diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 6b9718fbb8..e502d44a08 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -14,10 +14,12 @@ #include #include #include +#include #include #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 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) { diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index eb9424d938..dcb90643b6 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -15,6 +15,8 @@ #include #include +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); diff --git a/libraries/shared/src/Plane.h b/libraries/shared/src/Plane.h index c903ad9db7..cf17ca7201 100644 --- a/libraries/shared/src/Plane.h +++ b/libraries/shared/src/Plane.h @@ -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() {} ; diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 316fcb2f04..7a39314f4d 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -149,6 +149,7 @@ public: Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; + Vec3 transformDirection(const Vec3& dir) const; bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); } @@ -541,6 +542,13 @@ inline Transform::Vec3 Transform::transform(const Vec3& pos) const { return Vec3(result.x / result.w, result.y / result.w, result.z / result.w); } +inline Transform::Vec3 Transform::transformDirection(const Vec3& dir) const { + Mat4 m; + getMatrix(m); + Vec4 result = m * Vec4(dir, 0.0f); + return Vec3(result.x, result.y, result.z); +} + inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const { updateCache(); result = (*_matrix); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 76fa5c9ec8..ebd30f9342 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -1080,6 +1080,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"; @@ -1103,6 +1104,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"; @@ -1124,7 +1128,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 @@ -1159,6 +1175,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; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 62754efe14..dc16c873fb 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -90,6 +90,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; // Checks to see if a new texture is available. If one is, the function returns true and diff --git a/scripts/developer/tests/performance/rayPickPerformance.js b/scripts/developer/tests/performance/rayPickPerformance.js index b4faf4c1be..92d12c0e71 100644 --- a/scripts/developer/tests/performance/rayPickPerformance.js +++ b/scripts/developer/tests/performance/rayPickPerformance.js @@ -96,7 +96,6 @@ function rayCastTest() { color: color, alpha: 1, visible: visible, - lineWidth: 2, start: origin, end: Vec3.sum(origin,Vec3.multiply(5,direction)) }); diff --git a/scripts/developer/utilities/render/debugShadow.js b/scripts/developer/utilities/render/debugShadow.js new file mode 100644 index 0000000000..a0d2142258 --- /dev/null +++ b/scripts/developer/utilities/render/debugShadow.js @@ -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(); }); \ No newline at end of file diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml new file mode 100644 index 0000000000..8548ba4119 --- /dev/null +++ b/scripts/developer/utilities/render/shadow.qml @@ -0,0 +1,50 @@ +// +// shadow.qml +// 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 https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Column { + id: root + spacing: 8 + property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum"); + property var shadowConfig: Render.getConfig("RenderMainView.DrawShadowFrustum"); + + Component.onCompleted: { + viewConfig.enabled = true; + shadowConfig.enabled = true; + } + Component.onDestruction: { + viewConfig.enabled = false; + shadowConfig.enabled = false; + } + + CheckBox { + text: "Freeze Frustums" + checked: false + onCheckedChanged: { + viewConfig.isFrozen = checked; + shadowConfig.isFrozen = checked; + } + } + Row { + spacing: 8 + Label { + text: "View" + color: "yellow" + font.italic: true + } + Label { + text: "Shadow" + color: "blue" + font.italic: true + } + } +} diff --git a/scripts/system/chat.js b/scripts/system/chat.js index fa997e20cc..0cb414e23c 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -284,8 +284,7 @@ endParentJointIndex: yourJointIndex, end: yourJointPosition, color: identifyAvatarLineColor, - alpha: 1, - lineWidth: 1 + alpha: 1 }; avatarIdentifiers[yourAvatarID] = identifierParams; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 7d9a6dc1b5..8139302dfe 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -33,7 +33,6 @@ Script.include("/~/system/libraries/Xform.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -55,7 +54,6 @@ Script.include("/~/system/libraries/Xform.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -77,7 +75,6 @@ Script.include("/~/system/libraries/Xform.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -649,6 +646,7 @@ Script.include("/~/system/libraries/Xform.js"); renderStates: renderStates, faceAvatar: true, distanceScaleEnd: true, + scaleWithAvatar: true, defaultRenderStates: defaultRenderStates }); } diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index de60e2e227..9c72c3df4d 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -25,7 +25,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -47,7 +46,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -69,7 +67,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -190,6 +187,7 @@ Script.include("/~/system/libraries/controllers.js"); renderStates: renderStates, faceAvatar: true, distanceScaleEnd: true, + scaleWithAvatar: true, defaultRenderStates: defaultRenderStates }); diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 23802893a8..acddcaf246 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -31,7 +31,6 @@ alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawHUDLayer: true, parentID: MyAvatar.SELF_ID @@ -53,7 +52,6 @@ alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawHUDLayer: true, parentID: MyAvatar.SELF_ID @@ -75,7 +73,6 @@ alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawHUDLayer: true, parentID: MyAvatar.SELF_ID diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index f9ec38d22a..94233376c7 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -27,7 +27,6 @@ Script.include("/~/system/libraries/utils.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -49,7 +48,6 @@ Script.include("/~/system/libraries/utils.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -71,7 +69,6 @@ Script.include("/~/system/libraries/utils.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -237,6 +234,7 @@ Script.include("/~/system/libraries/utils.js"); posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, + scaleWithAvatar: true, defaultRenderStates: defaultRenderStates }); diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 1c83f38d9b..2d27f160c1 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -26,7 +26,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -48,7 +47,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -70,7 +68,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -371,6 +368,7 @@ Script.include("/~/system/libraries/controllers.js"); posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, + scaleWithAvatar: true, defaultRenderStates: defaultRenderStates }); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 0364e4f9b4..c4fbe781b9 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -153,6 +153,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", filter: RayPick.PICK_ENTITIES, faceAvatar: true, + scaleWithAvatar: true, centerEndY: false, renderStates: teleportRenderStates, defaultRenderStates: teleportDefaultRenderStates @@ -161,6 +162,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, faceAvatar: true, + scaleWithAvatar: true, centerEndY: false, renderStates: teleportRenderStates }); @@ -168,6 +170,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: "Avatar", filter: RayPick.PICK_ENTITIES, faceAvatar: true, + scaleWithAvatar: true, centerEndY: false, renderStates: teleportRenderStates, defaultRenderStates: teleportDefaultRenderStates @@ -176,6 +179,7 @@ Script.include("/~/system/libraries/controllers.js"); joint: "Avatar", filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, faceAvatar: true, + scaleWithAvatar: true, centerEndY: false, renderStates: teleportRenderStates }); diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index 39969df614..36a36d38f3 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -26,7 +26,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -48,7 +47,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -70,7 +68,6 @@ Script.include("/~/system/libraries/controllers.js"); alpha: 1, solid: true, glow: 1.0, - lineWidth: 5, ignoreRayIntersection: true, // always ignore this drawInFront: true, // Even when burried inside of something, show it. parentID: MyAvatar.SELF_ID @@ -88,7 +85,6 @@ Script.include("/~/system/libraries/controllers.js"); {name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath} ]; - // triggered when stylus presses a web overlay/entity var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; @@ -452,6 +448,7 @@ Script.include("/~/system/libraries/controllers.js"); posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, + scaleWithAvatar: true, defaultRenderStates: defaultRenderStates }); } diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 3a422bcb8a..d947a1d397 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -339,7 +339,6 @@ SelectionDisplay = (function() { solid: grabberSolid, visible: false, dashed: false, - lineWidth: grabberLineWidth, drawInFront: true, borderSize: 1.4 }; @@ -352,7 +351,6 @@ SelectionDisplay = (function() { solid: grabberSolid, visible: false, dashed: false, - lineWidth: grabberLineWidth, drawInFront: true, borderSize: 1.4 }; @@ -365,7 +363,6 @@ SelectionDisplay = (function() { solid: grabberSolid, visible: false, dashed: false, - lineWidth: grabberLineWidth, drawInFront: true, borderSize: 1.4 }; @@ -378,14 +375,12 @@ SelectionDisplay = (function() { solid: grabberSolid, visible: false, dashed: false, - lineWidth: grabberLineWidth, drawInFront: true, borderSize: 1.4 }; var spotLightLineProperties = { - color: lightOverlayColor, - lineWidth: 1.5 + color: lightOverlayColor }; var highlightBox = Overlays.addOverlay("cube", { @@ -400,7 +395,6 @@ SelectionDisplay = (function() { solid: false, visible: false, dashed: true, - lineWidth: 2.0, ignoreRayIntersection: true, // this never ray intersects drawInFront: true }); @@ -416,8 +410,7 @@ SelectionDisplay = (function() { alpha: 1, solid: false, visible: false, - dashed: false, - lineWidth: 1.0 + dashed: false }); var selectionBoxes = []; @@ -466,7 +459,6 @@ SelectionDisplay = (function() { // var normalLine = Overlays.addOverlay("line3d", { // visible: true, - // lineWidth: 2.0, // start: { x: 0, y: 0, z: 0 }, // end: { x: 0, y: 0, z: 0 }, // color: { red: 255, green: 255, blue: 0 }, @@ -656,7 +648,6 @@ SelectionDisplay = (function() { var xRailOverlay = Overlays.addOverlay("line3d", { visible: false, - lineWidth: 1.0, start: Vec3.ZERO, end: Vec3.ZERO, color: { @@ -668,7 +659,6 @@ SelectionDisplay = (function() { }); var yRailOverlay = Overlays.addOverlay("line3d", { visible: false, - lineWidth: 1.0, start: Vec3.ZERO, end: Vec3.ZERO, color: { @@ -680,7 +670,6 @@ SelectionDisplay = (function() { }); var zRailOverlay = Overlays.addOverlay("line3d", { visible: false, - lineWidth: 1.0, start: Vec3.ZERO, end: Vec3.ZERO, color: { @@ -693,7 +682,6 @@ SelectionDisplay = (function() { var rotateZeroOverlay = Overlays.addOverlay("line3d", { visible: false, - lineWidth: 2.0, start: Vec3.ZERO, end: Vec3.ZERO, color: { @@ -706,7 +694,6 @@ SelectionDisplay = (function() { var rotateCurrentOverlay = Overlays.addOverlay("line3d", { visible: false, - lineWidth: 2.0, start: Vec3.ZERO, end: Vec3.ZERO, color: { @@ -1788,7 +1775,6 @@ SelectionDisplay = (function() { y: distance, z: 1 }, - lineWidth: 1.5, rotation: rotation, visible: true }); @@ -1994,7 +1980,6 @@ SelectionDisplay = (function() { solid: false, visible: false, dashed: false, - lineWidth: 1.0, ignoreRayIntersection: true })); } diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index fd30bde3a0..750194bed2 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -168,6 +168,106 @@ })); } + var HALF_TREE_SCALE = 16384; + function getPositionToCreateEntity(extra) { + var CREATE_DISTANCE = 2; + var position; + var delta = extra !== undefined ? extra : 0; + if (Camera.mode === "entity" || Camera.mode === "independent") { + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta)); + } else { + position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta)); + position.y += 0.5; + } + + if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { + return null; + } + return position; + } + + function rezEntity(itemHref, isWearable) { + var success = Clipboard.importEntities(itemHref); + + if (success) { + var VERY_LARGE = 10000; + var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE; + var position = Vec3.ZERO; + if (!isLargeImport) { + position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); + } + if (position !== null && position !== undefined) { + var pastedEntityIDs = Clipboard.pasteEntities(position); + if (!isLargeImport) { + // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move + // entities after they're imported so that they're all the correct distance in front of and with geometric mean + // centered on the avatar/camera direction. + var deltaPosition = Vec3.ZERO; + var entityPositions = []; + var entityParentIDs = []; + + var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; + var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; + if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { + var targetDirection; + if (Camera.mode === "entity" || Camera.mode === "independent") { + targetDirection = Camera.orientation; + } else { + targetDirection = MyAvatar.orientation; + } + targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); + + var targetPosition = getPositionToCreateEntity(); + var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. + var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. + for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { + var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", + "registrationPoint", "rotation", "parentID"]); + var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, + curLoopEntityProps.registrationPoint, curLoopEntityProps.dimensions, curLoopEntityProps.rotation); + var delta = Vec3.subtract(adjustedPosition, curLoopEntityProps.position); + var distance = Vec3.dot(delta, targetDirection); + deltaParallel = Math.min(distance, deltaParallel); + deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), + deltaPerpendicular); + entityPositions[i] = curLoopEntityProps.position; + entityParentIDs[i] = curLoopEntityProps.parentID; + } + deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); + deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); + } + + if (grid.getSnapToGrid()) { + var firstEntityProps = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", + "registrationPoint"]); + var positionPreSnap = Vec3.sum(deltaPosition, firstEntityProps.position); + position = grid.snapToSurface(grid.snapToGrid(positionPreSnap, false, firstEntityProps.dimensions, + firstEntityProps.registrationPoint), firstEntityProps.dimensions, firstEntityProps.registrationPoint); + deltaPosition = Vec3.subtract(position, firstEntityProps.position); + } + + if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { + for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { + if (Uuid.isNull(entityParentIDs[editEntityIndex])) { + Entities.editEntity(pastedEntityIDs[editEntityIndex], { + position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) + }); + } + } + } + } + + if (isActive) { + selectionManager.setSelections(pastedEntityIDs); + } + } else { + Window.notifyEditError("Can't import entities: entities would be out of bounds."); + } + } else { + Window.notifyEditError("There was an error importing the entity file."); + } + } + marketplaceButton.clicked.connect(onClick); tablet.screenChanged.connect(onScreenChanged); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); @@ -330,6 +430,10 @@ tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); } break; + case 'checkout_rezClicked': + case 'purchases_rezClicked': + rezEntity(message.itemHref, message.isWearable); + break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7ba7c8c0f1..bd318193cf 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -189,7 +189,6 @@ function HighlightedEntity(id, entityProperties) { green: 0x91, blue: 0x29 }, - lineWidth: 1.0, ignoreRayIntersection: true, drawInFront: false // Arguable. For now, let's not distract with mysterious wires around the scene. }); diff --git a/scripts/tutorials/entity_scripts/pistol.js b/scripts/tutorials/entity_scripts/pistol.js index 1a570cc80f..62517f486d 100644 --- a/scripts/tutorials/entity_scripts/pistol.js +++ b/scripts/tutorials/entity_scripts/pistol.js @@ -350,8 +350,7 @@ end: ZERO_VECTOR, color: { red: 255, green: 0, blue: 0}, alpha: 1, - visible: true, - lineWidth: 2 + visible: true }); }, }; diff --git a/unpublishedScripts/DomainContent/Toybox/bow/bow.js b/unpublishedScripts/DomainContent/Toybox/bow/bow.js index 0c16bcbc7b..47335bcb6d 100644 --- a/unpublishedScripts/DomainContent/Toybox/bow/bow.js +++ b/unpublishedScripts/DomainContent/Toybox/bow/bow.js @@ -329,7 +329,6 @@ y: 0, z: 0 }, lineVectors[0]], - lineWidth: 5, color: this.stringData.currentColor }); @@ -339,7 +338,6 @@ y: 0, z: 0 }, lineVectors[1]], - lineWidth: 5, color: this.stringData.currentColor }); diff --git a/unpublishedScripts/DomainContent/Toybox/bow/createBow.js b/unpublishedScripts/DomainContent/Toybox/bow/createBow.js index f1ed9eb263..5a4275d96a 100644 --- a/unpublishedScripts/DomainContent/Toybox/bow/createBow.js +++ b/unpublishedScripts/DomainContent/Toybox/bow/createBow.js @@ -125,7 +125,6 @@ function createPreNotchString() { y: 0, z: 0 }, downOffset)], - lineWidth: 5, color: { red: 255, green: 255, diff --git a/unpublishedScripts/DomainContent/Toybox/hiddenEntityReset.js b/unpublishedScripts/DomainContent/Toybox/hiddenEntityReset.js index e2deec75ed..fe514f6dfc 100644 --- a/unpublishedScripts/DomainContent/Toybox/hiddenEntityReset.js +++ b/unpublishedScripts/DomainContent/Toybox/hiddenEntityReset.js @@ -450,7 +450,6 @@ y: 0, z: 0 }, downOffset)], - lineWidth: 5, color: { red: 255, green: 255, diff --git a/unpublishedScripts/DomainContent/Toybox/masterReset.js b/unpublishedScripts/DomainContent/Toybox/masterReset.js index 4ad9cce401..b621544621 100644 --- a/unpublishedScripts/DomainContent/Toybox/masterReset.js +++ b/unpublishedScripts/DomainContent/Toybox/masterReset.js @@ -427,7 +427,6 @@ MasterReset = function() { y: 0, z: 0 }, downOffset)], - lineWidth: 5, color: { red: 255, green: 255, diff --git a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js index 5f57c6fc17..b408e4f464 100644 --- a/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js +++ b/unpublishedScripts/DomainContent/Toybox/pistol/pistol.js @@ -346,8 +346,7 @@ end: { x: 0, y: 0, z: 0 }, color: COLORS.RED, alpha: 1, - visible: true, - lineWidth: 2 + visible: true }); }, }; diff --git a/unpublishedScripts/marketplace/bow/bow.js b/unpublishedScripts/marketplace/bow/bow.js index 818960e335..883eff113c 100644 --- a/unpublishedScripts/marketplace/bow/bow.js +++ b/unpublishedScripts/marketplace/bow/bow.js @@ -266,7 +266,6 @@ dimensions: { "x": 5, "y": 5, "z": 5 }, ignoreForCollisions: 1, linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ], - lineWidth: 5, name: STRING_NAME, parentID: this.entityID, localPosition: { "x": 0, "y": 0.6, "z": 0.1 }, @@ -287,7 +286,6 @@ resetStringToIdlePosition: function() { Entities.editEntity(this.stringID, { linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ], - lineWidth: 5, localPosition: { "x": 0, "y": 0.6, "z": 0.1 }, localRotation: { "w": 1, "x": 0, "y": 0, "z": 0 }, }); diff --git a/unpublishedScripts/marketplace/laser/laserPointerApp.js b/unpublishedScripts/marketplace/laser/laserPointerApp.js index 515a2c3a76..aa049ea470 100644 --- a/unpublishedScripts/marketplace/laser/laserPointerApp.js +++ b/unpublishedScripts/marketplace/laser/laserPointerApp.js @@ -99,7 +99,6 @@ lifetime: 360, type: 'Line', glow: 1.0, - lineWidth: 5, alpha: 0.5, ignoreRayIntersection: true, drawInFront: true, diff --git a/unpublishedScripts/marketplace/shapes/modules/laser.js b/unpublishedScripts/marketplace/shapes/modules/laser.js index 1efc38b65a..d5feda0e1f 100644 --- a/unpublishedScripts/marketplace/shapes/modules/laser.js +++ b/unpublishedScripts/marketplace/shapes/modules/laser.js @@ -72,7 +72,6 @@ Laser = function (side) { } laserLine = Overlays.addOverlay("line3d", { - lineWidth: 5, alpha: 1.0, glow: 1.0, ignoreRayIntersection: true, diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow.js b/unpublishedScripts/marketplace/shortbow/bow/bow.js index a8e76f76fd..5134fb6fd8 100644 --- a/unpublishedScripts/marketplace/shortbow/bow/bow.js +++ b/unpublishedScripts/marketplace/shortbow/bow/bow.js @@ -408,7 +408,6 @@ function getControllerLocation(controllerHand) { dimensions: { "x": 5, "y": 5, "z": 5 }, ignoreForCollisions: 1, linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ], - lineWidth: 5, color: { red: 153, green: 102, blue: 51 }, name: STRING_NAME, parentID: this.entityID, @@ -430,7 +429,6 @@ function getControllerLocation(controllerHand) { resetStringToIdlePosition: function() { Entities.editEntity(this.stringID, { linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ], - lineWidth: 5, localPosition: { "x": 0, "y": 0.6, "z": 0.1 }, localRotation: { "w": 1, "x": 0, "y": 0, "z": 0 }, });