diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8d2439e5b8..e12452472a 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -4,12 +4,16 @@ import "controls" import "styles" Dialog { + id: root + HifiConstants { id: hifi } + title: "Go to..." objectName: "AddressBarDialog" - height: 128 - width: 512 + contentImplicitWidth: addressBarDialog.implicitWidth + contentImplicitHeight: addressBarDialog.implicitHeight destroyOnCloseButton: false + onVisibleChanged: { if (!visible) { reset(); @@ -21,6 +25,11 @@ Dialog { addressLine.forceActiveFocus(); } } + onParentChanged: { + if (enabled && visible) { + addressLine.forceActiveFocus(); + } + } function reset() { addressLine.text = "" @@ -29,24 +38,26 @@ Dialog { AddressBarDialog { id: addressBarDialog - // The client area - anchors.fill: parent - anchors.margins: parent.margins - anchors.topMargin: parent.topMargin + x: root.clientX + y: root.clientY + implicitWidth: 512 + implicitHeight: border.height + hifi.layout.spacing * 4 + Border { + id: border height: 64 anchors.left: parent.left - anchors.leftMargin: 0 + anchors.leftMargin: hifi.layout.spacing * 2 anchors.right: goButton.left - anchors.rightMargin: 8 + anchors.rightMargin: hifi.layout.spacing anchors.verticalCenter: parent.verticalCenter TextInput { id: addressLine anchors.fill: parent helperText: "domain, location, @user, /x,y,z" - anchors.margins: 8 + anchors.margins: hifi.layout.spacing onAccepted: { event.accepted addressBarDialog.loadAddress(addressLine.text) @@ -59,7 +70,7 @@ Dialog { width: 32 height: 32 anchors.right: parent.right - anchors.rightMargin: 8 + anchors.rightMargin: hifi.layout.spacing * 2 source: "../images/address-bar-submit.svg" anchors.verticalCenter: parent.verticalCenter diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 1e02683c57..5653dfc7a1 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -60,9 +60,6 @@ Dialog { anchors.margins: 8 KeyNavigation.tab: password KeyNavigation.backtab: password - onAccepted: { - password.forceActiveFocus() - } } } @@ -78,13 +75,6 @@ Dialog { anchors.margins: 8 KeyNavigation.tab: username KeyNavigation.backtab: username - onAccepted: { - if (username.text == "") { - username.forceActiveFocus() - } else { - loginDialog.login(username.text, password.text) - } - } onFocusChanged: { if (password.focus) { password.selectAll() @@ -187,4 +177,22 @@ Dialog { } } } + Keys.onPressed: { + switch(event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + if (username.activeFocus) { + event.accepted = true + password.forceActiveFocus() + } else if (password.activeFocus) { + event.accepted = true + if (username.text == "") { + username.forceActiveFocus() + } else { + loginDialog.login(username.text, password.text) + } + } + break; + } + } } diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index a2a5ba9304..02f1e4716f 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -3,71 +3,51 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import "controls" +import "styles" Dialog { id: root - property real spacing: 8 - property real outerSpacing: 16 - + HifiConstants { id: hifi } + property real spacing: hifi.layout.spacing + property real outerSpacing: hifi.layout.spacing * 2 destroyOnCloseButton: true destroyOnInvisible: true - implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 - implicitWidth: Math.min(200, Math.max(mainText.implicitWidth, content.buttonsRowImplicitWidth) + outerSpacing * 2); + contentImplicitWidth: content.implicitWidth + contentImplicitHeight: content.implicitHeight - onImplicitHeightChanged: root.height = implicitHeight - onImplicitWidthChanged: root.width = implicitWidth - - function calculateImplicitWidth() { - if (buttons.visibleChildren.length < 2) - return; - var calcWidth = 0; - for (var i = 0; i < buttons.visibleChildren.length; ++i) { - calcWidth += Math.max(100, buttons.visibleChildren[i].implicitWidth) + root.spacing - } - content.buttonsRowImplicitWidth = outerSpacing + calcWidth + 48 - } - Component.onCompleted: { enabled = true } - - onEnabledChanged: { - if (enabled) { - root.forceActiveFocus(); + + onParentChanged: { + if (visible && enabled) { + forceActiveFocus(); } } Hifi.MessageDialog { id: content clip: true - anchors.fill: parent - anchors.topMargin: parent.topMargin + root.outerSpacing - anchors.leftMargin: parent.margins + root.outerSpacing - anchors.rightMargin: parent.margins + root.outerSpacing - anchors.bottomMargin: parent.margins + root.outerSpacing - implicitHeight: contentColumn.implicitHeight + outerSpacing * 2 - implicitWidth: Math.max(mainText.implicitWidth, buttonsRowImplicitWidth); - property real buttonsRowImplicitWidth: Screen.pixelDensity * 50 - onImplicitWidthChanged: root.width = implicitWidth + x: root.clientX + y: root.clientY + implicitHeight: contentColumn.implicitHeight + outerSpacing * 2 + implicitWidth: mainText.implicitWidth + outerSpacing * 2 Component.onCompleted: { root.title = title } - + onTitleChanged: { root.title = title } - + Column { + anchors.fill: parent + anchors.margins: 8 id: contentColumn spacing: root.outerSpacing - anchors { - top: parent.top - left: parent.left - right: parent.right - } Item { width: parent.width @@ -83,7 +63,7 @@ Dialog { horizontalAlignment: Text.AlignLeft color: iconColor() text: iconSymbol() - + function iconSymbol() { switch (content.icon) { case Hifi.MessageDialog.Information: @@ -262,7 +242,6 @@ Dialog { onClicked: content.click(StandardButton.Help) visible: content.standardButtons & StandardButton.Help } - onVisibleChildrenChanged: root.calculateImplicitWidth() } } @@ -319,6 +298,7 @@ Dialog { ] } + Keys.onPressed: { if (event.modifiers === Qt.ControlModifier) switch (event.key) { diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 01714e9ebe..01a726d2ba 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -50,6 +50,7 @@ Hifi.VrMenu { property var menuBuilder: Component { Border { + HifiConstants { id: hifi } Component.onCompleted: { menuDepth = root.models.length - 1 if (menuDepth == 0) { @@ -64,207 +65,181 @@ Hifi.VrMenu { border.color: hifi.colors.hifiBlue color: hifi.colors.window property int menuDepth -/* - MouseArea { -// Rectangle { anchors.fill: parent; color: "#7f0000FF"; visible: enabled } - anchors.fill: parent - onClicked: { - while (parent.menuDepth != root.models.length - 1) { - root.popColumn() - } - } - } -*/ + implicitHeight: listView.implicitHeight + 16 + implicitWidth: listView.implicitWidth + 16 - ListView { - spacing: 6 - property int outerMargin: 8 - property real minWidth: 0 - anchors.fill: parent - anchors.margins: outerMargin + Column { id: listView - height: root.height - - onCountChanged: { - recalculateSize() + property real minWidth: 0 + anchors { + top: parent.top + topMargin: 8 + left: parent.left + leftMargin: 8 + right: parent.right + rightMargin: 8 } - function recalculateSize() { - var newHeight = 0 - var newWidth = minWidth; - for (var i = 0; i < children.length; ++i) { - var item = children[i]; - if (!item.visible) { - continue - } - newHeight += item.height + Repeater { + model: root.models[menuDepth] + delegate: Loader { + id: loader + sourceComponent: root.itemBuilder + Binding { + target: loader.item + property: "root" + value: root + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "source" + value: modelData + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "border" + value: listView.parent + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "listView" + value: listView + when: loader.status == Loader.Ready + } } - parent.height = newHeight + outerMargin * 2; - parent.width = newWidth + outerMargin * 2 - } - - highlight: Rectangle { - width: listView.minWidth - 32; - height: 32 - color: hifi.colors.hifiBlue - y: (listView.currentItem) ? listView.currentItem.y : 0; - x: 32 - Behavior on y { - NumberAnimation { - duration: 100 - easing.type: Easing.InOutQuint - } - } - } - - model: root.models[menuDepth] - delegate: Loader { - id: loader - sourceComponent: root.itemBuilder - Binding { - target: loader.item - property: "root" - value: root - when: loader.status == Loader.Ready - } - Binding { - target: loader.item - property: "source" - value: modelData - when: loader.status == Loader.Ready - } - Binding { - target: loader.item - property: "listView" - value: listView - when: loader.status == Loader.Ready - } } } } } property var itemBuilder: Component { - Text { - id: thisText - x: 32 + Item { property var source property var root property var listView - text: typedText() - height: implicitHeight - width: implicitWidth - color: source.enabled ? hifi.colors.text : hifi.colors.disabledText - enabled: source.enabled && source.visible + property var border + implicitHeight: row.implicitHeight + 4 + implicitWidth: row.implicitWidth + label.height // FIXME uncommenting this line results in menus that have blank spots // rather than having the correct size // visible: source.visible - - onListViewChanged: { - if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); - listView.recalculateSize(); + Row { + id: row + spacing: 4 + anchors { + top: parent.top + topMargin: 2 } - } + FontAwesome { + id: check + size: label.height + text: checkText() + color: label.color + function checkText() { + if (!source || source.type != 1 || !source.checkable) { + return ""; + } - onVisibleChanged: { - if (listView) { - listView.recalculateSize(); - } - } - - onImplicitWidthChanged: { - if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); - listView.recalculateSize(); - } - } - - FontAwesome { - visible: source.type == 1 && source.checkable - x: -32 - text: checkText(); - color: parent.color - function checkText() { - if (source.type != 1) { - return; + // FIXME this works for native QML menus but I don't think it will + // for proxied QML menus + if (source.exclusiveGroup) { + return source.checked ? "\uF05D" : "\uF10C" + } + return source.checked ? "\uF046" : "\uF096" } - // FIXME this works for native QML menus but I don't think it will - // for proxied QML menus - if (source.exclusiveGroup) { - return source.checked ? "\uF05D" : "\uF10C" - } - return source.checked ? "\uF046" : "\uF096" } - } + Text { + id: label + text: typedText() + color: source.enabled ? hifi.colors.text : hifi.colors.disabledText + enabled: source.enabled && source.visible + function typedText() { + if (source) { + switch(source.type) { + case 2: + return source.title; + case 1: + return source.text; + case 0: + return "-----" + } + } + return "" + } + } + } // row - FontAwesome { - visible: source.type == 2 - x: listView.width - 32 - (hifi.layout.spacing * 2) - text: "\uF0DA" - color: parent.color - } - - function typedText() { - switch(source.type) { - case 2: - return source.title; - case 1: - return source.text; - case 0: - return "-----" - } - } + FontAwesome { + anchors { + top: row.top + } + id: tag + size: label.height + width: implicitWidth + visible: source.type == 2 + x: listView.width - width - 4 + text: "\uF0DA" + color: label.color + } - MouseArea { - id: mouseArea - acceptedButtons: Qt.LeftButton - anchors.left: parent.left - anchors.leftMargin: -32 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - width: listView.width - hoverEnabled: true - Timer { - id: timer - interval: 1000 - onTriggered: parent.select(); - } - /* - * Uncomment below to have menus auto-popup - * - * FIXME if we enabled timer based menu popup, either the timer has - * to be very very short or after auto popup there has to be a small - * amount of time, or a test if the mouse has moved before a click - * will be accepted, otherwise it's too easy to accidently click on - * something immediately after the auto-popup appears underneath your - * cursor - * - */ - //onEntered: { - // if (source.type == 2 && enabled) { - // timer.start() - // } - //} - //onExited: { - // timer.stop() - //} - onClicked: { - select(); - } - function select() { - timer.stop(); - var popped = false; - while (columns.length - 1 > listView.parent.menuDepth) { - popColumn(); - popped = true; - } + MouseArea { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: tag.right + } + acceptedButtons: Qt.LeftButton + hoverEnabled: true + Rectangle { + id: highlight + visible: false + anchors.fill: parent + color: "#7f0e7077" + } + Timer { + id: timer + interval: 1000 + onTriggered: parent.select(); + } + onEntered: { + /* + * Uncomment below to have menus auto-popup + * + * FIXME if we enabled timer based menu popup, either the timer has + * to be very very short or after auto popup there has to be a small + * amount of time, or a test if the mouse has moved before a click + * will be accepted, otherwise it's too easy to accidently click on + * something immediately after the auto-popup appears underneath your + * cursor + * + */ + //if (source.type == 2 && enabled) { + // timer.start() + //} + highlight.visible = source.enabled + } + onExited: { + timer.stop() + highlight.visible = false + } + onClicked: { + select(); + } + function select() { + //timer.stop(); + var popped = false; + while (columns.length - 1 > listView.parent.menuDepth) { + popColumn(); + popped = true; + } - if (!popped || source.type != 1) { - parent.root.selectItem(parent.source); - } + if (!popped || source.type != 1) { + parent.root.selectItem(parent.source); + } } } } diff --git a/interface/resources/qml/controls/DialogBase.qml b/interface/resources/qml/controls/DialogBase.qml index e5f0a8f0d1..050237c184 100644 --- a/interface/resources/qml/controls/DialogBase.qml +++ b/interface/resources/qml/controls/DialogBase.qml @@ -6,13 +6,15 @@ import "../styles" Item { id: root HifiConstants { id: hifi } - implicitHeight: 512 - implicitWidth: 512 + implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth + implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2 property string title property int titleSize: titleBorder.height + 12 property string frameColor: hifi.colors.hifiBlue property string backgroundColor: hifi.colors.dialogBackground property bool active: false + property real contentImplicitWidth: 800 + property real contentImplicitHeight: 800 property alias titleBorder: titleBorder readonly property alias titleX: titleBorder.x diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5e78413126..d1932ae691 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -182,8 +182,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; -bool renderCollisionHulls = false; - #ifdef Q_OS_WIN class MyNativeEventFilter : public QAbstractNativeEventFilter { public: @@ -1314,11 +1312,6 @@ void Application::keyPressEvent(QKeyEvent* event) { break; } - case Qt::Key_Comma: { - renderCollisionHulls = !renderCollisionHulls; - break; - } - default: event->ignore(); break; @@ -3157,13 +3150,21 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs PerformanceTimer perfTimer("entities"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... entities..."); - if (renderCollisionHulls) { - _entities.render(RenderArgs::DEBUG_RENDER_MODE, renderSide); - } else if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - _entities.render(RenderArgs::MIRROR_RENDER_MODE, renderSide); - } else { - _entities.render(RenderArgs::DEFAULT_RENDER_MODE, renderSide); + + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; + RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE; + + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { + renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS); } + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { + renderDebugFlags = + (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); + } + if (theCamera.getMode() == CAMERA_MODE_MIRROR) { + renderMode = RenderArgs::MIRROR_RENDER_MODE; + } + _entities.render(renderMode, renderSide, renderDebugFlags); } // render JS/scriptable overlays diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 42670c2979..c467658fc4 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -548,6 +548,11 @@ Menu::Menu() { statsRenderer.data(), SLOT(toggleShowInjectedStreams())); + + MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics"); + addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned); + addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); + MenuWrapper* helpMenu = addMenu("Help"); addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 194f64ddc7..5b2d1430a6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -216,6 +216,8 @@ namespace MenuOption { const QString OnlyDisplayTopTen = "Only Display Top Ten"; const QString PackageModel = "Package Model..."; const QString Pair = "Pair"; + const QString PhysicsShowOwned = "Highlight Simulation Ownership"; + const QString PhysicsShowHulls = "Draw Collision Hulls"; const QString PipelineWarnings = "Log Render Pipeline Warnings"; const QString Preferences = "Preferences..."; const QString Quit = "Quit"; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 191f3f4a99..a9eb9184dd 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -90,7 +90,7 @@ void Overlays::renderHUD() { RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), - RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, + RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; foreach(Overlay* thisOverlay, _overlaysHUD) { @@ -108,7 +108,10 @@ void Overlays::renderHUD() { } } -void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { +void Overlays::renderWorld(bool drawFront, + RenderArgs::RenderMode renderMode, + RenderArgs::RenderSide renderSide, + RenderArgs::DebugFlags renderDebugFlags) { QReadLocker lock(&_lock); if (_overlaysWorld.size() == 0) { return; @@ -125,7 +128,7 @@ void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, Re RenderArgs args = { NULL, Application::getInstance()->getDisplayViewFrustum(), lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), - renderMode, renderSide, + renderMode, renderSide, renderDebugFlags, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 42227d04f6..04e306097b 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -54,7 +54,8 @@ public: void init(); void update(float deltatime); void renderWorld(bool drawFront, RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::RenderSide renderSide = RenderArgs::MONO); + RenderArgs::RenderSide renderSide = RenderArgs::MONO, + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE); void renderHUD(); public slots: diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 029161dec4..c9a968a58e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -383,7 +383,9 @@ void EntityTreeRenderer::leaveAllEntities() { _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); } } -void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { +void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, + RenderArgs::RenderSide renderSide, + RenderArgs::DebugFlags renderDebugFlags) { if (_tree && !_shuttingDown) { Model::startScene(renderSide); @@ -391,8 +393,7 @@ void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::R _viewState->getShadowViewFrustum() : _viewState->getCurrentViewFrustum(); RenderArgs args = { this, frustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - + renderDebugFlags, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; _tree->lockForRead(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 8f720e5e48..20534c3e2b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -58,7 +58,8 @@ public: virtual void init(); virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::RenderSide renderSide = RenderArgs::MONO); + RenderArgs::RenderSide renderSide = RenderArgs::MONO, + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE); virtual const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem); virtual const Model* getModelForEntityItem(const EntityItem* entityItem); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8ca6562505..b9bf1d39a8 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -118,8 +118,15 @@ void RenderableModelEntityItem::render(RenderArgs* args) { glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); float size = glm::length(dimensions); - - if (drawAsModel) { + + bool highlightSimulationOwnership = false; + if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { + auto nodeList = DependencyManager::get(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + highlightSimulationOwnership = (getSimulatorID() == myNodeID); + } + + if (drawAsModel && !highlightSimulationOwnership) { remapTextures(); glPushMatrix(); { diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 65d4849b0e..5552013ca2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -308,6 +308,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef glm::vec3 savePosition = _position; glm::quat saveRotation = _rotation; glm::vec3 saveVelocity = _velocity; + glm::vec3 saveAngularVelocity = _angularVelocity; glm::vec3 saveGravity = _gravity; glm::vec3 saveAcceleration = _acceleration; @@ -592,7 +593,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef #ifdef WANT_DEBUG qCDebug(entities) << "skipTimeForward:" << skipTimeForward; #endif - simulateKinematicMotion(skipTimeForward); + + // we want to extrapolate the motion forward to compensate for packet travel time, but + // we don't want the side effect of flag setting. + simulateKinematicMotion(skipTimeForward, false); } _lastSimulated = now; } @@ -607,6 +611,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef _position = savePosition; _rotation = saveRotation; _velocity = saveVelocity; + _angularVelocity = saveAngularVelocity; _gravity = saveGravity; _acceleration = saveAcceleration; } @@ -725,7 +730,7 @@ void EntityItem::simulate(const quint64& now) { _lastSimulated = now; } -void EntityItem::simulateKinematicMotion(float timeElapsed) { +void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { if (hasAngularVelocity()) { // angular damping if (_angularDamping > 0.0f) { @@ -740,7 +745,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (angularSpeed > 0.0f) { + if (setFlags && angularSpeed > 0.0f) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } _angularVelocity = ENTITY_ITEM_ZERO_VEC3; @@ -802,7 +807,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); - if (speed > 0.0f) { + if (setFlags && speed > 0.0f) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } else { @@ -1080,6 +1085,7 @@ const float MIN_ALIGNMENT_DOT = 0.999999f; const float MIN_VELOCITY_DELTA = 0.01f; const float MIN_DAMPING_DELTA = 0.001f; const float MIN_GRAVITY_DELTA = 0.001f; +const float MIN_ACCELERATION_DELTA = 0.001f; const float MIN_SPIN_DELTA = 0.0003f; void EntityItem::updatePositionInDomainUnits(const glm::vec3& value) { @@ -1088,9 +1094,10 @@ void EntityItem::updatePositionInDomainUnits(const glm::vec3& value) { } void EntityItem::updatePosition(const glm::vec3& value) { - if (glm::distance(_position, value) > MIN_POSITION_DELTA) { + if (value != _position) { + auto distance = glm::distance(_position, value); + _dirtyFlags |= (distance > MIN_POSITION_DELTA) ? EntityItem::DIRTY_POSITION : EntityItem::DIRTY_PHYSICS_NO_WAKE; _position = value; - _dirtyFlags |= EntityItem::DIRTY_POSITION; } } @@ -1107,9 +1114,10 @@ void EntityItem::updateDimensions(const glm::vec3& value) { } void EntityItem::updateRotation(const glm::quat& rotation) { - if (glm::dot(_rotation, rotation) < MIN_ALIGNMENT_DOT) { - _rotation = rotation; - _dirtyFlags |= EntityItem::DIRTY_POSITION; + if (rotation != _rotation) { + auto alignmentDot = glm::abs(glm::dot(_rotation, rotation)); + _dirtyFlags |= (alignmentDot < MIN_ALIGNMENT_DOT) ? EntityItem::DIRTY_POSITION : EntityItem::DIRTY_PHYSICS_NO_WAKE; + _rotation = rotation; } } @@ -1166,21 +1174,28 @@ void EntityItem::updateGravityInDomainUnits(const glm::vec3& value) { } void EntityItem::updateGravity(const glm::vec3& value) { - if ( glm::distance(_gravity, value) > MIN_GRAVITY_DELTA) { + if (glm::distance(_gravity, value) > MIN_GRAVITY_DELTA) { _gravity = value; _dirtyFlags |= EntityItem::DIRTY_VELOCITY; } } void EntityItem::updateAcceleration(const glm::vec3& value) { - _acceleration = value; - _dirtyFlags |= EntityItem::DIRTY_VELOCITY; + if (glm::distance(_acceleration, value) > MIN_ACCELERATION_DELTA) { + _acceleration = value; + _dirtyFlags |= EntityItem::DIRTY_VELOCITY; + } } void EntityItem::updateAngularVelocity(const glm::vec3& value) { - if (glm::distance(_angularVelocity, value) > MIN_SPIN_DELTA) { - _angularVelocity = value; + auto distance = glm::distance(_angularVelocity, value); + if (distance > MIN_SPIN_DELTA) { _dirtyFlags |= EntityItem::DIRTY_VELOCITY; + if (glm::length(value) < MIN_SPIN_DELTA) { + _angularVelocity = ENTITY_ITEM_ZERO_VEC3; + } else { + _angularVelocity = value; + } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index fda8167564..890134828a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -54,7 +54,8 @@ public: DIRTY_MOTION_TYPE = 0x0010, DIRTY_SHAPE = 0x0020, DIRTY_LIFETIME = 0x0040, - DIRTY_UPDATEABLE = 0x0080 + DIRTY_UPDATEABLE = 0x0080, + DIRTY_PHYSICS_NO_WAKE = 0x0100 // we want to update values in physics engine without "waking" the object up }; DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly @@ -132,7 +133,7 @@ public: // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); - void simulateKinematicMotion(float timeElapsed); + void simulateKinematicMotion(float timeElapsed, bool setFlags=true); virtual bool needsToCallUpdate() const { return false; } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index b1e92e2140..059c0e5bbc 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -380,9 +380,9 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch } // if this is the root, and there is more data to read, allow it to read it's element data... - if (destinationElement == _rootElement && rootElementHasData() && (bytesLeftToRead - bytesRead) > 0) { + if (destinationElement == _rootElement && rootElementHasData() && bytesLeftToRead > 0) { // tell the element to read the subsequent data - int rootDataSize = _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead - bytesRead, args); + int rootDataSize = _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead, args); bytesRead += rootDataSize; bytesLeftToRead -= rootDataSize; } diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 6629d9ceb7..4347934d87 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -164,9 +164,11 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { return false; } -void OctreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { +void OctreeRenderer::render(RenderArgs::RenderMode renderMode, + RenderArgs::RenderSide renderSide, + RenderArgs::DebugFlags renderDebugFlags) { RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + renderDebugFlags, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 4ee0865243..ca0914723f 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -52,7 +52,8 @@ public: /// render the content of the octree virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::RenderSide renderSide = RenderArgs::MONO); + RenderArgs::RenderSide renderSide = RenderArgs::MONO, + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE); ViewFrustum* getViewFrustum() const { return _viewFrustum; } void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d1571fbcc5..6338736a19 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -38,7 +38,8 @@ void EntityMotionState::enqueueOutgoingEntity(EntityItem* entity) { EntityMotionState::EntityMotionState(EntityItem* entity) : _entity(entity), _accelerationNearlyGravityCount(0), - _shouldClaimSimulationOwnership(false) + _shouldClaimSimulationOwnership(false), + _movingStepsWithoutSimulationOwner(0) { _type = MOTION_STATE_TYPE_ENTITY; assert(entity != NULL); @@ -108,11 +109,23 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { getVelocity(v); _entity->setVelocity(v); - getAngularVelocity(v); - _entity->setAngularVelocity(v); + glm::vec3 av; + getAngularVelocity(av); + _entity->setAngularVelocity(av); _entity->setLastSimulated(usecTimestampNow()); + if (_entity->getSimulatorID().isNull() && isMoving()) { + // object is moving and has no owner. attempt to claim simulation ownership. + _movingStepsWithoutSimulationOwner++; + } else { + _movingStepsWithoutSimulationOwner = 0; + } + + if (_movingStepsWithoutSimulationOwner > 4) { // XXX maybe meters from our characterController ? + setShouldClaimSimulationOwnership(true); + } + _outgoingPacketFlags = DIRTY_PHYSICS_FLAGS; EntityMotionState::enqueueOutgoingEntity(_entity); @@ -126,7 +139,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { } void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) { - if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { + if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY | EntityItem::DIRTY_PHYSICS_NO_WAKE)) { if (flags & EntityItem::DIRTY_POSITION) { _sentPosition = _entity->getPosition() - ObjectMotionState::getWorldOffset(); btTransform worldTrans; @@ -141,6 +154,10 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) { updateObjectVelocities(); } _sentStep = step; + + if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { + _body->activate(); + } } // TODO: entity support for friction and restitution @@ -160,7 +177,6 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t step) { _body->setMassProps(mass, inertia); _body->updateInertiaTensor(); } - _body->activate(); }; void EntityMotionState::updateObjectVelocities() { @@ -287,13 +303,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ QUuid simulatorID = _entity->getSimulatorID(); if (getShouldClaimSimulationOwnership()) { - _entity->setSimulatorID(myNodeID); properties.setSimulatorID(myNodeID); setShouldClaimSimulationOwnership(false); - } - - if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { - // we are the simulator and the object has stopped. give up "simulator" status + } else if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { + // we are the simulator and the entity has stopped. give up "simulator" status + _entity->setSimulatorID(QUuid()); + properties.setSimulatorID(QUuid()); + } else if (simulatorID == myNodeID && !_body->isActive()) { + // it's not active. don't keep simulation ownership. _entity->setSimulatorID(QUuid()); properties.setSimulatorID(QUuid()); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 07f82aaa42..087d5b49b9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -74,6 +74,7 @@ protected: EntityItem* _entity; quint8 _accelerationNearlyGravityCount; bool _shouldClaimSimulationOwnership; + quint32 _movingStepsWithoutSimulationOwner; }; #endif // hifi_EntityMotionState_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 45f3c97e30..27ea816440 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -119,8 +119,10 @@ void PhysicsEngine::entityChangedInternal(EntityItem* entity) { assert(entity); void* physicsInfo = entity->getPhysicsInfo(); if (physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - _incomingChanges.insert(motionState); + if ((entity->getDirtyFlags() & (HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS)) > 0) { + ObjectMotionState* motionState = static_cast(physicsInfo); + _incomingChanges.insert(motionState); + } } else { // try to add this entity again (maybe something changed such that it will work this time) addEntity(entity); @@ -366,15 +368,46 @@ void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { } } -void PhysicsEngine::computeCollisionEvents() { - BT_PROFILE("computeCollisionEvents"); + +void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { + assert(objectA); + assert(objectB); auto nodeList = DependencyManager::get(); QUuid myNodeID = nodeList->getSessionUUID(); - const btCollisionObject* characterCollisionObject = _characterController ? _characterController->getCollisionObject() : NULL; + assert(!myNodeID.isNull()); + + ObjectMotionState* a = static_cast(objectA->getUserPointer()); + ObjectMotionState* b = static_cast(objectB->getUserPointer()); + EntityItem* entityA = a ? a->getEntity() : NULL; + EntityItem* entityB = b ? b->getEntity() : NULL; + bool aIsDynamic = entityA && !objectA->isStaticOrKinematicObject(); + bool bIsDynamic = entityB && !objectB->isStaticOrKinematicObject(); + + // collisions cause infectious spread of simulation-ownership. we also attempt to take + // ownership of anything that collides with our avatar. + if ((aIsDynamic && (entityA->getSimulatorID() == myNodeID)) || + // (a && a->getShouldClaimSimulationOwnership()) || + (objectA == characterCollisionObject)) { + if (bIsDynamic) { + b->setShouldClaimSimulationOwnership(true); + } + } else if ((bIsDynamic && (entityB->getSimulatorID() == myNodeID)) || + // (b && b->getShouldClaimSimulationOwnership()) || + (objectB == characterCollisionObject)) { + if (aIsDynamic) { + a->setShouldClaimSimulationOwnership(true); + } + } +} + + +void PhysicsEngine::computeCollisionEvents() { + BT_PROFILE("computeCollisionEvents"); + // update all contacts every frame int numManifolds = _collisionDispatcher->getNumManifolds(); for (int i = 0; i < numManifolds; ++i) { @@ -393,31 +426,12 @@ void PhysicsEngine::computeCollisionEvents() { ObjectMotionState* a = static_cast(objectA->getUserPointer()); ObjectMotionState* b = static_cast(objectB->getUserPointer()); - EntityItem* entityA = a ? a->getEntity() : NULL; - EntityItem* entityB = b ? b->getEntity() : NULL; - bool aIsDynamic = entityA && !objectA->isStaticOrKinematicObject(); - bool bIsDynamic = entityB && !objectB->isStaticOrKinematicObject(); - if (a || b) { // the manifold has up to 4 distinct points, but only extract info from the first _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0), _originOffset); } - // collisions cause infectious spread of simulation-ownership. we also attempt to take - // ownership of anything that collides with our avatar. - if ((aIsDynamic && entityA->getSimulatorID() == myNodeID) || - (a && a->getShouldClaimSimulationOwnership()) || - (objectA == characterCollisionObject)) { - if (bIsDynamic) { - b->setShouldClaimSimulationOwnership(true); - } - } - if ((bIsDynamic && entityB->getSimulatorID() == myNodeID) || - (b && b->getShouldClaimSimulationOwnership()) || - (objectB == characterCollisionObject)) { - if (aIsDynamic) { - a->setShouldClaimSimulationOwnership(true); - } - } + + doOwnershipInfection(objectA, objectB); } } @@ -576,11 +590,9 @@ void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); - // set the about-to-be-deleted entity active in order to wake up the island it's part of. this is done - // so that anything resting on top of it will fall. - // body->setActivationState(ACTIVE_TAG); - EntityItem* entity = static_cast(motionState)->getEntity(); - bump(entity); + // wake up anything touching this object + EntityItem* entityItem = motionState ? motionState->getEntity() : NULL; + bump(entityItem); if (body) { const btCollisionShape* shape = body->getCollisionShape(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 148261c6d2..9fe632d462 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -89,16 +89,19 @@ public: void dumpNextStats() { _dumpNextStats = true; } + void bump(EntityItem* bumpEntity); + private: /// \param motionState pointer to Object's MotionState void removeObjectFromBullet(ObjectMotionState* motionState); void removeContacts(ObjectMotionState* motionState); + void doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB); + // return 'true' of update was successful bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); - void bump(EntityItem* bumpEntity); btClock _clock; btDefaultCollisionConfiguration* _collisionConfig = NULL; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index fe82af6b3c..3508570d6a 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1947,14 +1947,14 @@ bool Model::renderInScene(float alpha, RenderArgs* args) { return false; } - if (args->_renderMode == RenderArgs::DEBUG_RENDER_MODE && _renderCollisionHull == false) { + if (args->_debugFlags == RenderArgs::RENDER_DEBUG_HULLS && _renderCollisionHull == false) { // turning collision hull rendering on _renderCollisionHull = true; _nextGeometry = _collisionGeometry; _saveNonCollisionGeometry = _geometry; updateGeometry(); simulate(0.0, true); - } else if (args->_renderMode != RenderArgs::DEBUG_RENDER_MODE && _renderCollisionHull == true) { + } else if (args->_debugFlags != RenderArgs::RENDER_DEBUG_HULLS && _renderCollisionHull == true) { // turning collision hull rendering off _renderCollisionHull = false; _nextGeometry = _saveNonCollisionGeometry; diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 116fc430c2..bc3e2edb6d 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -17,16 +17,23 @@ class OctreeRenderer; class RenderArgs { public: - enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, DEBUG_RENDER_MODE }; + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT }; + enum DebugFlags { + RENDER_DEBUG_NONE=0, + RENDER_DEBUG_HULLS=1, + RENDER_DEBUG_SIMULATION_OWNERSHIP=2 + }; + OctreeRenderer* _renderer; ViewFrustum* _viewFrustum; float _sizeScale; int _boundaryLevelAdjust; RenderMode _renderMode; RenderSide _renderSide; + DebugFlags _debugFlags; int _elementsTouched; int _itemsRendered; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 5db7349a02..8ba7b514e4 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -124,9 +124,9 @@ void OffscreenUi::resize(const QSize& newSize) { if (_quickWindow) { _quickWindow->setGeometry(QRect(QPoint(), newSize)); + _quickWindow->contentItem()->setSize(newSize); } - _quickWindow->contentItem()->setSize(newSize); // Update our members if (_rootItem) { diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 5e45bf23a2..0ba7416b28 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -205,7 +205,7 @@ void QTestWindow::renderText() { { size.x + QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x + QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, { QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, - }; + }; QString str = QString::fromWCharArray(EXAMPLE_TEXT); for (int i = 0; i < 4; ++i) {