diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index f51455e9d4..dfa59943d6 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -44,6 +44,8 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) set(SHADER_TARGET ${SHADER_TARGET}_vert.h) elseif(${SHADER_EXT} STREQUAL .slf) set(SHADER_TARGET ${SHADER_TARGET}_frag.h) + elseif(${SHADER_EXT} STREQUAL .slg) + set(SHADER_TARGET ${SHADER_TARGET}_geom.h) endif() set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}") @@ -87,7 +89,7 @@ macro(AUTOSCRIBE_SHADER_LIB) #message(${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh) - file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf) + file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg) #make the shader folder set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}") diff --git a/interface/resources/controllers/touchscreen.json b/interface/resources/controllers/touchscreen.json new file mode 100644 index 0000000000..5b2ff62a8d --- /dev/null +++ b/interface/resources/controllers/touchscreen.json @@ -0,0 +1,23 @@ +{ + "name": "Touchscreen to Actions", + "channels": [ + { "from": "Touchscreen.GesturePinchOut", "to": "Actions.BoomOut", "filters": [ { "type": "scale", "scale": 0.02 } ] }, + { "from": "Touchscreen.GesturePinchIn", "to": "Actions.BoomIn", "filters": [ { "type": "scale", "scale": 0.02 } ] }, + + { "from": { "makeAxis" : [ + [ "Touchscreen.DragLeft" ], + [ "Touchscreen.DragRight" ] + ] + }, + "to": "Actions.Yaw", "filters": [ { "type": "scale", "scale": 0.12 } ] + }, + + { "from": { "makeAxis" : [ + [ "Touchscreen.DragUp" ], + [ "Touchscreen.DragDown" ] + ] + }, + "to": "Actions.Pitch", "filters": [ { "type": "scale", "scale": 0.04 } ] + } + ] +} diff --git a/interface/resources/images/Loading-Inner-H.png b/interface/resources/images/Loading-Inner-H.png new file mode 100644 index 0000000000..cf09ea8317 Binary files /dev/null and b/interface/resources/images/Loading-Inner-H.png differ diff --git a/interface/resources/images/Loading-Outer-Ring.png b/interface/resources/images/Loading-Outer-Ring.png new file mode 100644 index 0000000000..52613949e5 Binary files /dev/null and b/interface/resources/images/Loading-Outer-Ring.png differ diff --git a/interface/resources/images/preview.png b/interface/resources/images/preview.png new file mode 100644 index 0000000000..faebbfce8f Binary files /dev/null and b/interface/resources/images/preview.png differ diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 8c8cf05444..e9b99331c7 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -3,12 +3,14 @@ import QtQuick.Controls 1.2 import QtWebEngine 1.1 import "controls-uit" +import "styles" as HifiStyles import "styles-uit" import "windows" ScrollingWindow { id: root HifiConstants { id: hifi } + HifiStyles.HifiConstants { id: hifistyles } title: "Browser" resizable: true destroyOnHidden: true @@ -46,7 +48,7 @@ ScrollingWindow { id: back; enabled: webview.canGoBack; text: hifi.glyphs.backward - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goBack() } } @@ -55,7 +57,7 @@ ScrollingWindow { id: forward; enabled: webview.canGoForward; text: hifi.glyphs.forward - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goForward() } } @@ -64,7 +66,7 @@ ScrollingWindow { id: reload; enabled: webview.canGoForward; text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload - color: enabled ? hifi.colors.text : hifi.colors.disabledText + color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goForward() } } @@ -105,7 +107,7 @@ ScrollingWindow { focus: true colorScheme: hifi.colorSchemes.dark placeholderText: "Enter URL" - Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i") + Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") Keys.onPressed: { switch(event.key) { case Qt.Key_Enter: diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 91dc210eda..ca3a2da577 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -3,13 +3,16 @@ import QtQuick 2.3 import QtQuick.Controls 1.3 import QtQuick.Controls.Styles 1.3 import QtGraphicalEffects 1.0 + import "controls-uit" +import "styles" as HifiStyles import "styles-uit" import "windows" ScrollingWindow { id: root HifiConstants { id: hifi } + HifiStyles.HifiConstants { id: hifistyles } objectName: "UpdateDialog" width: updateDialog.implicitWidth height: updateDialog.implicitHeight @@ -40,22 +43,6 @@ ScrollingWindow { width: updateDialog.contentWidth + updateDialog.borderWidth * 2 height: mainContent.height + updateDialog.borderWidth * 2 - updateDialog.closeMargin / 2 - - MouseArea { - width: parent.width - height: parent.height - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - drag { - target: root - minimumX: 0 - minimumY: 0 - maximumX: root.parent ? root.maximumX : 0 - maximumY: root.parent ? root.maximumY : 0 - } - } } Image { @@ -89,7 +76,7 @@ ScrollingWindow { text: "Update Available" font { family: updateDialog.fontFamily - pixelSize: hifi.fonts.pixelSize * 1.5 + pixelSize: hifistyles.fonts.pixelSize * 1.5 weight: Font.DemiBold } color: "#303030" @@ -100,10 +87,10 @@ ScrollingWindow { text: updateDialog.updateAvailableDetails font { family: updateDialog.fontFamily - pixelSize: hifi.fonts.pixelSize * 0.6 + pixelSize: hifistyles.fonts.pixelSize * 0.6 letterSpacing: -0.5 } - color: hifi.colors.text + color: hifistyles.colors.text anchors { top: updateAvailable.bottom } @@ -130,12 +117,12 @@ ScrollingWindow { Text { id: releaseNotes wrapMode: Text.Wrap - width: parent.width - updateDialog.closeMargin + width: parent.parent.width - updateDialog.closeMargin text: updateDialog.releaseNotes - color: hifi.colors.text + color: hifistyles.colors.text font { family: updateDialog.fontFamily - pixelSize: hifi.fonts.pixelSize * 0.65 + pixelSize: hifistyles.fonts.pixelSize * 0.65 } } } @@ -157,7 +144,7 @@ ScrollingWindow { color: "#0c9ab4" // Same as logo font { family: updateDialog.fontFamily - pixelSize: hifi.fonts.pixelSize * 1.2 + pixelSize: hifistyles.fonts.pixelSize * 1.2 weight: Font.DemiBold } anchors { @@ -169,7 +156,7 @@ ScrollingWindow { MouseArea { id: cancelButtonAction anchors.fill: parent - onClicked: updateDialog.closeDialog() + onClicked: root.shown = false cursorShape: "PointingHandCursor" } } @@ -185,7 +172,7 @@ ScrollingWindow { color: "#0c9ab4" // Same as logo font { family: updateDialog.fontFamily - pixelSize: hifi.fonts.pixelSize * 1.2 + pixelSize: hifistyles.fonts.pixelSize * 1.2 weight: Font.DemiBold } anchors { diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml old mode 100755 new mode 100644 diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml old mode 100755 new mode 100644 diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml old mode 100755 new mode 100644 diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml old mode 100755 new mode 100644 diff --git a/interface/resources/qml/windows/Window.qml b/interface/resources/qml/windows/Window.qml index e3e70c1e74..82bcf011e9 100644 --- a/interface/resources/qml/windows/Window.qml +++ b/interface/resources/qml/windows/Window.qml @@ -209,6 +209,9 @@ Fadable { var targetVisibility = getTargetVisibility(); if (targetVisibility === visible) { + if (force) { + window.raise(); + } return; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 82cbaf93c2..902312a022 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -170,7 +170,8 @@ static QTimer pingTimer; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; -static const QString SVO_JSON_EXTENSION = ".svo.json"; +static const QString SVO_JSON_EXTENSION = ".svo.json"; +static const QString JSON_EXTENSION = ".json"; static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; @@ -202,13 +203,16 @@ static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStanda Setting::Handle maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); +static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; + const QHash Application::_acceptedExtensions { { SNAPSHOT_EXTENSION, &Application::acceptSnapshot }, { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, + { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, + { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl }, - { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } + { FST_EXTENSION, &Application::askToSetAvatarUrl } }; class DeadlockWatchdogThread : public QThread { @@ -953,8 +957,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : return DependencyManager::get()->navigationFocused() ? 1 : 0; }); - // Setup the keyboardMouseDevice and the user input mapper with the default bindings + // Setup the _keyboardMouseDevice, _touchscreenDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); + // if the _touchscreenDevice is not supported it will not be registered + if (_touchscreenDevice) { + userInputMapper->registerDevice(_touchscreenDevice->getInputDevice()); + } // force the model the look at the correct directory (weird order of operations issue) scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); @@ -1596,6 +1604,9 @@ void Application::initializeUi() { if (KeyboardMouseDevice::NAME == inputPlugin->getName()) { _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); } + if (TouchscreenDevice::NAME == inputPlugin->getName()) { + _touchscreenDevice = std::dynamic_pointer_cast(inputPlugin); + } } _window->setMenuBar(new Menu()); @@ -1969,7 +1980,22 @@ void Application::resizeGL() { } } +bool Application::importJSONFromURL(const QString& urlString) { + // we only load files that terminate in just .json (not .svo.json and not .ava.json) + // if they come from the High Fidelity Marketplace Assets CDN + + QUrl jsonURL { urlString }; + + if (jsonURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) { + emit svoImportRequested(urlString); + return true; + } else { + return false; + } +} + bool Application::importSVOFromURL(const QString& urlString) { + emit svoImportRequested(urlString); return true; } @@ -2077,6 +2103,9 @@ bool Application::event(QEvent* event) { case QEvent::TouchUpdate: touchUpdateEvent(static_cast(event)); return true; + case QEvent::Gesture: + touchGestureEvent((QGestureEvent*)event); + return true; case QEvent::Wheel: wheelEvent(static_cast(event)); return true; @@ -2708,6 +2737,9 @@ void Application::touchUpdateEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchUpdateEvent(event); } + if (_touchscreenDevice->isActive()) { + _touchscreenDevice->touchUpdateEvent(event); + } } void Application::touchBeginEvent(QTouchEvent* event) { @@ -2726,6 +2758,9 @@ void Application::touchBeginEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchBeginEvent(event); } + if (_touchscreenDevice->isActive()) { + _touchscreenDevice->touchBeginEvent(event); + } } @@ -2743,10 +2778,19 @@ void Application::touchEndEvent(QTouchEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->touchEndEvent(event); } + if (_touchscreenDevice->isActive()) { + _touchscreenDevice->touchEndEvent(event); + } // put any application specific touch behavior below here.. } +void Application::touchGestureEvent(QGestureEvent* event) { + if (_touchscreenDevice->isActive()) { + _touchscreenDevice->touchGestureEvent(event); + } +} + void Application::wheelEvent(QWheelEvent* event) const { _altPressed = false; _controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts @@ -4806,7 +4850,17 @@ bool Application::askToSetAvatarUrl(const QString& url) { bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { QMessageBox::StandardButton reply; - QString message = "Would you like to run this script:\n" + scriptFilenameOrURL; + + QString shortName = scriptFilenameOrURL; + + QUrl scriptURL { scriptFilenameOrURL }; + + if (scriptURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) { + shortName = shortName.mid(shortName.lastIndexOf('/') + 1); + } + + QString message = "Would you like to run this script:\n" + shortName; + reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 6857ba2a3a..a1e3e2f930 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,7 @@ private: void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false); + bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); bool nearbyEntitiesAreReadyForPhysics(); @@ -402,6 +404,7 @@ private: void touchBeginEvent(QTouchEvent* event); void touchEndEvent(QTouchEvent* event); void touchUpdateEvent(QTouchEvent* event); + void touchGestureEvent(QGestureEvent* event); void wheelEvent(QWheelEvent* event) const; void dropEvent(QDropEvent* event); @@ -454,6 +457,7 @@ private: std::shared_ptr _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session std::shared_ptr _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad + std::shared_ptr _touchscreenDevice; // the good old touchscreen SimpleMovingAverage _avatarSimsPerSecond {10}; int _avatarSimsPerSecondReport {0}; quint64 _lastAvatarSimsPerSecondUpdate {0}; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 437e173807..60acd0895b 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -48,14 +48,6 @@ const QString& UpdateDialog::releaseNotes() const { return _releaseNotes; } -void UpdateDialog::closeDialog() { - hide(); -} - -void UpdateDialog::hide() { - ((QQuickItem*)parent())->setVisible(false); -} - void UpdateDialog::triggerUpgrade() { auto applicationUpdater = DependencyManager::get(); applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 84d390c942..4adff90283 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -21,22 +21,20 @@ class UpdateDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL - Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails) - Q_PROPERTY(QString releaseNotes READ releaseNotes) + Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails CONSTANT) + Q_PROPERTY(QString releaseNotes READ releaseNotes CONSTANT) public: UpdateDialog(QQuickItem* parent = nullptr); const QString& updateAvailableDetails() const; const QString& releaseNotes() const; - + private: QString _updateAvailableDetails; QString _releaseNotes; protected: - void hide(); Q_INVOKABLE void triggerUpgrade(); - Q_INVOKABLE void closeDialog(); }; diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index d53b287f76..c9a8b19f6a 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -55,12 +55,14 @@ void Line3DOverlay::render(RenderArgs* args) { batch->setModelTransform(_transform); auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleProgram(*batch, false, false, true, true); if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() + geometryCache->bindSimpleProgram(*batch, false, false, true, true); geometryCache->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID); + } else if (_glow > 0.0f) { + geometryCache->renderGlowLine(*batch, _start, _end, colorv4, _glow, _glowWidth, _geometryCacheID); } else { - + geometryCache->bindSimpleProgram(*batch, false, false, true, true); geometryCache->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); } } @@ -68,7 +70,7 @@ void Line3DOverlay::render(RenderArgs* args) { const render::ShapeKey Line3DOverlay::getShapeKey() { auto builder = render::ShapeKey::Builder().withOwnPipeline(); - if (getAlpha() != 1.0f) { + if (getAlpha() != 1.0f || _glow > 0.0f) { builder.withTranslucent(); } return builder.build(); @@ -94,6 +96,19 @@ void Line3DOverlay::setProperties(const QVariantMap& properties) { if (end.isValid()) { setEnd(vec3FromVariant(end)); } + + auto glow = properties["glow"]; + if (glow.isValid()) { + setGlow(glow.toFloat()); + if (_glow > 0.0f) { + _alpha = 0.5f; + } + } + + auto glowWidth = properties["glow"]; + if (glowWidth.isValid()) { + setGlow(glowWidth.toFloat()); + } } QVariant Line3DOverlay::getProperty(const QString& property) { diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h index db50d11276..d066677c70 100644 --- a/interface/src/ui/overlays/Line3DOverlay.h +++ b/interface/src/ui/overlays/Line3DOverlay.h @@ -30,10 +30,14 @@ public: // getters const glm::vec3& getStart() const { return _start; } const glm::vec3& getEnd() const { return _end; } + const float& getGlow() const { return _glow; } + const float& getGlowWidth() const { return _glowWidth; } // setters void setStart(const glm::vec3& start) { _start = start; } void setEnd(const glm::vec3& end) { _end = end; } + 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; @@ -43,6 +47,8 @@ public: protected: glm::vec3 _start; glm::vec3 _end; + float _glow { 0.0 }; + float _glowWidth { 0.0 }; int _geometryCacheID; }; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 6b81ad7ff3..a857dc39e0 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -43,7 +43,6 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : void ModelOverlay::update(float deltatime) { if (_updateModel) { _updateModel = false; - _model->setSnapModelToCenter(true); _model->setScaleToFit(true, getDimensions()); _model->setRotation(getRotation()); @@ -87,23 +86,15 @@ void ModelOverlay::render(RenderArgs* args) { void ModelOverlay::setProperties(const QVariantMap& properties) { auto position = getPosition(); auto rotation = getRotation(); - auto scale = getDimensions(); - + Volume3DOverlay::setProperties(properties); - + if (position != getPosition() || rotation != getRotation()) { _updateModel = true; } - if (scale != getDimensions()) { - auto newScale = getDimensions(); - if (newScale.x <= 0 || newScale.y <= 0 || newScale.z <= 0) { - setDimensions(scale); - } else { - _updateModel = true; - } - } - + _updateModel = true; + auto urlValue = properties["url"]; if (urlValue.isValid() && urlValue.canConvert()) { _url = urlValue.toString(); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 85530d1376..bbdd886d11 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -58,7 +58,7 @@ void Sphere3DOverlay::render(RenderArgs* args) { } const render::ShapeKey Sphere3DOverlay::getShapeKey() { - auto builder = render::ShapeKey::Builder().withOwnPipeline(); + auto builder = render::ShapeKey::Builder(); if (getAlpha() != 1.0f) { builder.withTranslucent(); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 18f39cd3df..e0c87fbbed 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -16,6 +16,9 @@ #include #include +#if defined(Q_OS_MAC) +#include +#endif #include #include #include @@ -612,8 +615,14 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) { if (!_vsyncSupported) { return; } -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) wglSwapIntervalEXT(enable ? 1 : 0); +#elif defined(Q_OS_MAC) + GLint interval = enable ? 1 : 0; + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); +#else + // TODO: Fill in for linux + return; #endif } @@ -621,9 +630,14 @@ bool OpenGLDisplayPlugin::isVsyncEnabled() { if (!_vsyncSupported) { return true; } -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) return wglGetSwapIntervalEXT() != 0; +#elif defined(Q_OS_MAC) + GLint interval; + CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); + return interval != 0; #else + // TODO: Fill in for linux return true; #endif } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index f1aa1edc81..c2497e5740 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -21,6 +21,11 @@ #include #include +#include +#include + +#include + #include "../Logging.h" #include "../CompositorHelper.h" @@ -58,9 +63,33 @@ bool HmdDisplayPlugin::internalActivate() { _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); }); + if (_previewTextureID == 0) { + QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png"); + if (!previewTexture.isNull()) { + glGenTextures(1, &_previewTextureID); + glBindTexture(GL_TEXTURE_2D, _previewTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, previewTexture.width(), previewTexture.height(), 0, + GL_BGRA, GL_UNSIGNED_BYTE, previewTexture.mirrored(false, true).bits()); + using namespace oglplus; + Texture::MinFilter(TextureTarget::_2D, TextureMinFilter::Linear); + Texture::MagFilter(TextureTarget::_2D, TextureMagFilter::Linear); + glBindTexture(GL_TEXTURE_2D, 0); + _previewAspect = ((float)previewTexture.width())/((float)previewTexture.height()); + _firstPreview = true; + } + } + return Parent::internalActivate(); } +void HmdDisplayPlugin::internalDeactivate() { + if (_previewTextureID != 0) { + glDeleteTextures(1, &_previewTextureID); + _previewTextureID = 0; + } + Parent::internalDeactivate(); +} + static const char * REPROJECTION_VS = R"VS(#version 410 core in vec3 Position; @@ -196,6 +225,7 @@ static ProgramPtr getReprojectionProgram() { } #endif +static GLint PREVIEW_TEXTURE_LOCATION = -1; static const char * LASER_VS = R"VS(#version 410 core uniform mat4 mvp = mat4(1); @@ -227,14 +257,24 @@ void main() { void HmdDisplayPlugin::customizeContext() { Parent::customizeContext(); // Only enable mirroring if we know vsync is disabled + // On Mac, this won't work due to how the contexts are handled, so don't try +#if !defined(Q_OS_MAC) enableVsync(false); +#endif _enablePreview = !isVsyncEnabled(); _sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); - compileProgram(_laserProgram, LASER_VS, LASER_FS); - _laserGeometry = loadLaser(_laserProgram); - compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS); using namespace oglplus; + if (!_enablePreview) { + const std::string version("#version 410 core\n"); + compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag); + PREVIEW_TEXTURE_LOCATION = Uniform(*_previewProgram, "colorMap").Location(); + } + + compileProgram(_laserProgram, LASER_VS, LASER_FS); + _laserGeometry = loadLaser(_laserProgram); + + compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS); REPROJECTION_MATRIX_LOCATION = Uniform(*_reprojectionProgram, "reprojection").Location(); INVERSE_PROJECTION_MATRIX_LOCATION = Uniform(*_reprojectionProgram, "inverseProjections").Location(); PROJECTION_MATRIX_LOCATION = Uniform(*_reprojectionProgram, "projections").Location(); @@ -243,6 +283,7 @@ void HmdDisplayPlugin::customizeContext() { void HmdDisplayPlugin::uncustomizeContext() { _sphereSection.reset(); _compositeFramebuffer.reset(); + _previewProgram.reset(); _reprojectionProgram.reset(); _laserProgram.reset(); _laserGeometry.reset(); @@ -335,30 +376,32 @@ void HmdDisplayPlugin::internalPresent() { hmdPresent(); // screen preview mirroring + auto window = _container->getPrimaryWidget(); + auto devicePixelRatio = window->devicePixelRatio(); + auto windowSize = toGlm(window->size()); + windowSize *= devicePixelRatio; + float windowAspect = aspect(windowSize); + float sceneAspect = _enablePreview ? aspect(_renderTargetSize) : _previewAspect; + if (_enablePreview && _monoPreview) { + sceneAspect /= 2.0f; + } + float aspectRatio = sceneAspect / windowAspect; + + uvec2 targetViewportSize = windowSize; + if (aspectRatio < 1.0f) { + targetViewportSize.x *= aspectRatio; + } else { + targetViewportSize.y /= aspectRatio; + } + + uvec2 targetViewportPosition; + if (targetViewportSize.x < windowSize.x) { + targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2; + } else if (targetViewportSize.y < windowSize.y) { + targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; + } + if (_enablePreview) { - auto window = _container->getPrimaryWidget(); - auto windowSize = toGlm(window->size()); - float windowAspect = aspect(windowSize); - float sceneAspect = aspect(_renderTargetSize); - if (_monoPreview) { - sceneAspect /= 2.0f; - } - float aspectRatio = sceneAspect / windowAspect; - - uvec2 targetViewportSize = windowSize; - if (aspectRatio < 1.0f) { - targetViewportSize.x *= aspectRatio; - } else { - targetViewportSize.y /= aspectRatio; - } - - uvec2 targetViewportPosition; - if (targetViewportSize.x < windowSize.x) { - targetViewportPosition.x = (windowSize.x - targetViewportSize.x) / 2; - } else if (targetViewportSize.y < windowSize.y) { - targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; - } - using namespace oglplus; Context::Clear().ColorBuffer(); auto sourceSize = _compositeFramebuffer->size; @@ -373,6 +416,21 @@ void HmdDisplayPlugin::internalPresent() { BufferSelectBit::ColorBuffer, BlitFilter::Nearest); }); swapBuffers(); + } else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) { + useProgram(_previewProgram); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y); + glUniform1i(PREVIEW_TEXTURE_LOCATION, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _previewTextureID); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + swapBuffers(); + _firstPreview = false; + _prevWindowSize = windowSize; + _prevDevicePixelRatio = devicePixelRatio; } postPreview(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index fada15d864..8e48690fd1 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -40,6 +40,7 @@ protected: virtual void updatePresentPose(); bool internalActivate() override; + void internalDeactivate() override; void compositeScene() override; void compositeOverlay() override; void compositePointer() override; @@ -89,8 +90,17 @@ private: bool _enablePreview { false }; bool _monoPreview { true }; bool _enableReprojection { true }; - ShapeWrapperPtr _sphereSection; + bool _firstPreview { true }; + + ProgramPtr _previewProgram; + float _previewAspect { 0 }; + GLuint _previewTextureID { 0 }; + glm::uvec2 _prevWindowSize { 0, 0 }; + qreal _prevDevicePixelRatio { 0 }; + ProgramPtr _reprojectionProgram; + ShapeWrapperPtr _sphereSection; + ProgramPtr _laserProgram; ShapeWrapperPtr _laserGeometry; }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 56f6438e70..1ec934be92 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -291,7 +291,9 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { foreach(const EntityItemID& entityID, _currentEntitiesInside) { if (!entitiesContainingAvatar.contains(entityID)) { emit leaveEntity(entityID); - _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); + } } } @@ -299,7 +301,9 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { foreach(const EntityItemID& entityID, entitiesContainingAvatar) { if (!_currentEntitiesInside.contains(entityID)) { emit enterEntity(entityID); - _entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity"); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "enterEntity"); + } } } _currentEntitiesInside = entitiesContainingAvatar; @@ -315,7 +319,9 @@ void EntityTreeRenderer::leaveAllEntities() { // for all of our previous containing entities, if they are no longer containing then send them a leave event foreach(const EntityItemID& entityID, _currentEntitiesInside) { emit leaveEntity(entityID); - _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); + } } _currentEntitiesInside.clear(); forceRecheckEntities(); @@ -652,11 +658,15 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { } emit mousePressOnEntity(rayPickResult, event); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event)); + } _currentClickingOnEntityID = rayPickResult.entityID; emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event)); + } } else { emit mousePressOffEntity(rayPickResult, event); } @@ -677,14 +687,18 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { if (rayPickResult.intersects) { //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; emit mouseReleaseOnEntity(rayPickResult, event); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event)); + } } // Even if we're no longer intersecting with an entity, if we started clicking on it, and now // we're releasing the button, then this is considered a clickOn event if (!_currentClickingOnEntityID.isInvalidID()) { emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event)); + } } // makes it the unknown ID, we just released so we can't be clicking on anything @@ -707,8 +721,10 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event)); + } // handle the hover logic... @@ -716,19 +732,25 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // then we need to send the hover leave. if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + } } // If the new hover entity does not match the previous hover entity then we are entering the new one // this is true if the _currentHoverOverEntityID is known or unknown if (rayPickResult.entityID != _currentHoverOverEntityID) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event)); + } } // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and // we should send our hover over event emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event)); + } // remember what we're hovering over _currentHoverOverEntityID = rayPickResult.entityID; @@ -739,7 +761,9 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // send the hover leave for our previous entity if (!_currentHoverOverEntityID.isInvalidID()) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + } _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID } } @@ -748,14 +772,16 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // not yet released the hold then this is still considered a holdingClickOnEntity event if (!_currentClickingOnEntityID.isInvalidID()) { emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event)); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event)); + } } _lastMouseEvent = MouseEvent(*event); _lastMouseEventValid = true; } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { - if (_tree && !_shuttingDown) { + if (_tree && !_shuttingDown && _entitiesScriptEngine) { _entitiesScriptEngine->unloadEntityScript(entityID); } @@ -801,7 +827,7 @@ void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) { if (_tree && !_shuttingDown) { EntityItemPointer entity = getTree()->findEntityByEntityItemID(entityID); - if (entity && entity->shouldPreloadScript()) { + if (entity && entity->shouldPreloadScript() && _entitiesScriptEngine) { QString scriptUrl = entity->getScript(); scriptUrl = ResourceManager::normalizeURL(scriptUrl); ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload); @@ -910,12 +936,16 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons // And now the entity scripts if (isCollisionOwner(myNodeID, entityTree, idA, collision)) { emit collisionWithEntity(idA, idB, collision); - _entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(idA, "collisionWithEntity", idB, collision); + } } if (isCollisionOwner(myNodeID, entityTree, idA, collision)) { emit collisionWithEntity(idB, idA, collision); - _entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(idB, "collisionWithEntity", idA, collision); + } } } @@ -941,7 +971,9 @@ void EntityTreeRenderer::updateZone(const EntityItemID& id) { if (zone && zone->contains(_lastAvatarPosition)) { _currentEntitiesInside << id; emit enterEntity(id); - _entitiesScriptEngine->callEntityScriptMethod(id, "enterEntity"); + if (_entitiesScriptEngine) { + _entitiesScriptEngine->callEntityScriptMethod(id, "enterEntity"); + } if (zone->getVisible()) { _bestZone = std::dynamic_pointer_cast(zone); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 43fea75eac..416a38d27c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -342,16 +342,23 @@ void RenderableModelEntityItem::updateModelBounds() { if (!hasModel() || !_model) { return; } + if (!_dimensionsInitialized || !_model->isActive()) { + return; + } + + bool movingOrAnimating = isMovingRelativeToParent() || isAnimatingSomething(); glm::vec3 dimensions = getDimensions(); - if ((movingOrAnimating || + bool success; + auto transform = getTransform(success); + + if (movingOrAnimating || _needsInitialSimulation || _needsJointSimulation || - _model->getTranslation() != getPosition() || + _model->getTranslation() != transform.getTranslation() || _model->getScaleToFitDimensions() != dimensions || - _model->getRotation() != getRotation() || - _model->getRegistrationPoint() != getRegistrationPoint()) - && _model->isActive() && _dimensionsInitialized) { + _model->getRotation() != transform.getRotation() || + _model->getRegistrationPoint() != getRegistrationPoint()) { doInitialModelSimulation(); _needsJointSimulation = false; } @@ -423,7 +430,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; + bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0 + && getShapeType() == SHAPE_TYPE_COMPOUND; if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) { _showCollisionHull = shouldShowCollisionHull; render::PendingChanges pendingChanges; @@ -593,7 +601,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { // the model is still being downloaded. return false; - } else if (type == SHAPE_TYPE_STATIC_MESH) { + } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { return (_model && _model->isLoaded()); } return true; @@ -607,7 +615,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // should never fall in here when collision model not fully loaded // hence we assert that all geometries exist and are loaded - assert(_model->isLoaded() && _model->isCollisionLoaded()); + assert(_model && _model->isLoaded() && _model->isCollisionLoaded()); const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry(); ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); @@ -691,14 +699,19 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } } info.setParams(type, dimensions, _compoundShapeURL); - } else if (type == SHAPE_TYPE_STATIC_MESH) { + } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + updateModelBounds(); + + // should never fall in here when model not fully loaded + assert(_model && _model->isLoaded()); + // compute meshPart local transforms QVector localTransforms; - const FBXGeometry& geometry = _model->getFBXGeometry(); - int numberOfMeshes = geometry.meshes.size(); + const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); + int numberOfMeshes = fbxGeometry.meshes.size(); int totalNumVertices = 0; for (int i = 0; i < numberOfMeshes; i++) { - const FBXMesh& mesh = geometry.meshes.at(i); + const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { const FBXCluster& cluster = mesh.clusters.at(0); auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex); @@ -716,30 +729,42 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { return; } - updateModelBounds(); - - // should never fall in here when collision model not fully loaded - assert(_model->isLoaded()); + auto& meshes = _model->getGeometry()->getGeometry()->getMeshes(); + int32_t numMeshes = (int32_t)(meshes.size()); ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); pointCollection.clear(); - - ShapeInfo::PointList points; - ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); - auto& meshes = _model->getGeometry()->getGeometry()->getMeshes(); + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + pointCollection.resize(numMeshes); + } else { + pointCollection.resize(1); + } Extents extents; int meshCount = 0; + int pointListIndex = 0; for (auto& mesh : meshes) { const gpu::BufferView& vertices = mesh->getVertexBuffer(); const gpu::BufferView& indices = mesh->getIndexBuffer(); const gpu::BufferView& parts = mesh->getPartBuffer(); + ShapeInfo::PointList& points = pointCollection[pointListIndex]; + + // reserve room + int32_t sizeToReserve = (int32_t)(vertices.getNumElements()); + if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // a list of points for each mesh + pointListIndex++; + } else { + // only one list of points + sizeToReserve += (int32_t)((gpu::Size)points.size()); + } + points.reserve(sizeToReserve); + // copy points - const glm::mat4& localTransform = localTransforms[meshCount]; uint32_t meshIndexOffset = (uint32_t)points.size(); + const glm::mat4& localTransform = localTransforms[meshCount]; gpu::BufferView::Iterator vertexItr = vertices.cbegin(); - points.reserve((int32_t)((gpu::Size)points.size() + vertices.getNumElements())); while (vertexItr != vertices.cend()) { glm::vec3 point = extractTranslation(localTransform * glm::translate(*vertexItr)); points.push_back(point); @@ -747,55 +772,57 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { ++vertexItr; } - // copy triangleIndices - triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); - gpu::BufferView::Iterator partItr = parts.cbegin(); - while (partItr != parts.cend()) { - - if (partItr->_topology == model::Mesh::TRIANGLES) { - assert(partItr->_numIndices % 3 == 0); - auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + partItr->_numIndices; - while (indexItr != indexEnd) { - triangleIndices.push_back(*indexItr + meshIndexOffset); - ++indexItr; - } - } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { - assert(partItr->_numIndices > 2); - uint32_t approxNumIndices = 3 * partItr->_numIndices; - if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) { - // we underestimated the final size of triangleIndices so we pre-emptively expand it - triangleIndices.reserve(triangleIndices.size() + approxNumIndices); - } - - auto indexItr = indices.cbegin() + partItr->_startIndex; - auto indexEnd = indexItr + (partItr->_numIndices - 2); - - // first triangle uses the first three indices - triangleIndices.push_back(*(indexItr++) + meshIndexOffset); - triangleIndices.push_back(*(indexItr++) + meshIndexOffset); - triangleIndices.push_back(*(indexItr++) + meshIndexOffset); - - // the rest use previous and next index - uint32_t triangleCount = 1; - while (indexItr != indexEnd) { - if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { - if (triangleCount % 2 == 0) { - // even triangles use first two indices in order - triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); - triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); - } else { - // odd triangles swap order of first two indices - triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); - triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); - } + if (type == SHAPE_TYPE_STATIC_MESH) { + // copy into triangleIndices + ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { + if (partItr->_topology == model::Mesh::TRIANGLES) { + assert(partItr->_numIndices % 3 == 0); + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + partItr->_numIndices; + while (indexItr != indexEnd) { triangleIndices.push_back(*indexItr + meshIndexOffset); - ++triangleCount; + ++indexItr; + } + } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { + assert(partItr->_numIndices > 2); + uint32_t approxNumIndices = 3 * partItr->_numIndices; + if (approxNumIndices > (uint32_t)(triangleIndices.capacity() - triangleIndices.size())) { + // we underestimated the final size of triangleIndices so we pre-emptively expand it + triangleIndices.reserve(triangleIndices.size() + approxNumIndices); + } + + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + (partItr->_numIndices - 2); + + // first triangle uses the first three indices + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + triangleIndices.push_back(*(indexItr++) + meshIndexOffset); + + // the rest use previous and next index + uint32_t triangleCount = 1; + while (indexItr != indexEnd) { + if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { + if (triangleCount % 2 == 0) { + // even triangles use first two indices in order + triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); + triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); + } else { + // odd triangles swap order of first two indices + triangleIndices.push_back(*(indexItr - 1) + meshIndexOffset); + triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); + } + triangleIndices.push_back(*indexItr + meshIndexOffset); + ++triangleCount; + } + ++indexItr; } - ++indexItr; } + ++partItr; } - ++partItr; } ++meshCount; } @@ -808,12 +835,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { scaleToFit[i] = 1.0f; } } - for (int i = 0; i < points.size(); ++i) { - points[i] = (points[i] * scaleToFit); + for (auto points : pointCollection) { + for (int i = 0; i < points.size(); ++i) { + points[i] = (points[i] * scaleToFit); + } } - pointCollection.push_back(points); - info.setParams(SHAPE_TYPE_STATIC_MESH, 0.5f * dimensions, _modelURL); + info.setParams(type, 0.5f * dimensions, _modelURL); } else { ModelEntityItem::computeShapeInfo(info); info.setParams(type, 0.5f * dimensions); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a62f4b182a..dcd7e25bc1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -101,6 +101,8 @@ const char* shapeTypeNames[] = { "hull", "plane", "compound", + "simple-hull", + "simple-compound", "static-mesh" }; @@ -123,6 +125,8 @@ void buildStringToShapeTypeLookup() { addShapeType(SHAPE_TYPE_HULL); addShapeType(SHAPE_TYPE_PLANE); addShapeType(SHAPE_TYPE_COMPOUND); + addShapeType(SHAPE_TYPE_SIMPLE_HULL); + addShapeType(SHAPE_TYPE_SIMPLE_COMPOUND); addShapeType(SHAPE_TYPE_STATIC_MESH); } diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index 593537a291..7947bd45df 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -20,6 +20,7 @@ #include #include +#include #endif diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index f113be1cfb..8b0bd9981f 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -43,17 +43,21 @@ int GLWidget::getDeviceHeight() const { void GLWidget::initializeGL() { setAttribute(Qt::WA_AcceptTouchEvents); + grabGesture(Qt::PinchGesture); setAcceptDrops(true); // Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate. setAutoBufferSwap(false); - // TODO: write the proper code for linux makeCurrent(); -#if defined(Q_OS_WIN) if (isValid() && context() && context()->contextHandle()) { - _vsyncSupported = context()->contextHandle()->hasExtension("WGL_EXT_swap_control");; - } +#if defined(Q_OS_WIN) + _vsyncSupported = context()->contextHandle()->hasExtension("WGL_EXT_swap_control"); +#elif defined(Q_OS_MAC) + _vsyncSupported = true; +#else + // TODO: write the proper code for linux #endif + } } void GLWidget::paintEvent(QPaintEvent* event) { @@ -78,6 +82,7 @@ bool GLWidget::event(QEvent* event) { case QEvent::TouchBegin: case QEvent::TouchEnd: case QEvent::TouchUpdate: + case QEvent::Gesture: case QEvent::Wheel: case QEvent::DragEnter: case QEvent::Drop: diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index c89bc88899..8dc3854b41 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -25,40 +25,45 @@ GLShader::~GLShader() { } } +// GLSL version +static const std::string glslVersion { + "#version 410 core" +}; + +// Shader domain +static const size_t NUM_SHADER_DOMAINS = 3; + +// GL Shader type enums +// Must match the order of type specified in gpu::Shader::Type +static const std::array SHADER_DOMAINS { { + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + GL_GEOMETRY_SHADER, +} }; + +// Domain specific defines +// Must match the order of type specified in gpu::Shader::Type +static const std::array DOMAIN_DEFINES { { + "#define GPU_VERTEX_SHADER", + "#define GPU_PIXEL_SHADER", + "#define GPU_GEOMETRY_SHADER", +} }; + +// Versions specific of the shader +static const std::array VERSION_DEFINES { { + "" +} }; + GLShader* compileBackendShader(const Shader& shader) { // Any GLSLprogram ? normally yes... const std::string& shaderSource = shader.getSource().getCode(); - - // GLSL version - const std::string glslVersion = { - "#version 410 core" - }; - - // Shader domain - const int NUM_SHADER_DOMAINS = 2; - const GLenum SHADER_DOMAINS[NUM_SHADER_DOMAINS] = { - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER - }; GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - - // Domain specific defines - const std::string domainDefines[NUM_SHADER_DOMAINS] = { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER" - }; - - // Versions specific of the shader - const std::string versionDefines[GLShader::NumVersions] = { - "" - }; - GLShader::ShaderObjects shaderObjects; for (int version = 0; version < GLShader::NumVersions; version++) { auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = glslVersion + "\n" + domainDefines[shader.getType()] + "\n" + versionDefines[version]; + std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version]; bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); if (!result) { diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 6dc1d63ca8..9161ee3642 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -26,28 +26,43 @@ ProfileRangeBatch::~ProfileRangeBatch() { } #endif -#define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); +#define ADD_COMMAND(call) _commands.emplace_back(COMMAND_##call); _commandOffsets.emplace_back(_params.size()); using namespace gpu; -Batch::Batch(const CacheState& cacheState) : Batch() { - _commands.reserve(cacheState.commandsSize); - _commandOffsets.reserve(cacheState.offsetsSize); - _params.reserve(cacheState.paramsSize); - _data.reserve(cacheState.dataSize); -} +size_t Batch::_commandsMax { BATCH_PREALLOCATE_MIN }; +size_t Batch::_commandOffsetsMax { BATCH_PREALLOCATE_MIN }; +size_t Batch::_paramsMax { BATCH_PREALLOCATE_MIN }; +size_t Batch::_dataMax { BATCH_PREALLOCATE_MIN }; +size_t Batch::_objectsMax { BATCH_PREALLOCATE_MIN }; +size_t Batch::_drawCallInfosMax { BATCH_PREALLOCATE_MIN }; -Batch::CacheState Batch::getCacheState() { - return CacheState(_commands.size(), _commandOffsets.size(), _params.size(), _data.size(), - _buffers.size(), _textures.size(), _streamFormats.size(), _transforms.size(), _pipelines.size(), - _framebuffers.size(), _queries.size()); +Batch::Batch() { + _commands.reserve(_commandsMax); + _commandOffsets.reserve(_commandOffsetsMax); + _params.reserve(_paramsMax); + _data.reserve(_dataMax); + _objects.reserve(_objectsMax); + _drawCallInfos.reserve(_drawCallInfosMax); } Batch::~Batch() { - //qDebug() << "Batch::~Batch()... " << getCacheState(); + _commandsMax = std::max(_commands.size(), _commandsMax); + _commandOffsetsMax = std::max(_commandOffsets.size(), _commandOffsetsMax); + _paramsMax = std::max(_params.size(), _paramsMax); + _dataMax = std::max(_data.size(), _dataMax); + _objectsMax = std::max(_objects.size(), _objectsMax); + _drawCallInfosMax = std::max(_drawCallInfos.size(), _drawCallInfosMax); } void Batch::clear() { + _commandsMax = std::max(_commands.size(), _commandsMax); + _commandOffsetsMax = std::max(_commandOffsets.size(), _commandOffsetsMax); + _paramsMax = std::max(_params.size(), _paramsMax); + _dataMax = std::max(_data.size(), _dataMax); + _objectsMax = std::max(_objects.size(), _objectsMax); + _drawCallInfosMax = std::max(_drawCallInfos.size(), _drawCallInfosMax); + _commands.clear(); _commandOffsets.clear(); _params.clear(); @@ -58,6 +73,8 @@ void Batch::clear() { _transforms.clear(); _pipelines.clear(); _framebuffers.clear(); + _objects.clear(); + _drawCallInfos.clear(); } size_t Batch::cacheData(size_t size, const void* data) { @@ -72,9 +89,9 @@ size_t Batch::cacheData(size_t size, const void* data) { void Batch::draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex) { ADD_COMMAND(draw); - _params.push_back(startVertex); - _params.push_back(numVertices); - _params.push_back(primitiveType); + _params.emplace_back(startVertex); + _params.emplace_back(numVertices); + _params.emplace_back(primitiveType); captureDrawCallInfo(); } @@ -82,9 +99,9 @@ void Batch::draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex void Batch::drawIndexed(Primitive primitiveType, uint32 numIndices, uint32 startIndex) { ADD_COMMAND(drawIndexed); - _params.push_back(startIndex); - _params.push_back(numIndices); - _params.push_back(primitiveType); + _params.emplace_back(startIndex); + _params.emplace_back(numIndices); + _params.emplace_back(primitiveType); captureDrawCallInfo(); } @@ -92,11 +109,11 @@ void Batch::drawIndexed(Primitive primitiveType, uint32 numIndices, uint32 start void Batch::drawInstanced(uint32 numInstances, Primitive primitiveType, uint32 numVertices, uint32 startVertex, uint32 startInstance) { ADD_COMMAND(drawInstanced); - _params.push_back(startInstance); - _params.push_back(startVertex); - _params.push_back(numVertices); - _params.push_back(primitiveType); - _params.push_back(numInstances); + _params.emplace_back(startInstance); + _params.emplace_back(startVertex); + _params.emplace_back(numVertices); + _params.emplace_back(primitiveType); + _params.emplace_back(numInstances); captureDrawCallInfo(); } @@ -104,11 +121,11 @@ void Batch::drawInstanced(uint32 numInstances, Primitive primitiveType, uint32 n void Batch::drawIndexedInstanced(uint32 numInstances, Primitive primitiveType, uint32 numIndices, uint32 startIndex, uint32 startInstance) { ADD_COMMAND(drawIndexedInstanced); - _params.push_back(startInstance); - _params.push_back(startIndex); - _params.push_back(numIndices); - _params.push_back(primitiveType); - _params.push_back(numInstances); + _params.emplace_back(startInstance); + _params.emplace_back(startIndex); + _params.emplace_back(numIndices); + _params.emplace_back(primitiveType); + _params.emplace_back(numInstances); captureDrawCallInfo(); } @@ -116,16 +133,16 @@ void Batch::drawIndexedInstanced(uint32 numInstances, Primitive primitiveType, u void Batch::multiDrawIndirect(uint32 numCommands, Primitive primitiveType) { ADD_COMMAND(multiDrawIndirect); - _params.push_back(numCommands); - _params.push_back(primitiveType); + _params.emplace_back(numCommands); + _params.emplace_back(primitiveType); captureDrawCallInfo(); } void Batch::multiDrawIndexedIndirect(uint32 nbCommands, Primitive primitiveType) { ADD_COMMAND(multiDrawIndexedIndirect); - _params.push_back(nbCommands); - _params.push_back(primitiveType); + _params.emplace_back(nbCommands); + _params.emplace_back(primitiveType); captureDrawCallInfo(); } @@ -133,16 +150,16 @@ void Batch::multiDrawIndexedIndirect(uint32 nbCommands, Primitive primitiveType) void Batch::setInputFormat(const Stream::FormatPointer& format) { ADD_COMMAND(setInputFormat); - _params.push_back(_streamFormats.cache(format)); + _params.emplace_back(_streamFormats.cache(format)); } void Batch::setInputBuffer(Slot channel, const BufferPointer& buffer, Offset offset, Offset stride) { ADD_COMMAND(setInputBuffer); - _params.push_back(stride); - _params.push_back(offset); - _params.push_back(_buffers.cache(buffer)); - _params.push_back(channel); + _params.emplace_back(stride); + _params.emplace_back(offset); + _params.emplace_back(_buffers.cache(buffer)); + _params.emplace_back(channel); } void Batch::setInputBuffer(Slot channel, const BufferView& view) { @@ -163,9 +180,9 @@ void Batch::setInputStream(Slot startChannel, const BufferStream& stream) { void Batch::setIndexBuffer(Type type, const BufferPointer& buffer, Offset offset) { ADD_COMMAND(setIndexBuffer); - _params.push_back(offset); - _params.push_back(_buffers.cache(buffer)); - _params.push_back(type); + _params.emplace_back(offset); + _params.emplace_back(_buffers.cache(buffer)); + _params.emplace_back(type); } void Batch::setIndexBuffer(const BufferView& buffer) { @@ -175,9 +192,9 @@ void Batch::setIndexBuffer(const BufferView& buffer) { void Batch::setIndirectBuffer(const BufferPointer& buffer, Offset offset, Offset stride) { ADD_COMMAND(setIndirectBuffer); - _params.push_back(_buffers.cache(buffer)); - _params.push_back(offset); - _params.push_back(stride); + _params.emplace_back(_buffers.cache(buffer)); + _params.emplace_back(offset); + _params.emplace_back(stride); } @@ -191,56 +208,56 @@ void Batch::setModelTransform(const Transform& model) { void Batch::setViewTransform(const Transform& view) { ADD_COMMAND(setViewTransform); - _params.push_back(_transforms.cache(view)); + _params.emplace_back(_transforms.cache(view)); } void Batch::setProjectionTransform(const Mat4& proj) { ADD_COMMAND(setProjectionTransform); - _params.push_back(cacheData(sizeof(Mat4), &proj)); + _params.emplace_back(cacheData(sizeof(Mat4), &proj)); } void Batch::setViewportTransform(const Vec4i& viewport) { ADD_COMMAND(setViewportTransform); - _params.push_back(cacheData(sizeof(Vec4i), &viewport)); + _params.emplace_back(cacheData(sizeof(Vec4i), &viewport)); } void Batch::setDepthRangeTransform(float nearDepth, float farDepth) { ADD_COMMAND(setDepthRangeTransform); - _params.push_back(farDepth); - _params.push_back(nearDepth); + _params.emplace_back(farDepth); + _params.emplace_back(nearDepth); } void Batch::setPipeline(const PipelinePointer& pipeline) { ADD_COMMAND(setPipeline); - _params.push_back(_pipelines.cache(pipeline)); + _params.emplace_back(_pipelines.cache(pipeline)); } void Batch::setStateBlendFactor(const Vec4& factor) { ADD_COMMAND(setStateBlendFactor); - _params.push_back(factor.x); - _params.push_back(factor.y); - _params.push_back(factor.z); - _params.push_back(factor.w); + _params.emplace_back(factor.x); + _params.emplace_back(factor.y); + _params.emplace_back(factor.z); + _params.emplace_back(factor.w); } void Batch::setStateScissorRect(const Vec4i& rect) { ADD_COMMAND(setStateScissorRect); - _params.push_back(cacheData(sizeof(Vec4i), &rect)); + _params.emplace_back(cacheData(sizeof(Vec4i), &rect)); } void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) { ADD_COMMAND(setUniformBuffer); - _params.push_back(size); - _params.push_back(offset); - _params.push_back(_buffers.cache(buffer)); - _params.push_back(slot); + _params.emplace_back(size); + _params.emplace_back(offset); + _params.emplace_back(_buffers.cache(buffer)); + _params.emplace_back(slot); } void Batch::setUniformBuffer(uint32 slot, const BufferView& view) { @@ -251,8 +268,8 @@ void Batch::setUniformBuffer(uint32 slot, const BufferView& view) { void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { ADD_COMMAND(setResourceTexture); - _params.push_back(_textures.cache(texture)); - _params.push_back(slot); + _params.emplace_back(_textures.cache(texture)); + _params.emplace_back(slot); } void Batch::setResourceTexture(uint32 slot, const TextureView& view) { @@ -262,21 +279,21 @@ void Batch::setResourceTexture(uint32 slot, const TextureView& view) { void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { ADD_COMMAND(setFramebuffer); - _params.push_back(_framebuffers.cache(framebuffer)); + _params.emplace_back(_framebuffers.cache(framebuffer)); } void Batch::clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor) { ADD_COMMAND(clearFramebuffer); - _params.push_back(enableScissor); - _params.push_back(stencil); - _params.push_back(depth); - _params.push_back(color.w); - _params.push_back(color.z); - _params.push_back(color.y); - _params.push_back(color.x); - _params.push_back(targets); + _params.emplace_back(enableScissor); + _params.emplace_back(stencil); + _params.emplace_back(depth); + _params.emplace_back(color.w); + _params.emplace_back(color.z); + _params.emplace_back(color.y); + _params.emplace_back(color.x); + _params.emplace_back(targets); } void Batch::clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color, bool enableScissor) { @@ -299,40 +316,40 @@ void Batch::blit(const FramebufferPointer& src, const Vec4i& srcViewport, const FramebufferPointer& dst, const Vec4i& dstViewport) { ADD_COMMAND(blit); - _params.push_back(_framebuffers.cache(src)); - _params.push_back(srcViewport.x); - _params.push_back(srcViewport.y); - _params.push_back(srcViewport.z); - _params.push_back(srcViewport.w); - _params.push_back(_framebuffers.cache(dst)); - _params.push_back(dstViewport.x); - _params.push_back(dstViewport.y); - _params.push_back(dstViewport.z); - _params.push_back(dstViewport.w); + _params.emplace_back(_framebuffers.cache(src)); + _params.emplace_back(srcViewport.x); + _params.emplace_back(srcViewport.y); + _params.emplace_back(srcViewport.z); + _params.emplace_back(srcViewport.w); + _params.emplace_back(_framebuffers.cache(dst)); + _params.emplace_back(dstViewport.x); + _params.emplace_back(dstViewport.y); + _params.emplace_back(dstViewport.z); + _params.emplace_back(dstViewport.w); } void Batch::generateTextureMips(const TexturePointer& texture) { ADD_COMMAND(generateTextureMips); - _params.push_back(_textures.cache(texture)); + _params.emplace_back(_textures.cache(texture)); } void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); - _params.push_back(_queries.cache(query)); + _params.emplace_back(_queries.cache(query)); } void Batch::endQuery(const QueryPointer& query) { ADD_COMMAND(endQuery); - _params.push_back(_queries.cache(query)); + _params.emplace_back(_queries.cache(query)); } void Batch::getQuery(const QueryPointer& query) { ADD_COMMAND(getQuery); - _params.push_back(_queries.cache(query)); + _params.emplace_back(_queries.cache(query)); } void Batch::resetStages() { @@ -341,12 +358,12 @@ void Batch::resetStages() { void Batch::runLambda(std::function f) { ADD_COMMAND(runLambda); - _params.push_back(_lambdas.cache(f)); + _params.emplace_back(_lambdas.cache(f)); } void Batch::startNamedCall(const std::string& name) { ADD_COMMAND(startNamedCall); - _params.push_back(_names.cache(name)); + _params.emplace_back(_names.cache(name)); _currentNamedCall = name; } @@ -422,14 +439,14 @@ void Batch::captureDrawCallInfoImpl() { //_model.getInverseMatrix(_object._modelInverse); object._modelInverse = glm::inverse(object._model); - _objects.push_back(object); + _objects.emplace_back(object); // Flag is clean _invalidModel = false; } auto& drawCallInfos = getDrawCallInfoBuffer(); - drawCallInfos.push_back((uint16)_objects.size() - 1); + drawCallInfos.emplace_back((uint16)_objects.size() - 1); } void Batch::captureDrawCallInfo() { @@ -458,22 +475,11 @@ void Batch::preExecute() { } } -QDebug& operator<<(QDebug& debug, const Batch::CacheState& cacheState) { - debug << "Batch::CacheState[ " - << "commandsSize:" << cacheState.commandsSize - << "offsetsSize:" << cacheState.offsetsSize - << "paramsSize:" << cacheState.paramsSize - << "dataSize:" << cacheState.dataSize - << "]"; - - return debug; -} - // Debugging void Batch::pushProfileRange(const char* name) { #if defined(NSIGHT_FOUND) ADD_COMMAND(pushProfileRange); - _params.push_back(_profileRanges.cache(name)); + _params.emplace_back(_profileRanges.cache(name)); #endif } @@ -490,9 +496,9 @@ void Batch::_glActiveBindTexture(uint32 unit, uint32 target, uint32 texture) { setResourceTexture(unit - GL_TEXTURE0, nullptr); ADD_COMMAND(glActiveBindTexture); - _params.push_back(texture); - _params.push_back(target); - _params.push_back(unit); + _params.emplace_back(texture); + _params.emplace_back(target); + _params.emplace_back(unit); } void Batch::_glUniform1i(int32 location, int32 v0) { @@ -500,8 +506,8 @@ void Batch::_glUniform1i(int32 location, int32 v0) { return; } ADD_COMMAND(glUniform1i); - _params.push_back(v0); - _params.push_back(location); + _params.emplace_back(v0); + _params.emplace_back(location); } void Batch::_glUniform1f(int32 location, float v0) { @@ -509,89 +515,89 @@ void Batch::_glUniform1f(int32 location, float v0) { return; } ADD_COMMAND(glUniform1f); - _params.push_back(v0); - _params.push_back(location); + _params.emplace_back(v0); + _params.emplace_back(location); } void Batch::_glUniform2f(int32 location, float v0, float v1) { ADD_COMMAND(glUniform2f); - _params.push_back(v1); - _params.push_back(v0); - _params.push_back(location); + _params.emplace_back(v1); + _params.emplace_back(v0); + _params.emplace_back(location); } void Batch::_glUniform3f(int32 location, float v0, float v1, float v2) { ADD_COMMAND(glUniform3f); - _params.push_back(v2); - _params.push_back(v1); - _params.push_back(v0); - _params.push_back(location); + _params.emplace_back(v2); + _params.emplace_back(v1); + _params.emplace_back(v0); + _params.emplace_back(location); } void Batch::_glUniform4f(int32 location, float v0, float v1, float v2, float v3) { ADD_COMMAND(glUniform4f); - _params.push_back(v3); - _params.push_back(v2); - _params.push_back(v1); - _params.push_back(v0); - _params.push_back(location); + _params.emplace_back(v3); + _params.emplace_back(v2); + _params.emplace_back(v1); + _params.emplace_back(v0); + _params.emplace_back(location); } void Batch::_glUniform3fv(int32 location, int count, const float* value) { ADD_COMMAND(glUniform3fv); const int VEC3_SIZE = 3 * sizeof(float); - _params.push_back(cacheData(count * VEC3_SIZE, value)); - _params.push_back(count); - _params.push_back(location); + _params.emplace_back(cacheData(count * VEC3_SIZE, value)); + _params.emplace_back(count); + _params.emplace_back(location); } void Batch::_glUniform4fv(int32 location, int count, const float* value) { ADD_COMMAND(glUniform4fv); const int VEC4_SIZE = 4 * sizeof(float); - _params.push_back(cacheData(count * VEC4_SIZE, value)); - _params.push_back(count); - _params.push_back(location); + _params.emplace_back(cacheData(count * VEC4_SIZE, value)); + _params.emplace_back(count); + _params.emplace_back(location); } void Batch::_glUniform4iv(int32 location, int count, const int32* value) { ADD_COMMAND(glUniform4iv); const int VEC4_SIZE = 4 * sizeof(int); - _params.push_back(cacheData(count * VEC4_SIZE, value)); - _params.push_back(count); - _params.push_back(location); + _params.emplace_back(cacheData(count * VEC4_SIZE, value)); + _params.emplace_back(count); + _params.emplace_back(location); } void Batch::_glUniformMatrix3fv(int32 location, int count, uint8 transpose, const float* value) { ADD_COMMAND(glUniformMatrix3fv); const int MATRIX3_SIZE = 9 * sizeof(float); - _params.push_back(cacheData(count * MATRIX3_SIZE, value)); - _params.push_back(transpose); - _params.push_back(count); - _params.push_back(location); + _params.emplace_back(cacheData(count * MATRIX3_SIZE, value)); + _params.emplace_back(transpose); + _params.emplace_back(count); + _params.emplace_back(location); } void Batch::_glUniformMatrix4fv(int32 location, int count, uint8 transpose, const float* value) { ADD_COMMAND(glUniformMatrix4fv); const int MATRIX4_SIZE = 16 * sizeof(float); - _params.push_back(cacheData(count * MATRIX4_SIZE, value)); - _params.push_back(transpose); - _params.push_back(count); - _params.push_back(location); + _params.emplace_back(cacheData(count * MATRIX4_SIZE, value)); + _params.emplace_back(transpose); + _params.emplace_back(count); + _params.emplace_back(location); } void Batch::_glColor4f(float red, float green, float blue, float alpha) { ADD_COMMAND(glColor4f); - _params.push_back(alpha); - _params.push_back(blue); - _params.push_back(green); - _params.push_back(red); -} \ No newline at end of file + _params.emplace_back(alpha); + _params.emplace_back(blue); + _params.emplace_back(green); + _params.emplace_back(red); +} diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 4e51038368..9cf1ca8269 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -26,7 +26,7 @@ #include "Transform.h" class QDebug; - +#define BATCH_PREALLOCATE_MIN 128 namespace gpu { enum ReservedSlot { @@ -87,6 +87,7 @@ public: using NamedBatchDataMap = std::map; DrawCallInfoBuffer _drawCallInfos; + static size_t _drawCallInfosMax; std::string _currentNamedCall; @@ -96,34 +97,7 @@ public: void captureDrawCallInfo(); void captureNamedDrawCallInfo(std::string name); - class CacheState { - public: - size_t commandsSize; - size_t offsetsSize; - size_t paramsSize; - size_t dataSize; - - size_t buffersSize; - size_t texturesSize; - size_t streamFormatsSize; - size_t transformsSize; - size_t pipelinesSize; - size_t framebuffersSize; - size_t queriesSize; - - CacheState() : commandsSize(0), offsetsSize(0), paramsSize(0), dataSize(0), buffersSize(0), texturesSize(0), - streamFormatsSize(0), transformsSize(0), pipelinesSize(0), framebuffersSize(0), queriesSize(0) { } - - CacheState(size_t commandsSize, size_t offsetsSize, size_t paramsSize, size_t dataSize, size_t buffersSize, - size_t texturesSize, size_t streamFormatsSize, size_t transformsSize, size_t pipelinesSize, - size_t framebuffersSize, size_t queriesSize) : - commandsSize(commandsSize), offsetsSize(offsetsSize), paramsSize(paramsSize), dataSize(dataSize), - buffersSize(buffersSize), texturesSize(texturesSize), streamFormatsSize(streamFormatsSize), - transformsSize(transformsSize), pipelinesSize(pipelinesSize), framebuffersSize(framebuffersSize), queriesSize(queriesSize) { } - }; - - Batch() {} - Batch(const CacheState& cacheState); + Batch(); explicit Batch(const Batch& batch); ~Batch(); @@ -131,9 +105,6 @@ public: void preExecute(); - CacheState getCacheState(); - - // Batches may need to override the context level stereo settings // if they're performing framebuffer copy operations, like the // deferred lighting resolution mechanism @@ -401,15 +372,25 @@ public: typedef T Data; Data _data; Cache(const Data& data) : _data(data) {} + static size_t _max; class Vector { public: std::vector< Cache > _items; + Vector() { + _items.reserve(_max); + } + + ~Vector() { + _max = std::max(_items.size(), _max); + } + + size_t size() const { return _items.size(); } size_t cache(const Data& data) { size_t offset = _items.size(); - _items.push_back(Cache(data)); + _items.emplace_back(data); return offset; } @@ -449,9 +430,16 @@ public: } Commands _commands; + static size_t _commandsMax; + CommandOffsets _commandOffsets; + static size_t _commandOffsetsMax; + Params _params; + static size_t _paramsMax; + Bytes _data; + static size_t _dataMax; // SSBO class... layout MUST match the layout in Transform.slh class TransformObject { @@ -464,6 +452,7 @@ public: bool _invalidModel { true }; Transform _currentModel; TransformObjects _objects; + static size_t _objectsMax; BufferCaches _buffers; TextureCaches _textures; @@ -491,6 +480,9 @@ protected: void captureDrawCallInfoImpl(); }; +template +size_t Batch::Cache::_max = BATCH_PREALLOCATE_MIN; + } #if defined(NSIGHT_FOUND) @@ -512,6 +504,4 @@ private: #endif -QDebug& operator<<(QDebug& debug, const gpu::Batch::CacheState& cacheState); - #endif diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 652338f911..47233b2fe3 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -231,11 +231,9 @@ typedef std::shared_ptr ContextPointer; template void doInBatch(std::shared_ptr context, F f) { - static gpu::Batch::CacheState cacheState; - gpu::Batch batch(cacheState); + gpu::Batch batch; f(batch); context->render(batch); - cacheState = batch.getCacheState(); } }; diff --git a/libraries/gpu/src/gpu/Shader.cpp b/libraries/gpu/src/gpu/Shader.cpp index 74b7734618..a044e4845e 100755 --- a/libraries/gpu/src/gpu/Shader.cpp +++ b/libraries/gpu/src/gpu/Shader.cpp @@ -31,6 +31,13 @@ Shader::Shader(Type type, const Pointer& vertex, const Pointer& pixel): _shaders[PIXEL] = pixel; } +Shader::Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel) : +_type(type) { + _shaders.resize(3); + _shaders[VERTEX] = vertex; + _shaders[GEOMETRY] = geometry; + _shaders[PIXEL] = pixel; +} Shader::~Shader() { @@ -44,6 +51,10 @@ Shader::Pointer Shader::createPixel(const Source& source) { return Pointer(new Shader(PIXEL, source)); } +Shader::Pointer Shader::createGeometry(const Source& source) { + return Pointer(new Shader(GEOMETRY, source)); +} + Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& pixelShader) { if (vertexShader && vertexShader->getType() == VERTEX && pixelShader && pixelShader->getType() == PIXEL) { @@ -52,6 +63,15 @@ Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer return Pointer(); } +Shader::Pointer Shader::createProgram(const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader) { + if (vertexShader && vertexShader->getType() == VERTEX && + geometryShader && geometryShader->getType() == GEOMETRY && + pixelShader && pixelShader->getType() == PIXEL) { + return Pointer(new Shader(PROGRAM, vertexShader, geometryShader, pixelShader)); + } + return Pointer(); +} + void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) { _uniforms = uniforms; _buffers = buffers; diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index e4643f2b7c..9072bf23a9 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -110,8 +110,10 @@ public: static Pointer createVertex(const Source& source); static Pointer createPixel(const Source& source); + static Pointer createGeometry(const Source& source); static Pointer createProgram(const Pointer& vertexShader, const Pointer& pixelShader); + static Pointer createProgram(const Pointer& vertexShader, const Pointer& geometryShader, const Pointer& pixelShader); ~Shader(); @@ -163,6 +165,7 @@ public: protected: Shader(Type type, const Source& source); Shader(Type type, const Pointer& vertex, const Pointer& pixel); + Shader(Type type, const Pointer& vertex, const Pointer& geometry, const Pointer& pixel); Shader(const Shader& shader); // deep copy of the sysmem shader Shader& operator=(const Shader& shader); // deep copy of the sysmem texture diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 32c28af2ef..acd7a20327 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -13,11 +13,13 @@ #include #include "KeyboardMouseDevice.h" +#include "TouchscreenDevice.h" // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), + new TouchscreenDevice(), nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp old mode 100644 new mode 100755 index 915ec1db87..56894efc4c --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -19,6 +19,7 @@ #include const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; +bool KeyboardMouseDevice::_enableTouch = true; void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); @@ -79,7 +80,7 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event) { // if we pressed and released at the same location within a small time window, then create a "_CLICKED" // input for this button we might want to add some small tolerance to this so if you do a small drag it - // till counts as a clicked. + // still counts as a click. static const int CLICK_TIME = USECS_PER_MSEC * 500; // 500 ms to click if (!_mouseMoved && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); @@ -98,7 +99,7 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) { _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); - // Y mouse is inverted positive is pointing up the screen + // Y mouse is inverted positive is pointing up the screen _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); @@ -132,34 +133,40 @@ glm::vec2 evalAverageTouchPoints(const QList& points) { } void KeyboardMouseDevice::touchBeginEvent(const QTouchEvent* event) { - _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); - _lastTouch = evalAverageTouchPoints(event->touchPoints()); - _lastTouchTime = _clock.now(); + if (_enableTouch) { + _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); + _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); + } } void KeyboardMouseDevice::touchEndEvent(const QTouchEvent* event) { - _isTouching = false; - _lastTouch = evalAverageTouchPoints(event->touchPoints()); - _lastTouchTime = _clock.now(); + if (_enableTouch) { + _isTouching = false; + _lastTouch = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); + } } void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { - auto currentPos = evalAverageTouchPoints(event->touchPoints()); - _lastTouchTime = _clock.now(); - - if (!_isTouching) { - _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); - } else { - auto currentMove = currentPos - _lastTouch; - - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); - // Y mouse is inverted positive is pointing up the screen - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); - _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); - } + if (_enableTouch) { + auto currentPos = evalAverageTouchPoints(event->touchPoints()); + _lastTouchTime = _clock.now(); - _lastTouch = currentPos; + if (!_isTouching) { + _isTouching = event->touchPointStates().testFlag(Qt::TouchPointPressed); + } else { + auto currentMove = currentPos - _lastTouch; + + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + // Y mouse is inverted positive is pointing up the screen + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + } + + _lastTouch = currentPos; + } } controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::Key code) const { @@ -247,4 +254,3 @@ QString KeyboardMouseDevice::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; return MAPPING_JSON; } - diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index a66cc7060b..2fdecf0bba 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -84,6 +84,8 @@ public: void touchUpdateEvent(const QTouchEvent* event); void wheelEvent(QWheelEvent* event); + + static void enableTouch(bool enableTouch) { _enableTouch = enableTouch; } static const QString NAME; @@ -122,6 +124,8 @@ protected: bool _isTouching = false; std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; + + static bool _enableTouch; }; #endif // hifi_KeyboardMouseDevice_h diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp new file mode 100644 index 0000000000..64f02b5df3 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.cpp @@ -0,0 +1,132 @@ +// +// TouchscreenDevice.cpp +// input-plugins/src/input-plugins +// +// Created by Triplelexx on 01/31/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "TouchscreenDevice.h" +#include "KeyboardMouseDevice.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +const QString TouchscreenDevice::NAME = "Touchscreen"; + +bool TouchscreenDevice::isSupported() const { + for (auto touchDevice : QTouchDevice::devices()) { + if (touchDevice->type() == QTouchDevice::TouchScreen) { + return true; + } + } + return false; +} + +void TouchscreenDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->update(deltaTime, inputCalibrationData); + }); + + float distanceScaleX, distanceScaleY; + if (_touchPointCount == 1) { + if (_firstTouchVec.x < _currentTouchVec.x) { + distanceScaleX = (_currentTouchVec.x - _firstTouchVec.x) / _screenDPIScale.x; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = distanceScaleX; + } else if (_firstTouchVec.x > _currentTouchVec.x) { + distanceScaleX = (_firstTouchVec.x - _currentTouchVec.x) / _screenDPIScale.x; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = distanceScaleX; + } + // Y axis is inverted, positive is pointing up the screen + if (_firstTouchVec.y > _currentTouchVec.y) { + distanceScaleY = (_firstTouchVec.y - _currentTouchVec.y) / _screenDPIScale.y; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = distanceScaleY; + } else if (_firstTouchVec.y < _currentTouchVec.y) { + distanceScaleY = (_currentTouchVec.y - _firstTouchVec.y) / _screenDPIScale.y; + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = distanceScaleY; + } + } else if (_touchPointCount == 2) { + if (_pinchScale > _lastPinchScale && _pinchScale != 0) { + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_POS).getChannel()] = 1.0f; + } else if (_pinchScale != 0) { + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_GESTURE_PINCH_NEG).getChannel()] = 1.0f; + } + _lastPinchScale = _pinchScale; + } +} + +void TouchscreenDevice::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + _axisStateMap.clear(); +} + +void TouchscreenDevice::InputDevice::focusOutEvent() { +} + +void TouchscreenDevice::touchBeginEvent(const QTouchEvent* event) { + const QTouchEvent::TouchPoint& point = event->touchPoints().at(0); + _firstTouchVec = glm::vec2(point.pos().x(), point.pos().y()); + KeyboardMouseDevice::enableTouch(false); + QScreen* eventScreen = event->window()->screen(); + if (_screenDPI != eventScreen->physicalDotsPerInch()) { + _screenDPIScale.x = (float)eventScreen->physicalDotsPerInchX(); + _screenDPIScale.y = (float)eventScreen->physicalDotsPerInchY(); + _screenDPI = eventScreen->physicalDotsPerInch(); + } +} + +void TouchscreenDevice::touchEndEvent(const QTouchEvent* event) { + _touchPointCount = 0; + KeyboardMouseDevice::enableTouch(true); +} + +void TouchscreenDevice::touchUpdateEvent(const QTouchEvent* event) { + const QTouchEvent::TouchPoint& point = event->touchPoints().at(0); + _currentTouchVec = glm::vec2(point.pos().x(), point.pos().y()); + _touchPointCount = event->touchPoints().count(); +} + +void TouchscreenDevice::touchGestureEvent(const QGestureEvent* event) { + if (QGesture* gesture = event->gesture(Qt::PinchGesture)) { + QPinchGesture* pinch = static_cast(gesture); + _pinchScale = pinch->totalScaleFactor(); + } +} + +controller::Input TouchscreenDevice::InputDevice::makeInput(TouchscreenDevice::TouchAxisChannel axis) const { + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); +} + +controller::Input TouchscreenDevice::InputDevice::makeInput(TouchscreenDevice::TouchGestureAxisChannel gesture) const { + return controller::Input(_deviceID, gesture, controller::ChannelType::AXIS); +} + +controller::Input::NamedVector TouchscreenDevice::InputDevice::getAvailableInputs() const { + using namespace controller; + static QVector availableInputs; + static std::once_flag once; + std::call_once(once, [&] { + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "DragRight")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "DragLeft")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "DragUp")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "DragDown")); + + availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_POS), "GesturePinchOut")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_GESTURE_PINCH_NEG), "GesturePinchIn")); + }); + return availableInputs; +} + +QString TouchscreenDevice::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/touchscreen.json"; + return MAPPING_JSON; +} diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h new file mode 100644 index 0000000000..f89f247ce8 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/TouchscreenDevice.h @@ -0,0 +1,84 @@ +// +// TouchscreenDevice.h +// input-plugins/src/input-plugins +// +// Created by Triplelexx on 1/31/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_TouchscreenDevice_h +#define hifi_TouchscreenDevice_h + +#include +#include "InputPlugin.h" +#include + +class QTouchEvent; +class QGestureEvent; + +class TouchscreenDevice : public InputPlugin { + Q_OBJECT +public: + + enum TouchAxisChannel { + TOUCH_AXIS_X_POS = 0, + TOUCH_AXIS_X_NEG, + TOUCH_AXIS_Y_POS, + TOUCH_AXIS_Y_NEG, + }; + + enum TouchGestureAxisChannel { + TOUCH_GESTURE_PINCH_POS = TOUCH_AXIS_Y_NEG + 1, + TOUCH_GESTURE_PINCH_NEG, + }; + + // Plugin functions + virtual bool isSupported() const override; + virtual const QString& getName() const override { return NAME; } + + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + + void touchBeginEvent(const QTouchEvent* event); + void touchEndEvent(const QTouchEvent* event); + void touchUpdateEvent(const QTouchEvent* event); + void touchGestureEvent(const QGestureEvent* event); + + static const QString NAME; + +protected: + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Touchscreen") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + virtual void focusOutEvent() override; + + controller::Input makeInput(TouchAxisChannel axis) const; + controller::Input makeInput(TouchGestureAxisChannel gesture) const; + + friend class TouchscreenDevice; + }; + +public: + const std::shared_ptr& getInputDevice() const { return _inputDevice; } + +protected: + qreal _lastPinchScale; + qreal _pinchScale; + qreal _screenDPI; + glm::vec2 _screenDPIScale; + glm::vec2 _firstTouchVec; + glm::vec2 _currentTouchVec; + int _touchPointCount; + std::shared_ptr _inputDevice { std::make_shared() }; +}; + +#endif // hifi_TouchscreenDevice_h diff --git a/libraries/model/src/model/skybox.slv b/libraries/model/src/model/skybox.slv index 810afb1033..5df1aa0a4a 100755 --- a/libraries/model/src/model/skybox.slv +++ b/libraries/model/src/model/skybox.slv @@ -36,4 +36,4 @@ void main(void) { // Position is supposed to come in clip space gl_Position = vec4(inPosition.xy, 0.0, 1.0); -} \ No newline at end of file +} diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index c74b10820d..6359ad0aff 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH; + return VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS; case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index e484a06502..9140cf8738 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -181,6 +181,7 @@ const PacketVersion VERSION_ENTITIES_NO_FLY_ZONES = 58; const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59; const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61; +const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 3afc170a8c..f71711eccd 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -204,7 +204,7 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { if (numIndices < INT16_MAX) { int16_t* indices = static_cast((void*)(mesh.m_triangleIndexBase)); for (int32_t i = 0; i < numIndices; ++i) { - indices[i] = triangleIndices[i]; + indices[i] = (int16_t)triangleIndices[i]; } } else { int32_t* indices = static_cast((void*)(mesh.m_triangleIndexBase)); @@ -257,7 +257,9 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { shape = new btCapsuleShape(radius, height); } break; - case SHAPE_TYPE_COMPOUND: { + case SHAPE_TYPE_COMPOUND: + case SHAPE_TYPE_SIMPLE_HULL: + case SHAPE_TYPE_SIMPLE_COMPOUND: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); uint32_t numSubShapes = info.getNumSubShapes(); if (numSubShapes == 1) { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 9ea4bd9905..fd06272fa9 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -11,10 +11,12 @@ #include "GeometryCache.h" + #include -#include -#include +#include +#include +#include #include #include @@ -33,6 +35,9 @@ #include "simple_vert.h" #include "simple_textured_frag.h" #include "simple_textured_unlit_frag.h" +#include "glowLine_vert.h" +#include "glowLine_geom.h" +#include "glowLine_frag.h" #include "grid_frag.h" @@ -43,9 +48,9 @@ const int GeometryCache::UNKNOWN_ID = -1; static const int VERTICES_PER_TRIANGLE = 3; -static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; -static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; -static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; +static const gpu::Element POSITION_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; +static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; +static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; @@ -190,7 +195,7 @@ void setupFlatShape(GeometryCache::ShapeData& shapeData, const geometry::Solid void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid& shape, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) { - using namespace geometry; + using namespace geometry; Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE); VertexVector vertices; @@ -254,7 +259,7 @@ void GeometryCache::buildShapes() { setupFlatShape(_shapes[Octahedron], geometry::octahedron(), _shapeVertices, _shapeIndices); // Dodecahedron setupFlatShape(_shapes[Dodecahedron], geometry::dodecahedron(), _shapeVertices, _shapeIndices); - + // Sphere // FIXME this uses way more vertices than required. Should find a way to calculate the indices // using shared vertices for better vertex caching @@ -266,7 +271,7 @@ void GeometryCache::buildShapes() { { Index baseVertex = (Index)(_shapeVertices->getSize() / SHAPE_VERTEX_STRIDE); ShapeData& shapeData = _shapes[Line]; - shapeData.setupVertices(_shapeVertices, VertexVector{ + shapeData.setupVertices(_shapeVertices, VertexVector { vec3(-0.5, 0, 0), vec3(-0.5f, 0, 0), vec3(0.5f, 0, 0), vec3(0.5f, 0, 0) }); @@ -312,32 +317,31 @@ render::ShapePipelinePointer GeometryCache::_simplePipeline; render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; GeometryCache::GeometryCache() : - _nextID(0) -{ +_nextID(0) { buildShapes(); GeometryCache::_simplePipeline = std::make_shared(getSimplePipeline(), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) { - // Set the defaults needed for a simple program - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, - DependencyManager::get()->getWhiteTexture()); - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); - } - ); + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); GeometryCache::_simpleWirePipeline = std::make_shared(getSimplePipeline(false, false, true, true), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) { } - ); + [](const render::ShapePipeline&, gpu::Batch& batch) {} + ); } GeometryCache::~GeometryCache() { - #ifdef WANT_DEBUG - qCDebug(renderutils) << "GeometryCache::~GeometryCache()... "; - qCDebug(renderutils) << " _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); - qCDebug(renderutils) << " _line3DVBOs.size():" << _line3DVBOs.size(); - qCDebug(renderutils) << " BatchItemDetails... population:" << GeometryCache::BatchItemDetails::population; - #endif //def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "GeometryCache::~GeometryCache()... "; + qCDebug(renderutils) << " _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); + qCDebug(renderutils) << " _line3DVBOs.size():" << _line3DVBOs.size(); + qCDebug(renderutils) << " BatchItemDetails... population:" << GeometryCache::BatchItemDetails::population; +#endif //def WANT_DEBUG } void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { @@ -384,9 +388,9 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch) { } void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, - int majorRows, int majorCols, float majorEdge, - int minorRows, int minorCols, float minorEdge, - const glm::vec4& color, bool isLayered, int id) { + int majorRows, int majorCols, float majorEdge, + int minorRows, int minorCols, float minorEdge, + const glm::vec4& color, bool isLayered, int id) { static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f); static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f); @@ -433,9 +437,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con if (details.isCreated) { details.clear(); - #ifdef WANT_DEBUG - qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals @@ -444,7 +448,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -466,9 +470,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.vertexSize = FLOATS_PER_VERTEX; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; @@ -477,13 +481,13 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorDataAt = colorData; const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); - foreach (const glm::vec2& point, points) { + foreach(const glm::vec2& point, points) { *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = NORMAL.x; *(vertex++) = NORMAL.y; *(vertex++) = NORMAL.z; - + *(colorDataAt++) = compactColor; } @@ -492,18 +496,18 @@ void GeometryCache::updateVertices(int id, const QVector& points, con delete[] vertexData; delete[] colorData; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); - #endif +#ifdef WANT_DEBUG + qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); +#endif } void GeometryCache::updateVertices(int id, const QVector& points, const glm::vec4& color) { BatchItemDetails& details = _registeredVertices[id]; if (details.isCreated) { details.clear(); - #ifdef WANT_DEBUG - qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals @@ -512,7 +516,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -534,9 +538,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.vertexSize = FLOATS_PER_VERTEX; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; @@ -545,14 +549,14 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorDataAt = colorData; const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); - foreach (const glm::vec3& point, points) { + foreach(const glm::vec3& point, points) { *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; *(vertex++) = NORMAL.x; *(vertex++) = NORMAL.y; *(vertex++) = NORMAL.z; - + *(colorDataAt++) = compactColor; } @@ -561,9 +565,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con delete[] vertexData; delete[] colorData; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); - #endif +#ifdef WANT_DEBUG + qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); +#endif } void GeometryCache::updateVertices(int id, const QVector& points, const QVector& texCoords, const glm::vec4& color) { @@ -571,9 +575,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con if (details.isCreated) { details.clear(); - #ifdef WANT_DEBUG - qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "updateVertices()... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords @@ -584,7 +588,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -609,9 +613,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con details.vertexSize = FLOATS_PER_VERTEX; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; @@ -640,9 +644,9 @@ void GeometryCache::updateVertices(int id, const QVector& points, con delete[] vertexData; delete[] colorData; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); - #endif +#ifdef WANT_DEBUG + qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); +#endif } void GeometryCache::renderVertices(gpu::Batch& batch, gpu::Primitive primitiveType, int id) { @@ -664,27 +668,27 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int Vec3Pair& lastKey = _lastRegisteredBevelRects[id]; if (lastKey != key) { details.clear(); - _lastRegisteredBevelRects[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderBevelCornersRect()... RELEASING REGISTERED"; - #endif // def WANT_DEBUG + _lastRegisteredBevelRects[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderBevelCornersRect()... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderBevelCornersRect()... REUSING PREVIOUSLY REGISTERED"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } if (!details.isCreated) { static const int FLOATS_PER_VERTEX = 2; // vertices static const int NUM_VERTICES = 8; static const int NUM_FLOATS = NUM_VERTICES * FLOATS_PER_VERTEX; - + details.isCreated = true; details.vertices = NUM_VERTICES; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -694,7 +698,7 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); @@ -713,7 +717,7 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int // 2 8 // // \ / // // 4 ------ 6 // - + // 1 vertexBuffer[vertexPoint++] = x; vertexBuffer[vertexPoint++] = y + height - bevelDistance; @@ -738,13 +742,13 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int // 8 vertexBuffer[vertexPoint++] = x + width; vertexBuffer[vertexPoint++] = y + bevelDistance; - + int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_VERTICES] = { compactColor, compactColor, compactColor, compactColor, - compactColor, compactColor, compactColor, compactColor }; + compactColor, compactColor, compactColor, compactColor }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); @@ -766,16 +770,16 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co Vec4Pair & lastKey = _lastRegisteredQuad2D[id]; if (lastKey != key) { details.clear(); - _lastRegisteredQuad2D[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderQuad() 2D ... RELEASING REGISTERED"; - #endif // def WANT_DEBUG + _lastRegisteredQuad2D[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderQuad() 2D ... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 2D ... REUSING PREVIOUSLY REGISTERED"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals @@ -788,7 +792,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -798,7 +802,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); @@ -808,7 +812,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); - float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, @@ -817,9 +821,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); @@ -841,13 +845,13 @@ void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, in void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, - const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, - const glm::vec4& color, int id) { + const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, + const glm::vec4& color, int id) { bool registered = (id != UNKNOWN_ID); Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), - glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)), - color); + glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)), + color); BatchItemDetails& details = registered ? _registeredQuad2DTextures[id] : _quad2DTextures[key]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed @@ -855,16 +859,16 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co Vec4PairVec4& lastKey = _lastRegisteredQuad2DTexture[id]; if (lastKey != key) { details.clear(); - _lastRegisteredQuad2DTexture[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderQuad() 2D+texture ... RELEASING REGISTERED"; - #endif // def WANT_DEBUG + _lastRegisteredQuad2DTexture[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderQuad() 2D+texture ... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 2D+texture ... REUSING PREVIOUSLY REGISTERED"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 2 + 3 + 2; // vertices + normals + tex coords @@ -879,7 +883,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); @@ -891,7 +895,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co details.streamFormat = streamFormat; details.stream = stream; - + // zzmp: fix the normal across all renderQuad details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); @@ -903,7 +907,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); - float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMinCorner.y, maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMinCorner.y, minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMaxCorner.y, @@ -913,9 +917,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); @@ -937,16 +941,16 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co Vec3PairVec4& lastKey = _lastRegisteredQuad3D[id]; if (lastKey != key) { details.clear(); - _lastRegisteredQuad3D[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderQuad() 3D ... RELEASING REGISTERED"; - #endif // def WANT_DEBUG + _lastRegisteredQuad3D[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderQuad() 3D ... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 3D ... REUSING PREVIOUSLY REGISTERED"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals @@ -959,7 +963,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); @@ -971,7 +975,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); @@ -981,7 +985,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); - float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { + float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { minCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, maxCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, minCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, @@ -990,9 +994,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); @@ -1004,28 +1008,28 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } -void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, - const glm::vec3& bottomRight, const glm::vec3& topRight, - const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, - const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, - const glm::vec4& color, int id) { +void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, const glm::vec3& bottomLeft, + const glm::vec3& bottomRight, const glm::vec3& topRight, + const glm::vec2& texCoordTopLeft, const glm::vec2& texCoordBottomLeft, + const glm::vec2& texCoordBottomRight, const glm::vec2& texCoordTopRight, + const glm::vec4& color, int id) { + +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderQuad() vec3 + texture VBO..."; + qCDebug(renderutils) << " topLeft:" << topLeft; + qCDebug(renderutils) << " bottomLeft:" << bottomLeft; + qCDebug(renderutils) << " bottomRight:" << bottomRight; + qCDebug(renderutils) << " topRight:" << topRight; + qCDebug(renderutils) << " texCoordTopLeft:" << texCoordTopLeft; + qCDebug(renderutils) << " texCoordBottomRight:" << texCoordBottomRight; + qCDebug(renderutils) << " color:" << color; +#endif //def WANT_DEBUG - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderQuad() vec3 + texture VBO..."; - qCDebug(renderutils) << " topLeft:" << topLeft; - qCDebug(renderutils) << " bottomLeft:" << bottomLeft; - qCDebug(renderutils) << " bottomRight:" << bottomRight; - qCDebug(renderutils) << " topRight:" << topRight; - qCDebug(renderutils) << " texCoordTopLeft:" << texCoordTopLeft; - qCDebug(renderutils) << " texCoordBottomRight:" << texCoordBottomRight; - qCDebug(renderutils) << " color:" << color; - #endif //def WANT_DEBUG - bool registered = (id != UNKNOWN_ID); Vec3PairVec4Pair key(Vec3Pair(topLeft, bottomRight), - Vec4Pair(glm::vec4(texCoordTopLeft.x,texCoordTopLeft.y,texCoordBottomRight.x,texCoordBottomRight.y), - color)); - + Vec4Pair(glm::vec4(texCoordTopLeft.x, texCoordTopLeft.y, texCoordBottomRight.x, texCoordBottomRight.y), + color)); + BatchItemDetails& details = registered ? _registeredQuad3DTextures[id] : _quad3DTextures[key]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed @@ -1034,15 +1038,15 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons if (lastKey != key) { details.clear(); _lastRegisteredQuad3DTexture[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderQuad() 3D+texture ... RELEASING REGISTERED"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderQuad() 3D+texture ... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderQuad() 3D+texture ... REUSING PREVIOUSLY REGISTERED"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords @@ -1058,7 +1062,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -1068,7 +1072,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); @@ -1088,9 +1092,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons const int NUM_COLOR_SCALARS_PER_QUAD = 4; int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); @@ -1103,7 +1107,7 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons } void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, - const float dash_length, const float gap_length, int id) { + const float dash_length, const float gap_length, int id) { bool registered = (id != UNKNOWN_ID); Vec3PairVec2Pair key(Vec3Pair(start, end), Vec2Pair(glm::vec2(color.x, color.y), glm::vec2(color.z, color.w))); @@ -1114,18 +1118,18 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, if (_lastRegisteredDashedLines[id] != key) { details.clear(); _lastRegisteredDashedLines[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderDashedLine()... RELEASING REGISTERED"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderDashedLine()... RELEASING REGISTERED"; +#endif // def WANT_DEBUG } } if (!details.isCreated) { int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); // draw each line segment with appropriate gaps const float SEGMENT_LENGTH = dash_length + gap_length; @@ -1143,7 +1147,7 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, details.vertices = (segmentCountFloor + 1) * 2; details.vertexSize = FLOATS_PER_VERTEX; details.isCreated = true; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -1209,13 +1213,13 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, delete[] vertexData; delete[] colorData; - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG if (registered) { qCDebug(renderutils) << "new registered dashed line buffer made -- _registeredVertices:" << _registeredDashedLines.size(); } else { qCDebug(renderutils) << "new dashed lines buffer made -- _dashedLines:" << _dashedLines.size(); } - #endif +#endif } batch.setInputFormat(details.streamFormat); @@ -1227,41 +1231,39 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, int GeometryCache::BatchItemDetails::population = 0; GeometryCache::BatchItemDetails::BatchItemDetails() : - verticesBuffer(NULL), - colorBuffer(NULL), - streamFormat(NULL), - stream(NULL), - vertices(0), - vertexSize(0), - isCreated(false) -{ +verticesBuffer(NULL), +colorBuffer(NULL), +streamFormat(NULL), +stream(NULL), +vertices(0), +vertexSize(0), +isCreated(false) { population++; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; - #endif +#ifdef WANT_DEBUG + qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; +#endif } GeometryCache::BatchItemDetails::BatchItemDetails(const GeometryCache::BatchItemDetails& other) : - verticesBuffer(other.verticesBuffer), - colorBuffer(other.colorBuffer), - streamFormat(other.streamFormat), - stream(other.stream), - vertices(other.vertices), - vertexSize(other.vertexSize), - isCreated(other.isCreated) -{ +verticesBuffer(other.verticesBuffer), +colorBuffer(other.colorBuffer), +streamFormat(other.streamFormat), +stream(other.stream), +vertices(other.vertices), +vertexSize(other.vertexSize), +isCreated(other.isCreated) { population++; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; - #endif +#ifdef WANT_DEBUG + qCDebug(renderutils) << "BatchItemDetails()... population:" << population << "**********************************"; +#endif } GeometryCache::BatchItemDetails::~BatchItemDetails() { population--; - clear(); - #ifdef WANT_DEBUG - qCDebug(renderutils) << "~BatchItemDetails()... population:" << population << "**********************************"; - #endif + clear(); +#ifdef WANT_DEBUG + qCDebug(renderutils) << "~BatchItemDetails()... population:" << population << "**********************************"; +#endif } void GeometryCache::BatchItemDetails::clear() { @@ -1272,23 +1274,23 @@ void GeometryCache::BatchItemDetails::clear() { stream.reset(); } -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2, int id) { + bool registered = (id != UNKNOWN_ID); Vec3Pair key(p1, p2); BatchItemDetails& details = registered ? _registeredLine3DVBOs[id] : _line3DVBOs[key]; int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); + ((int(color1.y * 255.0f) & 0xFF) << 8) | + ((int(color1.z * 255.0f) & 0xFF) << 16) | + ((int(color1.w * 255.0f) & 0xFF) << 24); int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); + ((int(color2.y * 255.0f) & 0xFF) << 8) | + ((int(color2.z * 255.0f) & 0xFF) << 16) | + ((int(color2.w * 255.0f) & 0xFF) << 24); // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed @@ -1296,16 +1298,16 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm Vec3Pair& lastKey = _lastRegisteredLine3D[id]; if (lastKey != key) { details.clear(); - _lastRegisteredLine3D[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; - #endif // def WANT_DEBUG + _lastRegisteredLine3D[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals @@ -1317,7 +1319,7 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm details.isCreated = true; details.vertices = vertices; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -1327,7 +1329,7 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); @@ -1338,7 +1340,7 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z, - p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z}; + p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z }; const int NUM_COLOR_SCALARS = 2; int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; @@ -1346,13 +1348,13 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - #ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); - } - #endif +#ifdef WANT_DEBUG + if (id == UNKNOWN_ID) { + qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); + } else { + qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); + } +#endif } // this is what it takes to render a quad @@ -1361,23 +1363,23 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm batch.draw(gpu::LINES, 2, 0); } -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, + const glm::vec4& color1, const glm::vec4& color2, int id) { + bool registered = (id != UNKNOWN_ID); Vec2Pair key(p1, p2); BatchItemDetails& details = registered ? _registeredLine2DVBOs[id] : _line2DVBOs[key]; int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); + ((int(color1.y * 255.0f) & 0xFF) << 8) | + ((int(color1.z * 255.0f) & 0xFF) << 16) | + ((int(color1.w * 255.0f) & 0xFF) << 24); int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); + ((int(color2.y * 255.0f) & 0xFF) << 8) | + ((int(color2.z * 255.0f) & 0xFF) << 16) | + ((int(color2.w * 255.0f) & 0xFF) << 24); // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed @@ -1386,15 +1388,15 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm if (lastKey != key) { details.clear(); _lastRegisteredLine2D[id] = key; - #ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 2D ... RELEASING REGISTERED line"; - #endif // def WANT_DEBUG +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderLine() 2D ... RELEASING REGISTERED line"; +#endif // def WANT_DEBUG } - #ifdef WANT_DEBUG +#ifdef WANT_DEBUG else { qCDebug(renderutils) << "renderLine() 2D ... REUSING PREVIOUSLY REGISTERED line"; } - #endif // def WANT_DEBUG +#endif // def WANT_DEBUG } const int FLOATS_PER_VERTEX = 2; @@ -1404,7 +1406,7 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm details.isCreated = true; details.vertices = vertices; details.vertexSize = FLOATS_PER_VERTEX; - + auto verticesBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); @@ -1414,7 +1416,7 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); @@ -1430,13 +1432,103 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - #ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); - } - #endif +#ifdef WANT_DEBUG + if (id == UNKNOWN_ID) { + qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); + } else { + qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); + } +#endif + } + + // this is what it takes to render a quad + batch.setInputFormat(details.streamFormat); + batch.setInputStream(0, *details.stream); + batch.draw(gpu::LINES, 2, 0); +} + + +void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color, float glowIntensity, float glowWidth, int id) { + if (glowIntensity <= 0) { + renderLine(batch, p1, p2, color, id); + return; + } + + // Compile the shaders + static std::once_flag once; + std::call_once(once, [&] { + auto state = std::make_shared(); + auto VS = gpu::Shader::createVertex(std::string(glowLine_vert)); + auto GS = gpu::Shader::createGeometry(std::string(glowLine_geom)); + auto PS = gpu::Shader::createPixel(std::string(glowLine_frag)); + auto program = gpu::Shader::createProgram(VS, GS, PS); + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); + gpu::Shader::makeProgram(*program, slotBindings); + _glowLinePipeline = gpu::Pipeline::create(program, state); + }); + + batch.setPipeline(_glowLinePipeline); + + Vec3Pair key(p1, p2); + bool registered = (id != UNKNOWN_ID); + BatchItemDetails& details = registered ? _registeredLine3DVBOs[id] : _line3DVBOs[key]; + + int compactColor = ((int(color.x * 255.0f) & 0xFF)) | + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); + + // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed + if (registered && details.isCreated) { + Vec3Pair& lastKey = _lastRegisteredLine3D[id]; + if (lastKey != key) { + details.clear(); + _lastRegisteredLine3D[id] = key; + } + } + + const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals + const int NUM_POS_COORDS = 3; + const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int vertices = 2; + if (!details.isCreated) { + details.isCreated = true; + details.vertices = vertices; + details.vertexSize = FLOATS_PER_VERTEX; + + auto verticesBuffer = std::make_shared(); + auto colorBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); + auto stream = std::make_shared(); + + details.verticesBuffer = verticesBuffer; + details.colorBuffer = colorBuffer; + details.streamFormat = streamFormat; + details.stream = stream; + + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + + const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); + float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z, + p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z }; + + const int NUM_COLOR_SCALARS = 2; + int colors[NUM_COLOR_SCALARS] = { compactColor, compactColor }; + details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); + details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); } // this is what it takes to render a quad @@ -1532,7 +1624,7 @@ public: SimpleProgramKey(bool textured = false, bool culled = true, - bool unlit = false, bool depthBias = false) { + bool unlit = false, bool depthBias = false) { _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0); } @@ -1562,7 +1654,7 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool cul } gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) { - SimpleProgramKey config{ textured, culled, unlit, depthBiased }; + SimpleProgramKey config { textured, culled, unlit, depthBiased }; // Compile the shaders static std::once_flag once; @@ -1570,10 +1662,10 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled auto VS = gpu::Shader::createVertex(std::string(simple_vert)); auto PS = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto PSUnlit = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); - + _simpleShader = gpu::Shader::createProgram(VS, PS); _unlitShader = gpu::Shader::createProgram(VS, PSUnlit); - + gpu::Shader::BindingSet slotBindings; slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING)); gpu::Shader::makeProgram(*_simpleShader, slotBindings); @@ -1610,16 +1702,16 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled uint32_t toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor; } static const size_t INSTANCE_COLOR_BUFFER = 0; void renderInstances(gpu::Batch& batch, const glm::vec4& color, bool isWire, - const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { + const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash()(pipeline)); @@ -1663,7 +1755,7 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& #ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - + auto usecs = usecTimestampNow(); usecs -= startTime; auto msecs = usecs / USECS_PER_MSEC; @@ -1671,7 +1763,7 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& seconds /= MSECS_PER_SECOND; float fractionalSeconds = seconds - floor(seconds); int shapeIndex = (int)seconds; - + // Every second we flip to the next shape. static const int SHAPE_COUNT = 5; GeometryCache::Shape shapes[SHAPE_COUNT] = { @@ -1681,10 +1773,10 @@ void GeometryCache::renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& GeometryCache::Icosahedron, GeometryCache::Line, }; - + shapeIndex %= SHAPE_COUNT; GeometryCache::Shape shape = shapes[shapeIndex]; - + // For the first half second for a given shape, show the wireframe, for the second half, show the solid. if (fractionalSeconds > 0.5f) { renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 9f18f1644c..647fa9889c 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -259,6 +259,9 @@ public: void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID); + void renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color, float glowIntensity = 1.0f, float glowWidth = 0.05f, int id = UNKNOWN_ID); + void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id = UNKNOWN_ID) { renderDashedLine(batch, start, end, color, 0.05f, 0.025f, id); } @@ -403,6 +406,7 @@ private: gpu::ShaderPointer _unlitShader; static render::ShapePipelinePointer _simplePipeline; static render::ShapePipelinePointer _simpleWirePipeline; + gpu::PipelinePointer _glowLinePipeline; QHash _simplePrograms; }; diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf new file mode 100644 index 0000000000..edebc99c81 --- /dev/null +++ b/libraries/render-utils/src/glowLine.slf @@ -0,0 +1,35 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Bradley Austin Davis on 2016/07/05 +// Copyright 2013-2016 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 +// + +layout(location = 0) in vec4 inColor; +layout(location = 1) in vec3 inLineDistance; + +out vec4 _fragColor; + +void main(void) { + vec2 d = inLineDistance.xy; + d.y = abs(d.y); + d.x = abs(d.x); + if (d.x > 1.0) { + d.x = (d.x - 1.0) / 0.02; + } else { + d.x = 0.0; + } + float alpha = 1.0 - length(d); + if (alpha <= 0.0) { + discard; + } + alpha = pow(alpha, 10.0); + if (alpha < 0.05) { + discard; + } + _fragColor = vec4(inColor.rgb, alpha); +} diff --git a/libraries/render-utils/src/glowLine.slg b/libraries/render-utils/src/glowLine.slg new file mode 100644 index 0000000000..429cb8ee37 --- /dev/null +++ b/libraries/render-utils/src/glowLine.slg @@ -0,0 +1,106 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Bradley Austin Davis on 2016/07/05 +// Copyright 2013-2016 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 +// +#extension GL_EXT_geometry_shader4 : enable + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> + +layout(location = 0) in vec4 inColor[]; + +layout(location = 0) out vec4 outColor; +layout(location = 1) out vec3 outLineDistance; + +layout(lines) in; +layout(triangle_strip, max_vertices = 24) out; + +vec3 ndcToEyeSpace(in vec4 v) { + TransformCamera cam = getTransformCamera(); + vec4 u = cam._projectionInverse * v; + return u.xyz / u.w; +} + +vec2 toScreenSpace(in vec4 v) +{ + TransformCamera cam = getTransformCamera(); + vec4 u = cam._projection * cam._view * v; + return u.xy / u.w; +} + +vec3[2] getOrthogonals(in vec3 n, float scale) { + float yDot = abs(dot(n, vec3(0, 1, 0))); + + vec3 result[2]; + if (yDot < 0.9) { + result[0] = normalize(cross(n, vec3(0, 1, 0))); + } else { + result[0] = normalize(cross(n, vec3(1, 0, 0))); + } + // The cross of result[0] and n is orthogonal to both, which are orthogonal to each other + result[1] = cross(result[0], n); + result[0] *= scale; + result[1] *= scale; + return result; +} + + +vec2 orthogonal(vec2 v) { + vec2 result = v.yx; + result.y *= -1.0; + return result; +} + +void main() { + vec2 endpoints[2]; + vec3 eyeSpace[2]; + TransformCamera cam = getTransformCamera(); + for (int i = 0; i < 2; ++i) { + eyeSpace[i] = ndcToEyeSpace(gl_PositionIn[i]); + endpoints[i] = gl_PositionIn[i].xy / gl_PositionIn[i].w; + } + vec2 lineNormal = normalize(endpoints[1] - endpoints[0]); + vec2 lineOrthogonal = orthogonal(lineNormal); + lineNormal *= 0.02; + lineOrthogonal *= 0.02; + + gl_Position = gl_PositionIn[0]; + gl_Position.xy -= lineNormal; + gl_Position.xy -= lineOrthogonal; + outColor = inColor[0]; + outLineDistance = vec3(-1.02, -1, gl_Position.z); + EmitVertex(); + + gl_Position = gl_PositionIn[0]; + gl_Position.xy -= lineNormal; + gl_Position.xy += lineOrthogonal; + outColor = inColor[0]; + outLineDistance = vec3(-1.02, 1, gl_Position.z); + EmitVertex(); + + gl_Position = gl_PositionIn[1]; + gl_Position.xy += lineNormal; + gl_Position.xy -= lineOrthogonal; + outColor = inColor[1]; + outLineDistance = vec3(1.02, -1, gl_Position.z); + EmitVertex(); + + gl_Position = gl_PositionIn[1]; + gl_Position.xy += lineNormal; + gl_Position.xy += lineOrthogonal; + outColor = inColor[1]; + outLineDistance = vec3(1.02, 1, gl_Position.z); + EmitVertex(); + + EndPrimitive(); +} + + + + diff --git a/libraries/render-utils/src/glowLine.slv b/libraries/render-utils/src/glowLine.slv new file mode 100644 index 0000000000..aa126fe31a --- /dev/null +++ b/libraries/render-utils/src/glowLine.slv @@ -0,0 +1,26 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Bradley Austin Davis on 2016/07/05 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +layout(location = 0) out vec4 _color; + +void main(void) { + _color = inColor; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> +} \ No newline at end of file diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index e0f4cc18b2..424c2bfa22 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -83,12 +83,19 @@ void ShapeInfo::setOffset(const glm::vec3& offset) { } uint32_t ShapeInfo::getNumSubShapes() const { - if (_type == SHAPE_TYPE_NONE) { - return 0; - } else if (_type == SHAPE_TYPE_COMPOUND) { - return _pointCollection.size(); + switch (_type) { + case SHAPE_TYPE_NONE: + return 0; + case SHAPE_TYPE_COMPOUND: + case SHAPE_TYPE_SIMPLE_COMPOUND: + return _pointCollection.size(); + case SHAPE_TYPE_SIMPLE_HULL: + case SHAPE_TYPE_STATIC_MESH: + assert(_pointCollection.size() == 1); + // yes fall through to default + default: + return 1; } - return 1; } int ShapeInfo::getLargestSubshapePointCount() const { diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 96132a4b23..7bf145412a 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -39,6 +39,8 @@ enum ShapeType { SHAPE_TYPE_HULL, SHAPE_TYPE_PLANE, SHAPE_TYPE_COMPOUND, + SHAPE_TYPE_SIMPLE_HULL, + SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH }; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 2c032f7005..6da842b7b9 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -68,18 +68,48 @@ bool OculusLegacyDisplayPlugin::isSupported() const { } auto hmd = ovrHmd_Create(0); + + // The Oculus SDK seems to have trouble finding the right screen sometimes, so we have to guess + // Guesses, in order of best match: + // - resolution and position match + // - resolution and one component of position match + // - resolution matches + // - position matches + // If it still picks the wrong screen, you'll have to mess with your monitor configuration + QList matches({ -1, -1, -1, -1 }); if (hmd) { QPoint targetPosition{ hmd->WindowsPos.x, hmd->WindowsPos.y }; + QSize targetResolution{ hmd->Resolution.w, hmd->Resolution.h }; auto screens = qApp->screens(); for(int i = 0; i < screens.size(); ++i) { auto screen = screens[i]; QPoint position = screen->geometry().topLeft(); - if (position == targetPosition) { - _hmdScreen = i; - break; + QSize resolution = screen->geometry().size(); + + if (position == targetPosition && resolution == targetResolution) { + matches[0] = i; + } else if ((position.x() == targetPosition.x() || position.y() == targetPosition.y()) && + resolution == targetResolution) { + matches[1] = i; + } else if (resolution == targetResolution) { + matches[2] = i; + } else if (position == targetPosition) { + matches[3] = i; } } } + + for (int screen : matches) { + if (screen != -1) { + _hmdScreen = screen; + break; + } + } + + if (_hmdScreen == -1) { + qDebug() << "Could not find Rift screen"; + result = false; + } ovr_Shutdown(); return result; diff --git a/scripts/developer/tests/loadedMachine.js b/scripts/developer/tests/loadedMachine.js new file mode 100644 index 0000000000..375e3d8004 --- /dev/null +++ b/scripts/developer/tests/loadedMachine.js @@ -0,0 +1,192 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*globals Script, MyAvatar, Quat, Render, ScriptDiscoveryService, Window, LODManager, Entities, print*/ +// +// loadedMachine.js +// scripts/developer/tests/ +// +// Created by Howard Stearns on 6/6/16. +// Copyright 2016 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 +// +// Characterises the performance of heavily loaded or struggling machines. +// There is no point in running this on a machine that producing the target 60 or 90 hz rendering rate. +// +// The script examines several source of load, including each running script and a couple of Entity types. +// It turns all of these off, as well as LOD management, and twirls in place to get a baseline render rate. +// Then it turns each load on, one at a time, and records a new render rate. +// At the end, it reports the ordered results (in a popup), restores the original loads and LOD management, and quits. +// +// Small performance changes are not meaningful. +// If you think you find something that is significant, you should probably run again to make sure that it isn't random variation. +// You can also compare (e.g., the baseline) for different conditions such as domain, version, server settings, etc. +// This does not profile scripts as they are used -- it merely measures the effect of having the script loaded and running quietly. + +var NUMBER_OF_TWIRLS_PER_LOAD = 10; +var MILLISECONDS_PER_SECOND = 1000; +var WAIT_TIME_MILLISECONDS = MILLISECONDS_PER_SECOND; +var accumulatedRotation = 0; +var start; +var frames = 0; +var config = Render.getConfig("Stats"); +var thisPath = Script.resolvePath(''); +var scriptData = ScriptDiscoveryService.getRunning(); +var scripts = scriptData.filter(function (datum) { return datum.name !== 'defaultScripts.js'; }).map(function (script) { return script.path; }); +// If defaultScripts.js is running, we leave it running, because restarting it safely is a mess. +var otherScripts = scripts.filter(function (path) { return path !== thisPath; }); +var numberLeftRunning = scriptData.length - otherScripts.length; +print('initially running', otherScripts.length, 'scripts. Leaving', numberLeftRunning, 'and stopping', otherScripts); +var typedEntities = {Light: [], ParticleEffect: []}; +var interestingTypes = Object.keys(typedEntities); +var propertiedEntities = {dynamic: []}; +var interestingProperties = Object.keys(propertiedEntities); +var loads = ['ignore', 'baseline'].concat(otherScripts, interestingTypes, interestingProperties); +var loadIndex = 0, nLoads = loads.length, load; +var results = []; +var initialLodIsAutomatic = LODManager.getAutomaticLODAdjust(); +var SEARCH_RADIUS = 17000; +var DEFAULT_LOD = 32768 * 400; +LODManager.setAutomaticLODAdjust(false); +LODManager.setOctreeSizeScale(DEFAULT_LOD); + +// Fill the typedEnties with the entityIDs that are already visible. It would be nice if this were more efficient. +var allEntities = Entities.findEntities(MyAvatar.position, SEARCH_RADIUS); +print('Searching', allEntities.length, 'entities for', interestingTypes, 'and', interestingProperties); +var propertiesToGet = ['type', 'visible'].concat(interestingProperties); +allEntities.forEach(function (entityID) { + var properties = Entities.getEntityProperties(entityID, propertiesToGet); + if (properties.visible) { + if (interestingTypes.indexOf(properties.type) >= 0) { + typedEntities[properties.type].push(entityID); + } else { + interestingProperties.forEach(function (property) { + if (entityID && properties[property]) { + propertiedEntities[property].push(entityID); + entityID = false; // Put in only one bin + } + }); + } + } +}); +allEntities = undefined; // free them +interestingTypes.forEach(function (type) { + print('There are', typedEntities[type].length, type, 'entities.'); +}); +interestingProperties.forEach(function (property) { + print('There are', propertiedEntities[property].length, property, 'entities.'); +}); +function toggleVisibility(type, on) { + typedEntities[type].forEach(function (entityID) { + Entities.editEntity(entityID, {visible: on}); + }); +} +function toggleProperty(property, on) { + propertiedEntities[property].forEach(function (entityID) { + var properties = {}; + properties[property] = on; + Entities.editEntity(entityID, properties); + }); +} +function restoreOneTest(load) { + print('restore', load); + switch (load) { + case 'baseline': + case 'ignore': // ignore is used to do a twirl to make sure everything is loaded. + break; + case 'Light': + case 'ParticleEffect': + toggleVisibility(load, true); + break; + case 'dynamic': + toggleProperty(load, 1); + break; + default: + Script.load(load); + } +} +function undoOneTest(load) { + print('stop', load); + switch (load) { + case 'baseline': + case 'ignore': + break; + case 'Light': + case 'ParticleEffect': + toggleVisibility(load, false); + break; + case 'dynamic': + toggleProperty(load, 0); + break; + default: + ScriptDiscoveryService.stopScript(load); + } +} +loads.forEach(undoOneTest); + +function finalReport() { + var baseline = results[0]; + results.forEach(function (result) { + result.ratio = (baseline.fps / result.fps); + }); + results.sort(function (a, b) { return b.ratio - a.ratio; }); + var report = 'Performance Report:\nBaseline: ' + baseline.fps.toFixed(1) + ' fps.'; + results.forEach(function (result) { + report += '\n' + result.ratio.toFixed(2) + ' (' + result.fps.toFixed(1) + ' fps over ' + result.elapsed + ' seconds) for ' + result.name; + }); + Window.alert(report); + LODManager.setAutomaticLODAdjust(initialLodIsAutomatic); + loads.forEach(restoreOneTest); + Script.stop(); +} + +function onNewStats() { // Accumulates frames on signal during load test + frames++; +} +var DEGREES_PER_FULL_TWIRL = 360; +var DEGREES_PER_UPDATE = 6; +function onUpdate() { // Spins on update signal during load test + MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, accumulatedRotation, 0); + accumulatedRotation += DEGREES_PER_UPDATE; + if (accumulatedRotation >= (DEGREES_PER_FULL_TWIRL * NUMBER_OF_TWIRLS_PER_LOAD)) { + tearDown(); + } +} +function startTest() { + print('start', load); + accumulatedRotation = frames = 0; + start = Date.now(); + Script.update.connect(onUpdate); + config.newStats.connect(onNewStats); +} + +function setup() { + if (loadIndex >= nLoads) { + return finalReport(); + } + load = loads[loadIndex]; + restoreOneTest(load); + Script.setTimeout(startTest, WAIT_TIME_MILLISECONDS); +} +function tearDown() { + var now = Date.now(); + var elapsed = (now - start) / MILLISECONDS_PER_SECOND; + Script.update.disconnect(onUpdate); + config.newStats.disconnect(onNewStats); + if (load !== 'ignore') { + results.push({name: load, fps: frames / elapsed, elapsed: elapsed}); + } + undoOneTest(load); + loadIndex++; + setup(); +} +function waitUntilStopped() { // Wait until all the scripts have stopped + var count = ScriptDiscoveryService.getRunning().length; + if (count === numberLeftRunning) { + return setup(); + } + print('Still', count, 'scripts running'); + Script.setTimeout(waitUntilStopped, WAIT_TIME_MILLISECONDS); +} +waitUntilStopped(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 81bd9cba2e..c8a7f251af 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -26,9 +26,11 @@ var WANT_DEBUG_SEARCH_NAME = null; // these tune time-averaging and "on" value for analog trigger // +var SPARK_MODEL_SCALE_FACTOR = 0.75; + var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab -var TRIGGER_GRAB_VALUE = 0.75; // Squeezed far enough to complete distant grab +var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_OFF_VALUE = 0.15; var BUMPER_ON_VALUE = 0.5; @@ -43,6 +45,10 @@ var DRAW_GRAB_BOXES = false; var DRAW_HAND_SPHERES = false; var DROP_WITHOUT_SHAKE = false; +var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 }; +var EQUIP_SPHERE_ALPHA = 0.15; +var EQUIP_SPHERE_SCALE_FACTOR = 0.65; + // // distant manipulation // @@ -72,11 +78,14 @@ var LINE_ENTITY_DIMENSIONS = { var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray +var EQUIP_RADIUS_EMBIGGEN_FACTOR = 1.1; + // // near grabbing // var EQUIP_RADIUS = 0.1; // radius used for palm vs equip-hotspot for equipping. +var EQUIP_HOTSPOT_RENDER_RADIUS = 0.3; // radius used for palm vs equip-hotspot for rendering hot-spots var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position @@ -179,9 +188,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { }; CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", - updateMethod: "search", - enterMethod: "searchEnter", - exitMethod: "searchExit" + updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { name: "distance_holding", @@ -261,7 +268,8 @@ function propsArePhysical(props) { var EXTERNALLY_MANAGED_2D_MINOR_MODE = true; var EDIT_SETTING = "io.highfidelity.isEditting"; function isEditing() { - return EXTERNALLY_MANAGED_2D_MINOR_MODE && Settings.getValue(EDIT_SETTING); + var actualSettingValue = Settings.getValue(EDIT_SETTING) === "false" ? false : !!Settings.getValue(EDIT_SETTING); + return EXTERNALLY_MANAGED_2D_MINOR_MODE && actualSettingValue; } function isIn2DMode() { // In this version, we make our own determination of whether we're aimed a HUD element, @@ -281,17 +289,17 @@ function restore2DMode() { function EntityPropertiesCache() { this.cache = {}; } -EntityPropertiesCache.prototype.clear = function() { +EntityPropertiesCache.prototype.clear = function () { this.cache = {}; }; -EntityPropertiesCache.prototype.findEntities = function(position, radius) { +EntityPropertiesCache.prototype.findEntities = function (position, radius) { var entities = Entities.findEntities(position, radius); var _this = this; - entities.forEach(function(x) { + entities.forEach(function (x) { _this.updateEntity(x); }); }; -EntityPropertiesCache.prototype.updateEntity = function(entityID) { +EntityPropertiesCache.prototype.updateEntity = function (entityID) { var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES); // convert props.userData from a string to an object. @@ -307,14 +315,14 @@ EntityPropertiesCache.prototype.updateEntity = function(entityID) { this.cache[entityID] = props; }; -EntityPropertiesCache.prototype.getEntities = function() { +EntityPropertiesCache.prototype.getEntities = function () { return Object.keys(this.cache); }; -EntityPropertiesCache.prototype.getProps = function(entityID) { +EntityPropertiesCache.prototype.getProps = function (entityID) { var obj = this.cache[entityID]; return obj ? obj : undefined; }; -EntityPropertiesCache.prototype.getGrabbableProps = function(entityID) { +EntityPropertiesCache.prototype.getGrabbableProps = function (entityID) { var props = this.cache[entityID]; if (props) { return props.userData.grabbableKey ? props.userData.grabbableKey : DEFAULT_GRABBABLE_DATA; @@ -322,7 +330,7 @@ EntityPropertiesCache.prototype.getGrabbableProps = function(entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getGrabProps = function(entityID) { +EntityPropertiesCache.prototype.getGrabProps = function (entityID) { var props = this.cache[entityID]; if (props) { return props.userData.grabKey ? props.userData.grabKey : {}; @@ -330,7 +338,7 @@ EntityPropertiesCache.prototype.getGrabProps = function(entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getWearableProps = function(entityID) { +EntityPropertiesCache.prototype.getWearableProps = function (entityID) { var props = this.cache[entityID]; if (props) { return props.userData.wearable ? props.userData.wearable : {}; @@ -338,7 +346,7 @@ EntityPropertiesCache.prototype.getWearableProps = function(entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getEquipHotspotsProps = function(entityID) { +EntityPropertiesCache.prototype.getEquipHotspotsProps = function (entityID) { var props = this.cache[entityID]; if (props) { return props.userData.equipHotspots ? props.userData.equipHotspots : {}; @@ -356,7 +364,7 @@ function MyController(hand) { this.getHandPosition = MyAvatar.getLeftPalmPosition; // this.getHandRotation = MyAvatar.getLeftPalmRotation; } - this.getHandRotation = function() { + this.getHandRotation = function () { var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; return Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); }; @@ -398,16 +406,18 @@ function MyController(hand) { this.entityPropertyCache = new EntityPropertiesCache(); + this.equipOverlayInfoSetMap = {}; + var _this = this; var suppressedIn2D = [STATE_OFF, STATE_SEARCHING]; - this.ignoreInput = function() { + this.ignoreInput = function () { // We've made the decision to use 'this' for new code, even though it is fragile, // in order to keep/ the code uniform without making any no-op line changes. return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode(); }; - this.update = function(deltaTime) { + this.update = function (deltaTime) { this.updateSmoothedTrigger(); @@ -429,12 +439,12 @@ function MyController(hand) { } }; - this.callEntityMethodOnGrabbed = function(entityMethodName) { + this.callEntityMethodOnGrabbed = function (entityMethodName) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args); }; - this.setState = function(newState, reason) { + this.setState = function (newState, reason) { if (WANT_DEBUG || WANT_DEBUG_STATE) { var oldStateName = stateToName(this.state); @@ -467,7 +477,7 @@ function MyController(hand) { } }; - this.debugLine = function(closePoint, farPoint, color) { + this.debugLine = function (closePoint, farPoint, color) { Entities.addEntity({ type: "Line", name: "Grab Debug Entity", @@ -487,7 +497,7 @@ function MyController(hand) { }); }; - this.lineOn = function(closePoint, farPoint, color) { + this.lineOn = function (closePoint, farPoint, color) { // draw a line if (this.pointer === null) { this.pointer = Entities.addEntity({ @@ -519,7 +529,7 @@ function MyController(hand) { }; var SEARCH_SPHERE_ALPHA = 0.5; - this.searchSphereOn = function(location, size, color) { + this.searchSphereOn = function (location, size, color) { if (this.searchSphere === null) { var sphereProperties = { position: location, @@ -542,10 +552,10 @@ function MyController(hand) { } }; - this.overlayLineOn = function(closePoint, farPoint, color) { + this.overlayLineOn = function (closePoint, farPoint, color) { if (this.overlayLine === null) { var lineProperties = { - lineWidth: 5, + glow: 1.0, start: closePoint, end: farPoint, color: color, @@ -570,7 +580,7 @@ function MyController(hand) { } }; - this.searchIndicatorOn = function(distantPickRay) { + this.searchIndicatorOn = function (distantPickRay) { var handPosition = distantPickRay.origin; var SEARCH_SPHERE_SIZE = 0.011; var SEARCH_SPHERE_FOLLOW_RATE = 0.50; @@ -591,7 +601,7 @@ function MyController(hand) { } }; - this.handleDistantParticleBeam = function(handPosition, objectPosition, color) { + this.handleDistantParticleBeam = function (handPosition, objectPosition, color) { var handToObject = Vec3.subtract(objectPosition, handPosition); var finalRotationObject = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); @@ -607,7 +617,7 @@ function MyController(hand) { } }; - this.createParticleBeam = function(positionObject, orientationObject, color, speed, spread, lifespan) { + this.createParticleBeam = function (positionObject, orientationObject, color, speed, spread, lifespan) { var particleBeamPropertiesObject = { type: "ParticleEffect", @@ -661,7 +671,7 @@ function MyController(hand) { this.particleBeamObject = Entities.addEntity(particleBeamPropertiesObject); }; - this.updateParticleBeam = function(positionObject, orientationObject, color, speed, spread, lifespan) { + this.updateParticleBeam = function (positionObject, orientationObject, color, speed, spread, lifespan) { Entities.editEntity(this.particleBeamObject, { rotation: orientationObject, position: positionObject, @@ -673,7 +683,7 @@ function MyController(hand) { }); }; - this.evalLightWorldTransform = function(modelPos, modelRot) { + this.evalLightWorldTransform = function (modelPos, modelRot) { var MODEL_LIGHT_POSITION = { x: 0, @@ -693,7 +703,7 @@ function MyController(hand) { }; }; - this.handleSpotlight = function(parentID) { + this.handleSpotlight = function (parentID) { var LIFETIME = 100; var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); @@ -730,7 +740,7 @@ function MyController(hand) { } }; - this.handlePointLight = function(parentID) { + this.handlePointLight = function (parentID) { var LIFETIME = 100; var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); @@ -762,21 +772,21 @@ function MyController(hand) { } }; - this.lineOff = function() { + this.lineOff = function () { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); } this.pointer = null; }; - this.overlayLineOff = function() { + this.overlayLineOff = function () { if (this.overlayLine !== null) { Overlays.deleteOverlay(this.overlayLine); } this.overlayLine = null; }; - this.searchSphereOff = function() { + this.searchSphereOff = function () { if (this.searchSphere !== null) { Overlays.deleteOverlay(this.searchSphere); this.searchSphere = null; @@ -785,14 +795,14 @@ function MyController(hand) { } }; - this.particleBeamOff = function() { + this.particleBeamOff = function () { if (this.particleBeamObject !== null) { Entities.deleteEntity(this.particleBeamObject); this.particleBeamObject = null; } }; - this.turnLightsOff = function() { + this.turnLightsOff = function () { if (this.spotlight !== null) { Entities.deleteEntity(this.spotlight); this.spotlight = null; @@ -804,7 +814,7 @@ function MyController(hand) { } }; - this.turnOffVisualizations = function() { + this.turnOffVisualizations = function () { if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { this.lineOff(); } @@ -821,63 +831,62 @@ function MyController(hand) { }; - this.triggerPress = function(value) { + this.triggerPress = function (value) { _this.rawTriggerValue = value; }; - this.secondaryPress = function(value) { + this.secondaryPress = function (value) { _this.rawSecondaryValue = value; }; - this.updateSmoothedTrigger = function() { + this.updateSmoothedTrigger = function () { var triggerValue = this.rawTriggerValue; // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; - this.triggerSmoothedGrab = function() { + this.triggerSmoothedGrab = function () { return this.triggerValue > TRIGGER_GRAB_VALUE; }; - this.triggerSmoothedSqueezed = function() { + this.triggerSmoothedSqueezed = function () { return this.triggerValue > TRIGGER_ON_VALUE; }; - this.triggerSmoothedReleased = function() { + this.triggerSmoothedReleased = function () { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.secondarySqueezed = function() { + this.secondarySqueezed = function () { return _this.rawSecondaryValue > BUMPER_ON_VALUE; }; - this.secondaryReleased = function() { + this.secondaryReleased = function () { return _this.rawSecondaryValue < BUMPER_ON_VALUE; }; - // this.triggerOrsecondarySqueezed = function() { + // this.triggerOrsecondarySqueezed = function () { // return triggerSmoothedSqueezed() || secondarySqueezed(); // } - // this.triggerAndSecondaryReleased = function() { + // this.triggerAndSecondaryReleased = function () { // return triggerSmoothedReleased() && secondaryReleased(); // } - this.thumbPress = function(value) { + this.thumbPress = function (value) { _this.rawThumbValue = value; }; - this.thumbPressed = function() { + this.thumbPressed = function () { return _this.rawThumbValue > THUMB_ON_VALUE; }; - this.thumbReleased = function() { + this.thumbReleased = function () { return _this.rawThumbValue < THUMB_ON_VALUE; }; - this.off = function() { - + this.off = function () { if (this.triggerSmoothedReleased()) { this.waitForTriggerRelease = false; } @@ -887,165 +896,129 @@ function MyController(hand) { this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; if (this.triggerSmoothedSqueezed()) { this.setState(STATE_SEARCHING, "trigger squeeze detected"); + return; } } - }; - this.createHotspots = function() { - var _this = this; - - var HAND_EQUIP_SPHERE_COLOR = { red: 90, green: 255, blue: 90 }; - var HAND_EQUIP_SPHERE_ALPHA = 0.7; - var HAND_EQUIP_SPHERE_RADIUS = 0.01; - - var HAND_GRAB_SPHERE_COLOR = { red: 90, green: 90, blue: 255 }; - var HAND_GRAB_SPHERE_ALPHA = 0.3; - var HAND_GRAB_SPHERE_RADIUS = NEAR_GRAB_RADIUS; - - var EQUIP_SPHERE_COLOR = { red: 90, green: 255, blue: 90 }; - var EQUIP_SPHERE_ALPHA = 0.3; - - var GRAB_BOX_COLOR = { red: 90, green: 90, blue: 255 }; - var GRAB_BOX_ALPHA = 0.1; - - this.hotspotOverlays = []; - - var overlay; - if (DRAW_HAND_SPHERES) { - // add tiny green sphere around the palm. - var handPosition = this.getHandPosition(); - overlay = Overlays.addOverlay("sphere", { - position: handPosition, - size: HAND_EQUIP_SPHERE_RADIUS * 2, - color: HAND_EQUIP_SPHERE_COLOR, - alpha: HAND_EQUIP_SPHERE_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - }); - this.hotspotOverlays.push({ - entityID: undefined, - overlay: overlay, - type: "hand" - }); - - // add larger blue sphere around the palm. - overlay = Overlays.addOverlay("sphere", { - position: handPosition, - size: HAND_GRAB_SPHERE_RADIUS * 2, - color: HAND_GRAB_SPHERE_COLOR, - alpha: HAND_GRAB_SPHERE_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - }); - this.hotspotOverlays.push({ - entityID: undefined, - overlay: overlay, - type: "hand", - localPosition: {x: 0, y: 0, z: 0} - }); - } - - // find entities near the avatar that might be equipable. this.entityPropertyCache.clear(); - this.entityPropertyCache.findEntities(MyAvatar.position, HOTSPOT_DRAW_DISTANCE); + this.entityPropertyCache.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS); + var candidateEntities = this.entityPropertyCache.getEntities(); - if (DRAW_GRAB_BOXES) { - // add blue box overlays for grabbable entities. - this.entityPropertyCache.getEntities().forEach(function(entityID) { - var props = _this.entityPropertyCache.getProps(entityID); - if (_this.entityIsGrabbable(entityID)) { - var overlay = Overlays.addOverlay("cube", { - rotation: props.rotation, - position: props.position, - size: props.dimensions, - color: GRAB_BOX_COLOR, - alpha: GRAB_BOX_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - }); - _this.hotspotOverlays.push({ - entityID: entityID, - overlay: overlay, - type: "near", - localPosition: {x: 0, y: 0, z: 0} - }); - } - }); + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (!this.waitForTriggerRelease) { + this.updateEquipHaptics(potentialEquipHotspot); } - // add green spheres for each equippable hotspot. - flatten(this.entityPropertyCache.getEntities().map(function(entityID) { - return _this.collectEquipHotspots(entityID); - })).filter(function(hotspot) { - return _this.hotspotIsEquippable(hotspot); - }).forEach(function(hotspot) { - var overlay = Overlays.addOverlay("sphere", { - position: hotspot.worldPosition, - size: hotspot.radius * 2, - color: EQUIP_SPHERE_COLOR, - alpha: EQUIP_SPHERE_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - }); - _this.hotspotOverlays.push({ - entityID: hotspot.entityID, - overlay: overlay, - type: "equip", - localPosition: hotspot.localPosition + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot); + }; + + this.clearEquipHaptics = function () { + this.prevPotentialEquipHotspot = null; + }; + + this.updateEquipHaptics = function (potentialEquipHotspot) { + if (potentialEquipHotspot && !this.prevPotentialEquipHotspot || + !potentialEquipHotspot && this.prevPotentialEquipHotspot) { + Controller.triggerShortHapticPulse(0.5, this.hand); + } + this.prevPotentialEquipHotspot = potentialEquipHotspot; + }; + + this.clearEquipHotspotRendering = function () { + var keys = Object.keys(this.equipOverlayInfoSetMap); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]]; + this.deleteOverlayInfoSet(overlayInfoSet); + } + this.equipOverlayInfoSetMap = {}; + }; + + this.createOverlayInfoSet = function (hotspot, timestamp) { + var overlayInfoSet = { + timestamp: timestamp, + entityID: hotspot.entityID, + localPosition: hotspot.localPosition, + hotspot: hotspot, + overlays: [] + }; + + var diameter = hotspot.radius * 2; + + overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { + position: hotspot.worldPosition, + rotation: {x: 0, y: 0, z: 0, w: 1}, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, + color: EQUIP_SPHERE_COLOR, + alpha: EQUIP_SPHERE_ALPHA, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false + })); + + return overlayInfoSet; + }; + + this.updateOverlayInfoSet = function (overlayInfoSet, timestamp, potentialEquipHotspot) { + overlayInfoSet.timestamp = timestamp; + + var diameter = overlayInfoSet.hotspot.radius * 2; + + // embiggen the overlays if it maches the potentialEquipHotspot + if (potentialEquipHotspot && overlayInfoSet.entityID == potentialEquipHotspot.entityID && + Vec3.equal(overlayInfoSet.localPosition, potentialEquipHotspot.localPosition)) { + diameter = diameter * EQUIP_RADIUS_EMBIGGEN_FACTOR; + } + + var props = _this.entityPropertyCache.getProps(overlayInfoSet.entityID); + var entityXform = new Xform(props.rotation, props.position); + var position = entityXform.xformPoint(overlayInfoSet.localPosition); + + overlayInfoSet.overlays.forEach(function (overlay) { + Overlays.editOverlay(overlay, { + position: position, + rotation: props.rotation, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR }); }); }; - this.updateHotspots = function() { + this.deleteOverlayInfoSet = function (overlayInfoSet) { + overlayInfoSet.overlays.forEach(function (overlay) { + Overlays.deleteOverlay(overlay); + }); + }; + + this.updateEquipHotspotRendering = function (hotspots, potentialEquipHotspot) { + var now = Date.now(); var _this = this; - var props; - this.hotspotOverlays.forEach(function(overlayInfo) { - if (overlayInfo.type === "hand") { - Overlays.editOverlay(overlayInfo.overlay, { position: _this.getHandPosition() }); - } else if (overlayInfo.type === "equip") { - _this.entityPropertyCache.updateEntity(overlayInfo.entityID); - props = _this.entityPropertyCache.getProps(overlayInfo.entityID); - var entityXform = new Xform(props.rotation, props.position); - Overlays.editOverlay(overlayInfo.overlay, { - position: entityXform.xformPoint(overlayInfo.localPosition), - rotation: props.rotation - }); - } else if (overlayInfo.type === "near") { - _this.entityPropertyCache.updateEntity(overlayInfo.entityID); - props = _this.entityPropertyCache.getProps(overlayInfo.entityID); - Overlays.editOverlay(overlayInfo.overlay, { position: props.position, rotation: props.rotation }); + + hotspots.forEach(function (hotspot) { + var overlayInfoSet = _this.equipOverlayInfoSetMap[hotspot.key]; + if (overlayInfoSet) { + _this.updateOverlayInfoSet(overlayInfoSet, now, potentialEquipHotspot); + } else { + _this.equipOverlayInfoSetMap[hotspot.key] = _this.createOverlayInfoSet(hotspot, now); } }); - }; - this.destroyHotspots = function() { - this.hotspotOverlays.forEach(function(overlayInfo) { - Overlays.deleteOverlay(overlayInfo.overlay); - }); - this.hotspotOverlays = []; - }; - - this.searchEnter = function() { - this.createHotspots(); - }; - - this.searchExit = function() { - this.destroyHotspots(); + // delete sets with old timestamps. + var keys = Object.keys(this.equipOverlayInfoSetMap); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]]; + if (overlayInfoSet.timestamp !== now) { + this.deleteOverlayInfoSet(overlayInfoSet); + delete this.equipOverlayInfoSetMap[keys[i]]; + } + } }; // Performs ray pick test from the hand controller into the world // @param {number} which hand to use, RIGHT_HAND or LEFT_HAND // @returns {object} returns object with two keys entityID and distance // - this.calcRayPickInfo = function(hand) { + this.calcRayPickInfo = function (hand) { var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); @@ -1098,7 +1071,7 @@ function MyController(hand) { } }; - this.entityWantsTrigger = function(entityID) { + this.entityWantsTrigger = function (entityID) { var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID); return grabbableProps && grabbableProps.wantsTrigger; }; @@ -1106,13 +1079,14 @@ function MyController(hand) { // returns a list of all equip-hotspots assosiated with this entity. // @param {UUID} entityID // @returns {Object[]} array of objects with the following fields. + // * key {string} a string that can be used to uniquely identify this hotspot // * entityID {UUID} // * localPosition {Vec3} position of the hotspot in object space. // * worldPosition {vec3} position of the hotspot in world space. // * radius {number} radius of equip hotspot // * joints {Object} keys are joint names values are arrays of two elements: // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. - this.collectEquipHotspots = function(entityID) { + this.collectEquipHotspots = function (entityID) { var result = []; var props = this.entityPropertyCache.getProps(entityID); var entityXform = new Xform(props.rotation, props.position); @@ -1123,6 +1097,7 @@ function MyController(hand) { var hotspot = equipHotspotsProps[i]; if (hotspot.position && hotspot.radius && hotspot.joints) { result.push({ + key: entityID.toString() + i.toString(), entityID: entityID, localPosition: hotspot.position, worldPosition: entityXform.xformPoint(hotspot.position), @@ -1135,6 +1110,7 @@ function MyController(hand) { var wearableProps = this.entityPropertyCache.getWearableProps(entityID); if (wearableProps && wearableProps.joints) { result.push({ + key: entityID.toString() + "0", entityID: entityID, localPosition: {x: 0, y: 0, z: 0}, worldPosition: entityXform.pos, @@ -1146,7 +1122,7 @@ function MyController(hand) { return result; }; - this.hotspotIsEquippable = function(hotspot) { + this.hotspotIsEquippable = function (hotspot) { var props = this.entityPropertyCache.getProps(hotspot.entityID); var grabProps = this.entityPropertyCache.getGrabProps(hotspot.entityID); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); @@ -1165,7 +1141,7 @@ function MyController(hand) { return true; }; - this.entityIsGrabbable = function(entityID) { + this.entityIsGrabbable = function (entityID) { var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID); var grabProps = this.entityPropertyCache.getGrabProps(entityID); var props = this.entityPropertyCache.getProps(entityID); @@ -1217,7 +1193,7 @@ function MyController(hand) { return true; }; - this.entityIsDistanceGrabbable = function(entityID, handPosition) { + this.entityIsDistanceGrabbable = function (entityID, handPosition) { if (!this.entityIsGrabbable(entityID)) { return false; } @@ -1254,7 +1230,7 @@ function MyController(hand) { return true; }; - this.entityIsNearGrabbable = function(entityID, handPosition, maxDistance) { + this.entityIsNearGrabbable = function (entityID, handPosition, maxDistance) { if (!this.entityIsGrabbable(entityID)) { return false; @@ -1275,12 +1251,36 @@ function MyController(hand) { return true; }; - this.search = function() { + this.chooseNearEquipHotspots = function (candidateEntities, distance) { + var equippableHotspots = flatten(candidateEntities.map(function (entityID) { + return _this.collectEquipHotspots(entityID); + })).filter(function (hotspot) { + return (_this.hotspotIsEquippable(hotspot) && + Vec3.distance(hotspot.worldPosition, _this.getHandPosition()) < hotspot.radius + distance); + }); + return equippableHotspots; + }; + + this.chooseBestEquipHotspot = function (candidateEntities) { + var DISTANCE = 0; + var equippableHotspots = this.chooseNearEquipHotspots(candidateEntities, DISTANCE); + if (equippableHotspots.length > 0) { + // sort by distance + equippableHotspots.sort(function (a, b) { + var aDistance = Vec3.distance(a.worldPosition, this.getHandPosition()); + var bDistance = Vec3.distance(b.worldPosition, this.getHandPosition()); + return aDistance - bDistance; + }); + return equippableHotspots[0]; + } else { + return null; + } + }; + + this.search = function () { var _this = this; var name; - this.updateHotspots(); - this.grabbedEntity = null; this.isInitialGrab = false; this.shouldResetParentOnRelease = false; @@ -1298,31 +1298,17 @@ function MyController(hand) { this.entityPropertyCache.findEntities(handPosition, NEAR_GRAB_RADIUS); var candidateEntities = this.entityPropertyCache.getEntities(); - var equippableHotspots = flatten(candidateEntities.map(function(entityID) { - return _this.collectEquipHotspots(entityID); - })).filter(function(hotspot) { - return _this.hotspotIsEquippable(hotspot) && Vec3.distance(hotspot.worldPosition, handPosition) < hotspot.radius; - }); - - var entity; - if (equippableHotspots.length > 0) { - // sort by distance - equippableHotspots.sort(function(a, b) { - var aDistance = Vec3.distance(a.worldPosition, handPosition); - var bDistance = Vec3.distance(b.worldPosition, handPosition); - return aDistance - bDistance; - }); + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (potentialEquipHotspot) { if (this.triggerSmoothedGrab()) { - this.grabbedHotspot = equippableHotspots[0]; - this.grabbedEntity = equippableHotspots[0].entityID; + this.grabbedHotspot = potentialEquipHotspot; + this.grabbedEntity = potentialEquipHotspot.entityID; this.setState(STATE_HOLD, "eqipping '" + this.entityPropertyCache.getProps(this.grabbedEntity).name + "'"); return; - } else { - // TODO: highlight the equippable object? } } - var grabbableEntities = candidateEntities.filter(function(entity) { + var grabbableEntities = candidateEntities.filter(function (entity) { return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); }); @@ -1339,9 +1325,10 @@ function MyController(hand) { this.intersectionDistance = 0; } + var entity; if (grabbableEntities.length > 0) { // sort by distance - grabbableEntities.sort(function(a, b) { + grabbableEntities.sort(function (a, b) { var aDistance = Vec3.distance(_this.entityPropertyCache.getProps(a).position, handPosition); var bDistance = Vec3.distance(_this.entityPropertyCache.getProps(b).position, handPosition); return aDistance - bDistance; @@ -1354,11 +1341,10 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); return; } else { - // TODO: highlight the near-triggerable object? + // potentialNearTriggerEntity = entity; } } else { if (this.triggerSmoothedGrab()) { - var props = this.entityPropertyCache.getProps(entity); var grabProps = this.entityPropertyCache.getGrabProps(entity); var refCount = grabProps.refCount ? grabProps.refCount : 0; @@ -1373,10 +1359,9 @@ function MyController(hand) { this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); return; } else { - // TODO: highlight the grabbable object? + // potentialNearGrabEntity = entity; } } - return; } if (rayPickInfo.entityID) { @@ -1388,7 +1373,7 @@ function MyController(hand) { this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); return; } else { - // TODO: highlight the far-triggerable object? + // potentialFarTriggerEntity = entity; } } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { if (this.triggerSmoothedGrab() && !isEditing()) { @@ -1396,11 +1381,16 @@ function MyController(hand) { this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); return; } else { - // TODO: highlight the far-grabbable object? + // potentialFarGrabEntity = entity; } } } + this.updateEquipHaptics(potentialEquipHotspot); + + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot); + // search line visualizations if (USE_ENTITY_LINES_FOR_SEARCHING === true) { this.lineOn(rayPickInfo.searchRay.origin, @@ -1412,7 +1402,7 @@ function MyController(hand) { Reticle.setVisible(false); }; - this.distanceGrabTimescale = function(mass, distance) { + this.distanceGrabTimescale = function (mass, distance) { var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / DISTANCE_HOLDING_UNITY_MASS * distance / DISTANCE_HOLDING_UNITY_DISTANCE; @@ -1422,11 +1412,14 @@ function MyController(hand) { return timeScale; }; - this.getMass = function(dimensions, density) { + this.getMass = function (dimensions, density) { return (dimensions.x * dimensions.y * dimensions.z) * density; }; - this.distanceHoldingEnter = function() { + this.distanceHoldingEnter = function () { + + this.clearEquipHotspotRendering(); + this.clearEquipHaptics(); // controller pose is in avatar frame var avatarControllerPose = @@ -1485,7 +1478,7 @@ function MyController(hand) { this.previousControllerRotation = controllerRotation; }; - this.distanceHolding = function() { + this.distanceHolding = function () { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); @@ -1617,7 +1610,7 @@ function MyController(hand) { this.previousControllerRotation = controllerRotation; }; - this.setupHoldAction = function() { + this.setupHoldAction = function () { this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, @@ -1637,7 +1630,7 @@ function MyController(hand) { return true; }; - this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { + this.projectVectorAlongAxis = function (position, axisStart, axisEnd) { var aPrime = Vec3.subtract(position, axisStart); var bPrime = Vec3.subtract(axisEnd, axisStart); var bPrimeMagnitude = Vec3.length(bPrime); @@ -1653,12 +1646,12 @@ function MyController(hand) { return projection; }; - this.dropGestureReset = function() { + this.dropGestureReset = function () { this.fastHandMoveDetected = false; this.fastHandMoveTimer = 0; }; - this.dropGestureProcess = function(deltaTime) { + this.dropGestureProcess = function (deltaTime) { var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); var worldHandVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity); @@ -1697,12 +1690,16 @@ function MyController(hand) { return (DROP_WITHOUT_SHAKE || this.fastHandMoveDetected) && handIsUpsideDown; }; - this.nearGrabbingEnter = function() { + this.nearGrabbingEnter = function () { this.lineOff(); this.overlayLineOff(); this.dropGestureReset(); + this.clearEquipHaptics(); + this.clearEquipHotspotRendering(); + + Controller.triggerShortHapticPulse(1.0, this.hand); if (this.entityActivated) { var saveGrabbedID = this.grabbedEntity; @@ -1802,7 +1799,7 @@ function MyController(hand) { this.currentAngularVelocity = ZERO_VEC; }; - this.nearGrabbing = function(deltaTime) { + this.nearGrabbing = function (deltaTime) { var dropDetected = this.dropGestureProcess(deltaTime); @@ -1919,15 +1916,23 @@ function MyController(hand) { } }; - this.nearTriggerEnter = function() { + this.nearTriggerEnter = function () { + + this.clearEquipHotspotRendering(); + this.clearEquipHaptics(); + + Controller.triggerShortHapticPulse(1.0, this.hand); this.callEntityMethodOnGrabbed("startNearTrigger"); }; - this.farTriggerEnter = function() { + this.farTriggerEnter = function () { + this.clearEquipHotspotRendering(); + this.clearEquipHaptics(); + this.callEntityMethodOnGrabbed("startFarTrigger"); }; - this.nearTrigger = function() { + this.nearTrigger = function () { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopNearTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -1936,7 +1941,7 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueNearTrigger"); }; - this.farTrigger = function() { + this.farTrigger = function () { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopFarTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -1969,11 +1974,11 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueFarTrigger"); }; - this.offEnter = function() { + this.offEnter = function () { this.release(); }; - this.release = function() { + this.release = function () { this.turnLightsOff(); this.turnOffVisualizations(); @@ -2011,14 +2016,14 @@ function MyController(hand) { } }; - this.cleanup = function() { + this.cleanup = function () { this.release(); Entities.deleteEntity(this.particleBeamObject); Entities.deleteEntity(this.spotLight); Entities.deleteEntity(this.pointLight); }; - this.heartBeat = function(entityID) { + this.heartBeat = function (entityID) { var now = Date.now(); if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) { var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); @@ -2028,7 +2033,7 @@ function MyController(hand) { } }; - this.resetAbandonedGrab = function(entityID) { + this.resetAbandonedGrab = function (entityID) { print("cleaning up abandoned grab on " + entityID); var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); data["refCount"] = 1; @@ -2036,7 +2041,7 @@ function MyController(hand) { this.deactivateEntity(entityID, false); }; - this.activateEntity = function(entityID, grabbedProperties, wasLoaded) { + this.activateEntity = function (entityID, grabbedProperties, wasLoaded) { if (this.entityActivated) { return; } @@ -2098,18 +2103,18 @@ function MyController(hand) { return data; }; - this.checkForStrayChildren = function() { + this.checkForStrayChildren = function () { // sometimes things can get parented to a hand and this script is unaware. Search for such entities and // unhook them. var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); - children.forEach(function(childID) { + children.forEach(function (childID) { print("disconnecting stray child of hand: (" + _this.hand + ") " + childID); Entities.editEntity(childID, {parentID: NULL_UUID}); }); }; - this.deactivateEntity = function(entityID, noVelocity) { + this.deactivateEntity = function (entityID, noVelocity) { var deactiveProps; if (!this.entityActivated) { @@ -2194,7 +2199,7 @@ function MyController(hand) { setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); }; - this.getOtherHandController = function() { + this.getOtherHandController = function () { return (this.hand === RIGHT_HAND) ? leftController : rightController; }; } @@ -2236,7 +2241,7 @@ Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); Messages.subscribe('Hifi-Object-Manipulation'); -var handleHandMessages = function(channel, message, sender) { +var handleHandMessages = function (channel, message, sender) { var data; if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Hand-Disabler') { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6810bae590..4e87fcbf71 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -332,6 +332,8 @@ var toolBar = (function() { entityListTool.clearEntityList(); }; + var EDIT_SETTING = "io.highfidelity.isEditting"; // for communication with other scripts + Settings.setValue(EDIT_SETTING, false); that.setActive = function(active) { if (active != isActive) { if (active && !Entities.canRez() && !Entities.canRezTmp()) { @@ -341,6 +343,7 @@ var toolBar = (function() { enabled: active })); isActive = active; + Settings.setValue(EDIT_SETTING, active); if (!isActive) { entityListTool.setVisible(false); gridTool.setVisible(false); @@ -348,6 +351,7 @@ var toolBar = (function() { propertiesTool.setVisible(false); selectionManager.clearSelections(); cameraManager.disable(); + selectionDisplay.triggerMapping.disable(); } else { UserActivityLogger.enabledEdit(); hasShownPropertiesTool = false; @@ -356,6 +360,7 @@ var toolBar = (function() { grid.setEnabled(true); propertiesTool.setVisible(true); // Not sure what the following was meant to accomplish, but it currently causes + selectionDisplay.triggerMapping.enable(); // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); } @@ -1438,6 +1443,9 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) { var entityID = SelectionManager.selections[i]; var initialProperties = SelectionManager.savedProperties[entityID]; var currentProperties = Entities.getEntityProperties(entityID); + if (!initialProperties) { + continue; + } undoData.setProperties.push({ entityID: entityID, properties: { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 121e38c340..54c79b1d9f 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -1646,6 +1646,8 @@ + + diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index e46306ca31..2003df3652 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1005,6 +1005,56 @@ SelectionDisplay = (function() { var activeTool = null; var grabberTools = {}; + // We get mouseMoveEvents from the handControllers, via handControllerPointer. + // But we dont' get mousePressEvents. + that.triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); + Script.scriptEnding.connect(that.triggerMapping.disable); + that.TRIGGER_GRAB_VALUE = 0.85; // From handControllerGrab/Pointer.js. Should refactor. + that.TRIGGER_ON_VALUE = 0.4; + that.TRIGGER_OFF_VALUE = 0.15; + that.triggered = false; + var activeHand = Controller.Standard.RightHand; + function makeTriggerHandler(hand) { + return function (value) { + if (!that.triggered && (value > that.TRIGGER_GRAB_VALUE)) { // should we smooth? + that.triggered = true; + if (activeHand !== hand) { + // No switching while the other is already triggered, so no need to release. + activeHand = (activeHand === Controller.Standard.RightHand) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + } + var eventResult = that.mousePressEvent({}); + if (!eventResult || (eventResult === 'selectionBox')) { + var pickRay = controllerComputePickRay(); + if (pickRay) { + var entityIntersection = Entities.findRayIntersection(pickRay, true); + var overlayIntersection = Overlays.findRayIntersection(pickRay); + if (entityIntersection.intersects && + (!overlayIntersection.intersects || (entityIntersection.distance < overlayIntersection.distance))) { + selectionManager.setSelections([entityIntersection.entityID]); + } + } + } + } else if (that.triggered && (value < that.TRIGGER_OFF_VALUE)) { + that.triggered = false; + that.mouseReleaseEvent({}); + } + }; + } + that.triggerMapping.from(Controller.Standard.RT).peek().to(makeTriggerHandler(Controller.Standard.RightHand)); + that.triggerMapping.from(Controller.Standard.LT).peek().to(makeTriggerHandler(Controller.Standard.LeftHand)); + function controllerComputePickRay() { + var controllerPose = Controller.getPoseValue(activeHand); + if (controllerPose.valid && that.triggered) { + var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), + MyAvatar.position); + // This gets point direction right, but if you want general quaternion it would be more complicated: + var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation)); + return {origin: controllerPosition, direction: controllerDirection}; + } + } + function generalComputePickRay(x, y) { + return controllerComputePickRay() || Camera.computePickRay(x, y); + } function addGrabberTool(overlay, tool) { grabberTools[overlay] = { mode: tool.mode, @@ -1047,7 +1097,7 @@ SelectionDisplay = (function() { lastCameraOrientation = Camera.getOrientation(); if (event !== false) { - pickRay = Camera.computePickRay(event.x, event.y); + pickRay = generalComputePickRay(event.x, event.y); var wantDebug = false; if (wantDebug) { @@ -2269,7 +2319,7 @@ SelectionDisplay = (function() { startPosition = SelectionManager.worldPosition; var dimensions = SelectionManager.worldDimensions; - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); initialXZPick = rayPlaneIntersection(pickRay, translateXZTool.pickPlanePosition, { x: 0, y: 1, @@ -2312,7 +2362,7 @@ SelectionDisplay = (function() { }, onMove: function(event) { var wantDebug = false; - pickRay = Camera.computePickRay(event.x, event.y); + pickRay = generalComputePickRay(event.x, event.y); var pick = rayPlaneIntersection2(pickRay, translateXZTool.pickPlanePosition, { x: 0, @@ -2422,6 +2472,9 @@ SelectionDisplay = (function() { for (var i = 0; i < SelectionManager.selections.length; i++) { var properties = SelectionManager.savedProperties[SelectionManager.selections[i]]; + if (!properties) { + continue; + } var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, @@ -2448,7 +2501,7 @@ SelectionDisplay = (function() { addGrabberTool(grabberMoveUp, { mode: "TRANSLATE_UP_DOWN", onBegin: function(event) { - pickRay = Camera.computePickRay(event.x, event.y); + pickRay = generalComputePickRay(event.x, event.y); upDownPickNormal = Quat.getFront(lastCameraOrientation); // Remove y component so the y-axis lies along the plane we picking on - this will @@ -2481,7 +2534,7 @@ SelectionDisplay = (function() { pushCommandForSelections(duplicatedEntityIDs); }, onMove: function(event) { - pickRay = Camera.computePickRay(event.x, event.y); + pickRay = generalComputePickRay(event.x, event.y); // translate mode left/right based on view toward entity var newIntersection = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, upDownPickNormal); @@ -2690,7 +2743,7 @@ SelectionDisplay = (function() { } } planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); lastPick = rayPlaneIntersection(pickRay, pickRayPosition, planeNormal); @@ -2726,7 +2779,7 @@ SelectionDisplay = (function() { rotation = SelectionManager.worldRotation; } - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); newPick = rayPlaneIntersection(pickRay, pickRayPosition, planeNormal); @@ -2782,10 +2835,10 @@ SelectionDisplay = (function() { var wantDebug = false; if (wantDebug) { print(stretchMode); - Vec3.print(" newIntersection:", newIntersection); + //Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); - Vec3.print(" oldPOS:", oldPOS); - Vec3.print(" newPOS:", newPOS); + //Vec3.print(" oldPOS:", oldPOS); + //Vec3.print(" newPOS:", newPOS); Vec3.print(" changeInDimensions:", changeInDimensions); Vec3.print(" newDimensions:", newDimensions); @@ -3350,7 +3403,7 @@ SelectionDisplay = (function() { pushCommandForSelections(); }, onMove: function(event) { - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false @@ -3520,7 +3573,7 @@ SelectionDisplay = (function() { pushCommandForSelections(); }, onMove: function(event) { - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false @@ -3682,7 +3735,7 @@ SelectionDisplay = (function() { pushCommandForSelections(); }, onMove: function(event) { - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false @@ -3797,13 +3850,13 @@ SelectionDisplay = (function() { that.mousePressEvent = function(event) { var wantDebug = false; - if (!event.isLeftButton) { + if (!event.isLeftButton && !that.triggered) { // if another mouse button than left is pressed ignore it return false; } var somethingClicked = false; - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); // before we do a ray test for grabbers, disable the ray intersection for our selection box Overlays.editOverlay(selectionBox, { @@ -3837,7 +3890,7 @@ SelectionDisplay = (function() { if (tool) { activeTool = tool; mode = tool.mode; - somethingClicked = true; + somethingClicked = 'tool'; if (activeTool && activeTool.onBegin) { activeTool.onBegin(event); } @@ -3845,7 +3898,7 @@ SelectionDisplay = (function() { switch (result.overlayID) { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; - somethingClicked = true; + somethingClicked = mode; // in translate mode, we hide our stretch handles... for (var i = 0; i < stretchHandles.length; i++) { @@ -3860,34 +3913,34 @@ SelectionDisplay = (function() { case grabberEdgeTN: // TODO: maybe this should be TOP+NEAR stretching? case grabberEdgeBN: // TODO: maybe this should be BOTTOM+FAR stretching? mode = "STRETCH_NEAR"; - somethingClicked = true; + somethingClicked = mode; break; case grabberFAR: case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? mode = "STRETCH_FAR"; - somethingClicked = true; + somethingClicked = mode; break; case grabberTOP: mode = "STRETCH_TOP"; - somethingClicked = true; + somethingClicked = mode; break; case grabberBOTTOM: mode = "STRETCH_BOTTOM"; - somethingClicked = true; + somethingClicked = mode; break; case grabberRIGHT: case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? mode = "STRETCH_RIGHT"; - somethingClicked = true; + somethingClicked = mode; break; case grabberLEFT: case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? mode = "STRETCH_LEFT"; - somethingClicked = true; + somethingClicked = mode; break; default: @@ -3955,7 +4008,7 @@ SelectionDisplay = (function() { if (tool) { activeTool = tool; mode = tool.mode; - somethingClicked = true; + somethingClicked = 'tool'; if (activeTool && activeTool.onBegin) { activeTool.onBegin(event); } @@ -3963,7 +4016,7 @@ SelectionDisplay = (function() { switch (result.overlayID) { case yawHandle: mode = "ROTATE_YAW"; - somethingClicked = true; + somethingClicked = mode; overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; yawZero = result.intersection; @@ -3973,7 +4026,7 @@ SelectionDisplay = (function() { case pitchHandle: mode = "ROTATE_PITCH"; initialPosition = SelectionManager.worldPosition; - somethingClicked = true; + somethingClicked = mode; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; pitchZero = result.intersection; @@ -3982,7 +4035,7 @@ SelectionDisplay = (function() { case rollHandle: mode = "ROTATE_ROLL"; - somethingClicked = true; + somethingClicked = mode; overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; rollZero = result.intersection; @@ -4156,7 +4209,7 @@ SelectionDisplay = (function() { mode = translateXZTool.mode; activeTool.onBegin(event); - somethingClicked = true; + somethingClicked = 'selectionBox'; break; default: if (wantDebug) { @@ -4169,7 +4222,7 @@ SelectionDisplay = (function() { } if (somethingClicked) { - pickRay = Camera.computePickRay(event.x, event.y); + pickRay = generalComputePickRay(event.x, event.y); if (wantDebug) { print("mousePressEvent()...... " + overlayNames[result.overlayID]); } @@ -4201,7 +4254,7 @@ SelectionDisplay = (function() { } // if no tool is active, then just look for handles to highlight... - var pickRay = Camera.computePickRay(event.x, event.y); + var pickRay = generalComputePickRay(event.x, event.y); var result = Overlays.findRayIntersection(pickRay); var pickedColor; var pickedAlpha; @@ -4320,7 +4373,7 @@ SelectionDisplay = (function() { that.updateHandleSizes = function() { if (selectionManager.hasSelection()) { var diff = Vec3.subtract(selectionManager.worldPosition, Camera.getPosition()); - var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 2; + var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO * 5; var dimensions = SelectionManager.worldDimensions; var avgDimension = (dimensions.x + dimensions.y + dimensions.z) / 3; grabberSize = Math.min(grabberSize, avgDimension / 10); diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 15b768bb36..1a4f8742e9 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -91,7 +92,7 @@ public: virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } virtual ui::Menu* getPrimaryMenu() override { return nullptr; } virtual bool isForeground() const override { return true; } - virtual DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); } + virtual DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); } }; class MyControllerScriptingInterface : public controller::ScriptingInterface { @@ -144,6 +145,9 @@ int main(int argc, char** argv) { if (name == KeyboardMouseDevice::NAME) { userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); } + if (name == TouchscreenDevice::NAME) { + userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); + } inputPlugin->pluginUpdate(0, calibrationData); } rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt new file mode 100644 index 0000000000..d4f90fdace --- /dev/null +++ b/tests/render-perf/CMakeLists.txt @@ -0,0 +1,18 @@ + +set(TARGET_NAME render-perf-test) + +if (WIN32) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") +endif() + +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Quick Gui OpenGL) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") + +# link in the shared libraries +link_hifi_libraries(shared octree gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics) + +package_libraries_for_deployment() + + +target_bullet() diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp new file mode 100644 index 0000000000..5367c65d94 --- /dev/null +++ b/tests/render-perf/src/main.cpp @@ -0,0 +1,651 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const QString LAST_SCENE_KEY = "lastSceneFile"; + +class ParentFinder : public SpatialParentFinder { +public: + EntityTreePointer _tree; + ParentFinder(EntityTreePointer tree) : _tree(tree) {} + + SpatiallyNestableWeakPointer find(QUuid parentID, bool& success, SpatialParentTree* entityTree = nullptr) const override { + SpatiallyNestableWeakPointer parent; + + if (parentID.isNull()) { + success = true; + return parent; + } + + // search entities + if (entityTree) { + parent = entityTree->findByID(parentID); + } else { + parent = _tree ? _tree->findEntityByEntityItemID(parentID) : nullptr; + } + + if (!parent.expired()) { + success = true; + return parent; + } + + success = false; + return parent; + } +}; + +class Camera { +protected: + float fov { 60.0f }; + float znear { 0.1f }, zfar { 512.0f }; + float aspect { 1.0f }; + + void updateViewMatrix() { + matrices.view = glm::inverse(glm::translate(glm::mat4(), position) * glm::mat4_cast(getOrientation())); + } + + glm::quat getOrientation() const { + return glm::angleAxis(yaw, Vectors::UP); + } +public: + float yaw { 0 }; + glm::vec3 position; + + float rotationSpeed { 1.0f }; + float movementSpeed { 1.0f }; + + struct Matrices { + glm::mat4 perspective; + glm::mat4 view; + } matrices; + enum Key { + RIGHT, + LEFT, + UP, + DOWN, + BACK, + FORWARD, + KEYS_SIZE, + INVALID = -1, + }; + + std::bitset keys; + + Camera() { + matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); + } + + bool moving() { + return keys.any(); + } + + void setFieldOfView(float fov) { + this->fov = fov; + matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); + } + + void setAspectRatio(const glm::vec2& size) { + setAspectRatio(size.x / size.y); + } + + void setAspectRatio(float aspect) { + this->aspect = aspect; + matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); + } + + void setPerspective(float fov, const glm::vec2& size, float znear = 0.1f, float zfar = 512.0f) { + setPerspective(fov, size.x / size.y, znear, zfar); + } + + void setPerspective(float fov, float aspect, float znear = 0.1f, float zfar = 512.0f) { + this->aspect = aspect; + this->fov = fov; + this->znear = znear; + this->zfar = zfar; + matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); + }; + + void rotate(const float delta) { + yaw += delta; + updateViewMatrix(); + } + + void setPosition(const glm::vec3& position) { + this->position = position; + updateViewMatrix(); + } + + // Translate in the Z axis of the camera + void dolly(float delta) { + auto direction = glm::vec3(0, 0, delta); + translate(direction); + } + + // Translate in the XY plane of the camera + void translate(const glm::vec2& delta) { + auto move = glm::vec3(delta.x, delta.y, 0); + translate(move); + } + + void translate(const glm::vec3& delta) { + position += getOrientation() * delta; + updateViewMatrix(); + } + + void update(float deltaTime) { + if (moving()) { + glm::vec3 camFront = getOrientation() * Vectors::FRONT; + glm::vec3 camRight = getOrientation() * Vectors::RIGHT; + glm::vec3 camUp = getOrientation() * Vectors::UP; + float moveSpeed = deltaTime * movementSpeed; + + if (keys[FORWARD]) { + position += camFront * moveSpeed; + } + if (keys[BACK]) { + position -= camFront * moveSpeed; + } + if (keys[LEFT]) { + position -= camRight * moveSpeed; + } + if (keys[RIGHT]) { + position += camRight * moveSpeed; + } + if (keys[UP]) { + position += camUp * moveSpeed; + } + if (keys[DOWN]) { + position -= camUp * moveSpeed; + } + updateViewMatrix(); + } + } +}; + +class QWindowCamera : public Camera { + Key forKey(int key) { + switch (key) { + case Qt::Key_W: return FORWARD; + case Qt::Key_S: return BACK; + case Qt::Key_A: return LEFT; + case Qt::Key_D: return RIGHT; + case Qt::Key_E: return UP; + case Qt::Key_C: return DOWN; + default: break; + } + return INVALID; + } + + vec2 _lastMouse; +public: + void onKeyPress(QKeyEvent* event) { + Key k = forKey(event->key()); + if (k == INVALID) { + return; + } + keys.set(k); + } + + void onKeyRelease(QKeyEvent* event) { + Key k = forKey(event->key()); + if (k == INVALID) { + return; + } + keys.reset(k); + } + + void onMouseMove(QMouseEvent* event) { + vec2 mouse = toGlm(event->localPos()); + vec2 delta = mouse - _lastMouse; + auto buttons = event->buttons(); + if (buttons & Qt::RightButton) { + dolly(delta.y * 0.01f); + } else if (buttons & Qt::LeftButton) { + rotate(delta.x * -0.01f); + } else if (buttons & Qt::MiddleButton) { + delta.y *= -1.0f; + translate(delta * -0.01f); + } + + _lastMouse = mouse; + } +}; + +// Create a simple OpenGL window that renders text in various ways +class QTestWindow : public QWindow, public AbstractViewStateInterface { + Q_OBJECT + +protected: + void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { + viewOut = _viewFrustum; + } + + void copyShadowViewFrustum(ViewFrustum& viewOut) const override { + viewOut = _shadowViewFrustum; + } + + QThread* getMainThread() override { + return QThread::currentThread(); + } + + PickRay computePickRay(float x, float y) const override { + return PickRay(); + } + + glm::vec3 getAvatarPosition() const override { + return vec3(); + } + + void postLambdaEvent(std::function f) override {} + qreal getDevicePixelRatio() override { + return 1.0f; + } + + render::ScenePointer getMain3DScene() override { + return _main3DScene; + } + render::EnginePointer getRenderEngine() override { + return _renderEngine; + } + + void pushPostUpdateLambda(void* key, std::function func) override {} + +public: + //"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126" + static void setup() { + DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent, 0); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + } + + QTestWindow() { + QThread::currentThread()->setPriority(QThread::HighestPriority); + AbstractViewStateInterface::setInstance(this); + _octree = DependencyManager::set(false, this, nullptr); + _octree->init(); + DependencyManager::set(_octree->getTree()); + getEntities()->setViewFrustum(_viewFrustum); + auto nodeList = DependencyManager::get(); + NodePermissions permissions; + permissions.setAll(true); + nodeList->setPermissions(permissions); + + ResourceManager::init(); + setSurfaceType(QSurface::OpenGLSurface); + auto format = getDefaultOpenGLSurfaceFormat(); + format.setOption(QSurfaceFormat::DebugContext); + setFormat(format); + + _context.setFormat(format); + _context.create(); + + show(); + makeCurrent(); + glewInit(); + glGetError(); +#ifdef Q_OS_WIN + wglSwapIntervalEXT(0); +#endif + _camera.movementSpeed = 3.0f; + + setupDebugLogger(this); + qDebug() << (const char*)glGetString(GL_VERSION); + + // GPU library init + { + gpu::Context::init(); + _gpuContext = std::make_shared(); + } + + // Render engine library init + { + makeCurrent(); + DependencyManager::get()->init(); + _renderEngine->addJob("RenderShadowTask", _cullFunctor); + _renderEngine->addJob("RenderDeferredTask", _cullFunctor); + _renderEngine->load(); + _renderEngine->registerScene(_main3DScene); + } + + QVariant lastScene = _settings.value(LAST_SCENE_KEY); + if (lastScene.isValid()) { + auto result = QMessageBox::question(nullptr, "Question", "Load last scene " + lastScene.toString()); + if (result != QMessageBox::No) { + importScene(lastScene.toString()); + } + } + + resize(QSize(800, 600)); + _elapsed.start(); + + QTimer* timer = new QTimer(this); + timer->setInterval(0); + connect(timer, &QTimer::timeout, this, [this] { + draw(); + }); + timer->start(); + } + + virtual ~QTestWindow() { + ResourceManager::cleanup(); + } + +protected: + void keyPressEvent(QKeyEvent* event) override { + switch (event->key()) { + case Qt::Key_F1: + importScene(); + return; + + case Qt::Key_F2: + goTo(); + return; + + default: + break; + } + _camera.onKeyPress(event); + } + + void keyReleaseEvent(QKeyEvent* event) override { + _camera.onKeyRelease(event); + } + + void mouseMoveEvent(QMouseEvent* event) override { + _camera.onMouseMove(event); + } + + void resizeEvent(QResizeEvent* ev) override { + resizeWindow(ev->size()); + } + +private: + + static bool cull(const RenderArgs* renderArgs, const AABox& box) { + return true; + } + + void update() { + auto now = usecTimestampNow(); + static auto last = now; + + float delta = now - last; + // Update the camera + _camera.update(delta / USECS_PER_SECOND); + + + // load the view frustum + { + _viewFrustum.setProjection(_camera.matrices.perspective); + auto view = glm::inverse(_camera.matrices.view); + _viewFrustum.setPosition(glm::vec3(view[3])); + _viewFrustum.setOrientation(glm::quat_cast(view)); + } + last = now; + } + + void draw() { + if (!isVisible()) { + return; + } + update(); + + makeCurrent(); +#define RENDER_SCENE 1 + +#if RENDER_SCENE + RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE, + 0, RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + + auto framebufferCache = DependencyManager::get(); + QSize windowSize = size(); + framebufferCache->setFrameBufferSize(windowSize); + // Viewport is assigned to the size of the framebuffer + renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height()); + + renderArgs.setViewFrustum(_viewFrustum); + + // Final framebuffer that will be handled to the display-plugin + { + auto finalFramebuffer = framebufferCache->getFramebuffer(); + renderArgs._blitFramebuffer = finalFramebuffer; + } + + render(&renderArgs); + + { + gpu::gl::GLFramebuffer* framebuffer = gpu::Backend::getGPUObject(*renderArgs._blitFramebuffer); + auto fbo = framebuffer->_id; + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + const auto& vp = renderArgs._viewport; + glBlitFramebuffer(vp.x, vp.y, vp.z, vp.w, vp.x, vp.y, vp.z, vp.w, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } +#else + glClearColor(0.0f, 0.5f, 0.8f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +#endif + + _context.swapBuffers(this); + +#if RENDER_SCENE + framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer); + renderArgs._blitFramebuffer.reset(); + gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { + batch.resetStages(); + }); +#endif + fps.increment(); + static size_t _frameCount { 0 }; + ++_frameCount; + if (_elapsed.elapsed() >= 4000) { + qDebug() << "FPS " << fps.rate(); + _frameCount = 0; + _elapsed.restart(); + } + + if (0 == ++_frameCount % 100) { + } + } + + void render(RenderArgs* renderArgs) { + PROFILE_RANGE(__FUNCTION__); + PerformanceTimer perfTimer("draw"); + // The pending changes collecting the changes here + render::PendingChanges pendingChanges; + // Setup the current Zone Entity lighting + DependencyManager::get()->setGlobalLight(_sunSkyStage.getSunLight()); + { + PerformanceTimer perfTimer("SceneProcessPendingChanges"); + _main3DScene->enqueuePendingChanges(pendingChanges); + _main3DScene->processPendingChangesQueue(); + } + + // For now every frame pass the renderContext + { + PerformanceTimer perfTimer("EngineRun"); + _renderEngine->getRenderContext()->args = renderArgs; + // Before the deferred pass, let's try to use the render engine + _renderEngine->run(); + } + } + + void makeCurrent() { + _context.makeCurrent(this); + } + + void resizeWindow(const QSize& size) { + _size = size; + _camera.setAspectRatio((float)_size.width() / (float)_size.height()); + } + + void parsePath(const QString& viewpointString) { + static const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)"; + static const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*"; + static const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + FLOAT_REGEX_STRING + "\\s*(?:$|\\/)"; + static const QString QUAT_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + + FLOAT_REGEX_STRING + "\\s*$"; + + static QRegExp orientationRegex(QUAT_REGEX_STRING); + static QRegExp positionRegex(POSITION_REGEX_STRING); + + if (positionRegex.indexIn(viewpointString) != -1) { + // we have at least a position, so emit our signal to say we need to change position + glm::vec3 newPosition(positionRegex.cap(1).toFloat(), + positionRegex.cap(2).toFloat(), + positionRegex.cap(3).toFloat()); + _camera.setPosition(newPosition); + + if (!glm::any(glm::isnan(newPosition))) { + // we may also have an orientation + if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/') + && orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) { + //glm::vec4 v = glm::vec4( + // orientationRegex.cap(1).toFloat(), + // orientationRegex.cap(2).toFloat(), + // orientationRegex.cap(3).toFloat(), + // orientationRegex.cap(4).toFloat()); + //if (!glm::any(glm::isnan(v))) { + // _camera.setRotation(glm::normalize(glm::quat(v.w, v.x, v.y, v.z))); + //} + } + } + } + } + + void importScene(const QString& fileName) { + _settings.setValue(LAST_SCENE_KEY, fileName); + _octree->clear(); + _octree->getTree()->readFromURL(fileName); + } + + void importScene() { + QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), "/home", tr("Hifi Exports (*.json *.svo)")); + if (fileName.isNull()) { + return; + } + importScene(fileName); + } + + void goTo() { + QString destination = QInputDialog::getText(nullptr, tr("Go To Location"), "Enter path"); + if (destination.isNull()) { + return; + } + parsePath(destination); + } + +private: + render::CullFunctor _cullFunctor { cull }; + gpu::ContextPointer _gpuContext; // initialized during window creation + render::EnginePointer _renderEngine { new render::Engine() }; + render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; + QOpenGLContextWrapper _context; + QSize _size; + RateCounter<> fps; + QSettings _settings; + + QWindowCamera _camera; + ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. + ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. + model::SunSkyStage _sunSkyStage; + model::LightPointer _globalLight { std::make_shared() }; + QElapsedTimer _elapsed; + QSharedPointer _octree; + QSharedPointer getEntities() { + return _octree; + } +}; + +void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + if (!message.isEmpty()) { +#ifdef Q_OS_WIN + OutputDebugStringA(message.toLocal8Bit().constData()); + OutputDebugStringA("\n"); +#endif + std::cout << message.toLocal8Bit().constData() << std::endl; + } +} + +const char * LOG_FILTER_RULES = R"V0G0N( +hifi.gpu=true +)V0G0N"; + +int main(int argc, char** argv) { + QApplication app(argc, argv); + QCoreApplication::setApplicationName("RenderPerf"); + QCoreApplication::setOrganizationName("High Fidelity"); + QCoreApplication::setOrganizationDomain("highfidelity.com"); + + qInstallMessageHandler(messageHandler); + QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + QTestWindow::setup(); + QTestWindow window; + app.exec(); + return 0; +} + +#include "main.moc"