From e38b0ab6b82b96cd2c6a85172634ee60feb68e9c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 Nov 2017 17:09:45 -0800 Subject: [PATCH 01/52] make web overlays match web entities more, remove resolution --- interface/src/ui/overlays/Web3DOverlay.cpp | 66 ++++++++++----------- interface/src/ui/overlays/Web3DOverlay.h | 5 +- scripts/system/libraries/WebTablet.js | 24 +++++--- scripts/system/libraries/touchEventUtils.js | 30 ++-------- scripts/system/libraries/utils.js | 13 ++-- 5 files changed, 61 insertions(+), 77 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d418a79fbf..f096c50a03 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -56,14 +56,15 @@ #include "ui/Snapshot.h" #include "SoundCache.h" -static const float DPI = 30.47f; +static int MAX_WINDOW_SIZE = 4096; + static const float INCHES_TO_METERS = 1.0f / 39.3701f; static const float METERS_TO_INCHES = 39.3701f; static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; const QString Web3DOverlay::TYPE = "web3d"; const QString Web3DOverlay::QML = "Web3DOverlay.qml"; -Web3DOverlay::Web3DOverlay() : _dpi(DPI) { +Web3DOverlay::Web3DOverlay() { _touchDevice.setCapabilities(QTouchDevice::Position); _touchDevice.setType(QTouchDevice::TouchScreen); _touchDevice.setName("RenderableWebEntityItemTouchDevice"); @@ -80,7 +81,6 @@ Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : _url(Web3DOverlay->_url), _scriptURL(Web3DOverlay->_scriptURL), _dpi(Web3DOverlay->_dpi), - _resolution(Web3DOverlay->_resolution), _showKeyboardFocusHighlight(Web3DOverlay->_showKeyboardFocusHighlight) { _geometryId = DependencyManager::get()->allocateID(); @@ -152,7 +152,7 @@ void Web3DOverlay::buildWebSurface() { setupQmlSurface(); } _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); - _webSurface->resize(QSize(_resolution.x, _resolution.y)); + onResizeWebSurface(); _webSurface->resume(); }); @@ -249,8 +249,16 @@ void Web3DOverlay::setMaxFPS(uint8_t maxFPS) { } void Web3DOverlay::onResizeWebSurface() { - _mayNeedResize = false; - _webSurface->resize(QSize(_resolution.x, _resolution.y)); + glm::vec2 dims = glm::vec2(getDimensions()); + dims *= METERS_TO_INCHES * _dpi; + + // ensure no side is never larger then MAX_WINDOW_SIZE + float max = (dims.x > dims.y) ? dims.x : dims.y; + if (max > MAX_WINDOW_SIZE) { + dims *= MAX_WINDOW_SIZE / max; + } + + _webSurface->resize(QSize(dims.x, dims.y)); } const int INVALID_DEVICE_ID = -1; @@ -277,14 +285,14 @@ void Web3DOverlay::render(RenderArgs* args) { return; } - if (_currentMaxFPS != _desiredMaxFPS) { - setMaxFPS(_desiredMaxFPS); - } - if (_mayNeedResize) { emit resizeWebSurface(); } + if (_currentMaxFPS != _desiredMaxFPS) { + setMaxFPS(_desiredMaxFPS); + } + vec4 color(toGlm(getColor()), getAlpha()); if (!_texture) { @@ -321,7 +329,7 @@ void Web3DOverlay::render(RenderArgs* args) { Transform Web3DOverlay::evalRenderTransform() { Transform transform = Parent::evalRenderTransform(); transform.setScale(1.0f); - transform.postScale(glm::vec3(getSize(), 1.0f)); + transform.postScale(glm::vec3(getDimensions(), 1.0f)); return transform; } @@ -520,18 +528,10 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { } } - auto resolution = properties["resolution"]; - if (resolution.isValid()) { - bool valid; - auto res = vec2FromVariant(resolution, valid); - if (valid) { - _resolution = res; - } - } - auto dpi = properties["dpi"]; if (dpi.isValid()) { _dpi = dpi.toFloat(); + _mayNeedResize = true; } auto maxFPS = properties["maxFPS"]; @@ -553,8 +553,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { _inputMode = Touch; } } - - _mayNeedResize = true; } QVariant Web3DOverlay::getProperty(const QString& property) { @@ -564,9 +562,6 @@ QVariant Web3DOverlay::getProperty(const QString& property) { if (property == "scriptURL") { return _scriptURL; } - if (property == "resolution") { - return vec2toVariant(_resolution); - } if (property == "dpi") { return _dpi; } @@ -622,17 +617,18 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { } } -glm::vec2 Web3DOverlay::getSize() const { - return _resolution / _dpi * INCHES_TO_METERS * getDimensions(); -}; - bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { - // FIXME - face and surfaceNormal not being returned + glm::vec2 dimensions = getDimensions(); + glm::quat rotation = getRotation(); + glm::vec3 position = getPosition(); - // Don't call applyTransformTo() or setTransform() here because this code runs too frequently. - - // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. - return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getSize(), distance); + if (findRayRectangleIntersection(origin, direction, rotation, position, dimensions, distance)) { + surfaceNormal = rotation * Vectors::UNIT_Z; + face = glm::dot(surfaceNormal, direction) > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } else { + return false; + } } Web3DOverlay* Web3DOverlay::createClone() const { @@ -641,4 +637,4 @@ Web3DOverlay* Web3DOverlay::createClone() const { void Web3DOverlay::emitScriptEvent(const QVariant& message) { QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message)); -} +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 2fc63df76a..e138344877 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -51,8 +51,6 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - glm::vec2 getSize() const override; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; @@ -92,8 +90,7 @@ private: gpu::TexturePointer _texture; QString _url; QString _scriptURL; - float _dpi; - vec2 _resolution{ 640, 480 }; + float _dpi { 30 }; int _geometryId { 0 }; bool _showKeyboardFocusHighlight{ true }; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c46cfaa073..4b521f531b 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -118,15 +118,16 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { Overlays.deleteOverlay(this.webOverlayID); } - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * (1 / sensorScaleFactor); - var WEB_ENTITY_Y_OFFSET = 0.004 * (1 / sensorScaleFactor); - + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor; + var WEB_ENTITY_Y_OFFSET = 0.004; + var screenWidth = 0.82 * tabletWidth; + var screenHeight = 0.81 * tabletHeight; this.webOverlayID = Overlays.addOverlay("web3d", { name: "WebTablet Web", url: url, localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, localRotation: Quat.angleAxis(180, Y_AXIS), - resolution: this.getTabletTextureResolution(), + dimensions: {x: screenWidth, y: screenHeight, z: 0.1}, dpi: tabletDpi, color: { red: 255, green: 255, blue: 255 }, alpha: 1.0, @@ -139,7 +140,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor); this.homeButtonID = Overlays.addOverlay("sphere", { name: "homeButton", - localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, + localPosition: {x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, localRotation: {x: 0, y: 1, z: 0, w: 0}, dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor}, alpha: 0.0, @@ -266,11 +267,16 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) { this.landscape = newLandscapeValue; Overlays.editOverlay(this.tabletEntityID, - { rotation: this.landscape ? Quat.multiply(Camera.orientation, ROT_LANDSCAPE) : - Quat.multiply(Camera.orientation, ROT_Y_180) }); + { rotation: Quat.multiply(Camera.orientation, this.landscape ? ROT_LANDSCAPE : ROT_Y_180) }); + + var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale; + var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x; + var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor; + var screenWidth = 0.82 * tabletWidth; + var screenHeight = 0.81 * tabletHeight; Overlays.editOverlay(this.webOverlayID, { - resolution: this.getTabletTextureResolution(), - rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW) + rotation: Quat.multiply(Camera.orientation, ROT_LANDSCAPE_WINDOW), + dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1} }); }; diff --git a/scripts/system/libraries/touchEventUtils.js b/scripts/system/libraries/touchEventUtils.js index 3c76c4c144..53ac4f49d1 100644 --- a/scripts/system/libraries/touchEventUtils.js +++ b/scripts/system/libraries/touchEventUtils.js @@ -169,31 +169,13 @@ function calculateTouchTargetFromOverlay(touchTip, overlayID) { // calclulate normalized position var invRot = Quat.inverse(overlayRotation); var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); - var dpi = Overlays.getProperty(overlayID, "dpi"); - var dimensions; - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property - // is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - if (resolution === undefined) { - return; - } - resolution.z = 1; // Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - if (scale === undefined) { - return; - } - scale.z = 0.01; // overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions === undefined) { - return; - } - if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. - } + var dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions === undefined) { + return; + } + if (!dimensions.z) { + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. } var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index 76c248d880..f03f286988 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -185,7 +185,7 @@ logTrace = function(str) { // (the vector that would move the point outside the sphere) // otherwise returns false findSphereHit = function(point, sphereRadius) { - var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations + var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations var vectorLength = Vec3.length(point); if (vectorLength < EPSILON) { return true; @@ -400,22 +400,25 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) }); // update webOverlay - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2) * sensorScaleOffsetOverride; - var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleOffsetOverride; + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride; + var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleFactor * sensorScaleOffsetOverride; + var screenWidth = 0.82 * tabletWidth; + var screenHeight = 0.81 * tabletHeight; Overlays.editOverlay(HMD.tabletScreenID, { localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + dimensions: {x: screenWidth, y: screenHeight, z: 0.1}, dpi: tabletDpi }); // update homeButton var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * sensorScaleOffsetOverride; Overlays.editOverlay(HMD.homeButtonID, { - localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, + localPosition: {x: 0, y: -HOME_BUTTON_Y_OFFSET, z: 0 }, dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor} }); Overlays.editOverlay(HMD.homeButtonHighlightID, { - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003 * sensorScaleFactor * sensorScaleOffsetOverride, z: -0.0158 * sensorScaleFactor * sensorScaleOffsetOverride }, dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor }, outerRadius: 25 * tabletScaleFactor, innerRadius: 20 * tabletScaleFactor From 393ad0079c4af2f09048aa5e18013129d4ba38de Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 17 Nov 2017 14:00:01 -0800 Subject: [PATCH 02/52] home button is circle instead of sphere, fixed positioning, fix lasers on tablet --- interface/src/ui/overlays/Web3DOverlay.cpp | 1 - scripts/system/libraries/WebTablet.js | 19 +++++++++++-------- .../libraries/controllerDispatcherUtils.js | 18 ++---------------- scripts/system/libraries/touchEventUtils.js | 4 +--- scripts/system/libraries/utils.js | 19 +++++++++---------- scripts/system/tablet-ui/tabletUI.js | 2 +- 6 files changed, 24 insertions(+), 39 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index f096c50a03..f8e20bcd32 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -58,7 +58,6 @@ static int MAX_WINDOW_SIZE = 4096; -static const float INCHES_TO_METERS = 1.0f / 39.3701f; static const float METERS_TO_INCHES = 39.3701f; static const float OPAQUE_ALPHA_THRESHOLD = 0.99f; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index cdc25fede3..01fdfb1845 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -137,12 +137,15 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { visible: visible }); - var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor); - this.homeButtonID = Overlays.addOverlay("sphere", { + var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor) - 0.003; + // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here + var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; + this.homeButtonID = Overlays.addOverlay("circle3d", { name: "homeButton", - localPosition: {x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, - localRotation: {x: 0, y: 1, z: 0, w: 0}, - dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor}, + localPosition: { x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + localRotation: { x: 0, y: 1, z: 0, w: 0}, + dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }, + solid: true, alpha: 0.0, visible: visible, drawInFront: false, @@ -152,14 +155,14 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { this.homeButtonHighlightID = Overlays.addOverlay("circle3d", { name: "homeButtonHighlight", - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, localRotation: { x: 0, y: 1, z: 0, w: 0 }, - dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor }, + dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim }, + color: { red: 255, green: 255, blue: 255 }, solid: true, innerRadius: 0.9, ignoreIntersection: true, alpha: 1.0, - color: { red: 255, green: 255, blue: 255 }, visible: visible, drawInFront: false, parentID: this.tabletEntityID, diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index fb6de0e683..33472d6581 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -255,22 +255,8 @@ projectOntoEntityXYPlane = function (entityID, worldPos, props) { projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) { var position = Overlays.getProperty(overlayID, "position"); var rotation = Overlays.getProperty(overlayID, "rotation"); - var dimensions; - - var dpi = Overlays.getProperty(overlayID, "dpi"); - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - resolution.z = 1; // Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - scale.z = 0.01; // overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions.z) { - dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. - } - } + var dimensions = Overlays.getProperty(overlayID, "dimensions"); + dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); }; diff --git a/scripts/system/libraries/touchEventUtils.js b/scripts/system/libraries/touchEventUtils.js index 53ac4f49d1..7bd2a10dc9 100644 --- a/scripts/system/libraries/touchEventUtils.js +++ b/scripts/system/libraries/touchEventUtils.js @@ -174,9 +174,7 @@ function calculateTouchTargetFromOverlay(touchTip, overlayID) { if (dimensions === undefined) { return; } - if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. - } + dimensions.z = 0.01; // we are projecting onto the XY plane of the overlay, so ignore the z dimension var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index a6d09e564d..3e9a548798 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -185,7 +185,7 @@ logTrace = function(str) { // (the vector that would move the point outside the sphere) // otherwise returns false findSphereHit = function(point, sphereRadius) { - var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations + var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations var vectorLength = Vec3.length(point); if (vectorLength < EPSILON) { return true; @@ -406,22 +406,21 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) var screenHeight = 0.81 * tabletHeight; Overlays.editOverlay(HMD.tabletScreenID, { localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, - dimensions: {x: screenWidth, y: screenHeight, z: 0.1}, + dimensions: { x: screenWidth, y: screenHeight, z: 0.1 }, dpi: tabletDpi }); // update homeButton - var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * sensorScaleOffsetOverride; - var homeButtonDim = 4 * tabletScaleFactor; + var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20) - 0.003 * sensorScaleFactor) * sensorScaleOffsetOverride; + // FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here + var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; Overlays.editOverlay(HMD.homeButtonID, { - localPosition: {x: 0, y: -HOME_BUTTON_Y_OFFSET, z: 0 }, - dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim} + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); - // Circle3D overlays render at 1.5x their proper dimensions - var highlightDim = homeButtonDim / 3.0; Overlays.editOverlay(HMD.homeButtonHighlightID, { - localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003 * sensorScaleFactor * sensorScaleOffsetOverride, z: -0.0158 * sensorScaleFactor * sensorScaleOffsetOverride }, - dimensions: { x: highlightDim, y: highlightDim, z: highlightDim } + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); }; diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 17821c737e..36a1cbcdd9 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -47,7 +47,7 @@ } return false; } - if (Overlays.getProperty(HMD.homeButtonID, "type") != "sphere" || + if (Overlays.getProperty(HMD.homeButtonID, "type") != "circle3d" || Overlays.getProperty(HMD.tabletScreenID, "type") != "web3d") { if (debugTablet) { print("TABLET is invalid due to other"); From 9f54ce55f301745badddb54047f62359d3492fc3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 20 Nov 2017 14:16:56 -0800 Subject: [PATCH 03/52] Change domain setting from min/max avatar scale to min/max avatar height * Domain settings version has been bumped from version 2.0 to 2.1 * Old domain settings for avatar scale will be auto-converted to avatar height * Avatar code has been changed so that limitDomainScale() works with the new height limits * Avatar getUnscaledEyeHeight() was added to C++. * MyAvatar.getHeight() was added to JS. --- assignment-client/src/avatars/AvatarMixer.cpp | 24 ++--- assignment-client/src/avatars/AvatarMixer.h | 4 +- .../resources/describe-settings.json | 22 ++--- .../src/DomainServerSettingsManager.cpp | 20 ++++ interface/src/Application.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 97 ++++++------------- libraries/animation/src/Rig.h | 3 + .../src/avatars-renderer/Avatar.cpp | 83 +++++++++++----- .../src/avatars-renderer/Avatar.h | 19 ++-- libraries/avatars/src/AvatarData.cpp | 31 ++++++ libraries/avatars/src/AvatarData.h | 40 ++++++-- .../networking/src/udt/PacketHeaders.cpp | 2 + libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/Model.h | 6 +- libraries/shared/src/AvatarConstants.h | 7 ++ 15 files changed, 226 insertions(+), 138 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c67e998dd4..3ca924b007 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -870,8 +870,8 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node node->setLinkedData(std::unique_ptr { new AvatarMixerClientData(node->getUUID()) }); clientData = dynamic_cast(node->getLinkedData()); auto& avatar = clientData->getAvatar(); - avatar.setDomainMinimumScale(_domainMinimumScale); - avatar.setDomainMaximumScale(_domainMaximumScale); + avatar.setDomainMinimumHeight(_domainMinimumHeight); + avatar.setDomainMaximumHeight(_domainMaximumHeight); } return clientData; @@ -939,21 +939,21 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { const QString AVATARS_SETTINGS_KEY = "avatars"; - static const QString MIN_SCALE_OPTION = "min_avatar_scale"; - float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE); - _domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + static const QString MIN_HEIGHT_OPTION = "min_avatar_height"; + float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT); + _domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); - static const QString MAX_SCALE_OPTION = "max_avatar_scale"; - float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); - _domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + static const QString MAX_HEIGHT_OPTION = "max_avatar_height"; + float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT); + _domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); // make sure that the domain owner didn't flip min and max - if (_domainMinimumScale > _domainMaximumScale) { - std::swap(_domainMinimumScale, _domainMaximumScale); + if (_domainMinimumHeight > _domainMaximumHeight) { + std::swap(_domainMinimumHeight, _domainMaximumHeight); } - qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale - << "and a maximum avatar scale of" << _domainMaximumScale; + qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight + << "and a maximum avatar height of" << _domainMaximumHeight; const QString AVATAR_WHITELIST_DEFAULT{ "" }; static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 610da8ad57..cb5f536faa 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -90,8 +90,8 @@ private: float _maxKbpsPerNode = 0.0f; - float _domainMinimumScale { MIN_AVATAR_SCALE }; - float _domainMaximumScale { MAX_AVATAR_SCALE }; + float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; + float _domainMaximumHeight { MAX_AVATAR_HEIGHT }; RateCounter<> _broadcastRate; p_high_resolution_clock::time_point _lastDebugMessage; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index d55da6c848..cacd95fba5 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 2.0, + "version": 2.1, "settings": [ { "name": "label", @@ -1007,20 +1007,20 @@ "assignment-types": [ 1, 2 ], "settings": [ { - "name": "min_avatar_scale", + "name": "min_avatar_height", "type": "double", - "label": "Minimum Avatar Scale", - "help": "Limits the scale of avatars in your domain. Must be at least 0.005.", - "placeholder": 0.25, - "default": 0.25 + "label": "Minimum Avatar Height (meters)", + "help": "Limits the height of avatars in your domain. Must be at least 0.009.", + "placeholder": 0.4, + "default": 0.4 }, { - "name": "max_avatar_scale", + "name": "max_avatar_height", "type": "double", - "label": "Maximum Avatar Scale", - "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", - "placeholder": 3.0, - "default": 3.0 + "label": "Maximum Avatar Height (meters)", + "help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.", + "placeholder": 5.2, + "default": 5.2 }, { "name": "avatar_whitelist", diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 6c50e5245d..674f3a18d1 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -304,6 +304,26 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList *wizardCompletedOnce = QVariant(true); } + if (oldVersion < 2.1) { + // convert old avatar scale settings into avatar height. + + const QString AVATAR_MIN_SCALE_KEYPATH = "avatars.min_avatar_scale"; + const QString AVATAR_MAX_SCALE_KEYPATH = "avatars.max_avatar_scale"; + const QString AVATAR_MIN_HEIGHT_KEYPATH = "avatars.min_avatar_height"; + const QString AVATAR_MAX_HEIGHT_KEYPATH = "avatars.max_avatar_height"; + + QVariant* avatarMinScale = _configMap.valueForKeyPath(AVATAR_MIN_SCALE_KEYPATH); + if (avatarMinScale) { + float scale = avatarMinScale->toFloat(); + QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); + } + + QVariant* avatarMaxScale = _configMap.valueForKeyPath(AVATAR_MAX_SCALE_KEYPATH); + if (avatarMaxScale) { + float scale = avatarMaxScale->toFloat(); + QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); + } + } // write the current description version to our settings *versionVariant = _descriptionVersion; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e7e1fbe2e4..d9c889aa42 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2792,10 +2792,10 @@ static int getEventQueueSize(QThread* thread) { static void dumpEventQueue(QThread* thread) { auto threadData = QThreadData::get2(thread); QMutexLocker locker(&threadData->postEventList.mutex); - qDebug() << "AJT: event list, size =" << threadData->postEventList.size(); + qDebug() << "Event list, size =" << threadData->postEventList.size(); for (auto& postEvent : threadData->postEventList) { QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None); - qDebug() << "AJT: " << type; + qDebug() << " " << type; } } #endif // DEBUG_EVENT_QUEUE diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6b2795fc90..d77bfbe09d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1799,6 +1799,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); + _isAnimatingScale = true; } if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { @@ -2161,39 +2162,14 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float // target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were // before they entered the limiting domain. -void MyAvatar::clampTargetScaleToDomainLimits() { - // when we're about to change the target scale because the user has asked to increase or decrease their scale, - // we first make sure that we're starting from a target scale that is allowed by the current domain - - auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - - if (clampedTargetScale != _targetScale) { - qCDebug(interfaceapp, "Clamped scale to %f since original target scale %f was not allowed by domain", - (double)clampedTargetScale, (double)_targetScale); - - setTargetScale(clampedTargetScale); - } -} - -void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) { - auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale); - - if (clampedTargetScale != desiredScale) { - qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain", - clampedTargetScale, desiredScale); - } - - setTargetScale(clampedTargetScale); - qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); - emit(scaleChanged()); -} - float MyAvatar::getDomainMinScale() { - return _domainMinimumScale; + const float unscaledHeight = getUnscaledEyeHeight(); + return _domainMinimumHeight / unscaledHeight; } float MyAvatar::getDomainMaxScale() { - return _domainMaximumScale; + const float unscaledHeight = getUnscaledEyeHeight(); + return _domainMaximumHeight / unscaledHeight; } void MyAvatar::setGravity(float gravity) { @@ -2205,70 +2181,54 @@ float MyAvatar::getGravity() { } void MyAvatar::increaseSize() { - // make sure we're starting from an allowable scale - clampTargetScaleToDomainLimits(); + float minScale = getDomainMinScale(); + float maxScale = getDomainMaxScale(); - // calculate what our new scale should be - float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO); - - // attempt to change to desired scale (clamped to the domain limits) - clampScaleChangeToDomainLimits(updatedTargetScale); + float newTargetScale = glm::clamp(_targetScale * (1.0f + SCALING_RATIO), minScale, maxScale); + setTargetScale(newTargetScale); } void MyAvatar::decreaseSize() { - // make sure we're starting from an allowable scale - clampTargetScaleToDomainLimits(); + float minScale = getDomainMinScale(); + float maxScale = getDomainMaxScale(); - // calculate what our new scale should be - float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO); - - // attempt to change to desired scale (clamped to the domain limits) - clampScaleChangeToDomainLimits(updatedTargetScale); + float newTargetScale = glm::clamp(_targetScale * (1.0f - SCALING_RATIO), minScale, maxScale); + setTargetScale(newTargetScale); } void MyAvatar::resetSize() { // attempt to reset avatar size to the default (clamped to domain limits) const float DEFAULT_AVATAR_SCALE = 1.0f; - - clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE); + setTargetScale(DEFAULT_AVATAR_SCALE); } void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) { - // pull out the minimum and maximum scale and set them to restrict our scale + // pull out the minimum and maximum height and set them to restrict our scale static const QString AVATAR_SETTINGS_KEY = "avatars"; auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject(); - static const QString MIN_SCALE_OPTION = "min_avatar_scale"; - float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE); - setDomainMinimumScale(settingMinScale); + static const QString MIN_HEIGHT_OPTION = "min_avatar_height"; + float settingMinHeight = avatarsObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT); + setDomainMinimumHeight(settingMinHeight); - static const QString MAX_SCALE_OPTION = "max_avatar_scale"; - float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE); - setDomainMaximumScale(settingMaxScale); + static const QString MAX_HEIGHT_OPTION = "max_avatar_height"; + float settingMaxHeight = avatarsObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT); + setDomainMaximumHeight(settingMaxHeight); // make sure that the domain owner didn't flip min and max - if (_domainMinimumScale > _domainMaximumScale) { - std::swap(_domainMinimumScale, _domainMaximumScale); + if (_domainMinimumHeight > _domainMaximumHeight) { + std::swap(_domainMinimumHeight, _domainMaximumHeight); } // Set avatar current scale Settings settings; settings.beginGroup("Avatar"); _targetScale = loadSetting(settings, "scale", 1.0f); - qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumScale - << " and a maximum avatar scale of " << _domainMaximumScale - << ". Current avatar scale is " << _targetScale; + qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumHeight + << " and a maximum avatar scale of " << _domainMaximumHeight; - // debug to log if this avatar's scale in this domain will be clamped - float clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); - - if (_targetScale != clampedScale) { - qCDebug(interfaceapp) << "Current avatar scale is clamped to " << clampedScale - << " because " << _targetScale << " is not allowed by current domain"; - // The current scale of avatar should not be more than domain's max_avatar_scale and not less than domain's min_avatar_scale . - _targetScale = clampedScale; - } + _isAnimatingScale = true; setModelScale(_targetScale); rebuildCollisionShape(); @@ -2288,8 +2248,8 @@ void MyAvatar::saveAvatarScale() { } void MyAvatar::clearScaleRestriction() { - _domainMinimumScale = MIN_AVATAR_SCALE; - _domainMaximumScale = MAX_AVATAR_SCALE; + _domainMinimumHeight = MIN_AVATAR_HEIGHT; + _domainMaximumHeight = MAX_AVATAR_HEIGHT; } void MyAvatar::goToLocation(const QVariant& propertiesVar) { @@ -3248,6 +3208,7 @@ void MyAvatar::setModelScale(float scale) { if (changed) { float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); emit sensorToWorldScaleChanged(sensorToWorldScale); + emit scaleChanged(); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e9cc444bd4..e738ad1c19 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -231,6 +231,9 @@ public: const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } + const AnimPose& getModelOffsetPose() const { return _modelOffset; } + const AnimPose& getGeometryOffsetPose() const { return _geometryOffset; } + void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; } void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; } void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index d00af4dd1e..819ad764a6 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -162,6 +162,7 @@ AABox Avatar::getBounds() const { } void Avatar::animateScaleChanges(float deltaTime) { + if (_isAnimatingScale) { float currentScale = getModelScale(); float desiredScale = getDomainLimitedScale(); @@ -172,7 +173,7 @@ void Avatar::animateScaleChanges(float deltaTime) { float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale; // snap to the end when we get close enough - const float MIN_RELATIVE_ERROR = 0.03f; + const float MIN_RELATIVE_ERROR = 0.001f; float relativeError = fabsf(desiredScale - currentScale) / desiredScale; if (relativeError < MIN_RELATIVE_ERROR) { animatedScale = desiredScale; @@ -698,6 +699,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _skeletonModel->removeFromScene(scene, transaction); _skeletonModel->addToScene(scene, transaction); canTryFade = true; + _isAnimatingScale = true; } for (auto attachmentModel : _attachmentModels) { if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) { @@ -1195,6 +1197,8 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setModelURLFinished(bool success) { invalidateJointIndicesCache(); + _isAnimatingScale = true; + if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || @@ -1588,45 +1592,80 @@ float Avatar::getEyeHeight() const { return result; } + return getModelScale() * getUnscaledEyeHeight(); +} + +float Avatar::getUnscaledEyeHeight() const { + float skeletonHeight = getUnscaledEyeHeightFromSkeleton(); + + // Sanity check by looking at the model extents. + Extents meshExtents = _skeletonModel->getUnscaledMeshExtents(); + float meshHeight = meshExtents.size().y; + + // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. + // This helps prevent absurdly large avatars from exceeding the domain height limit. + const float MESH_SLOP_RATIO = 1.5; + if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) { + return meshHeight; + } else { + return skeletonHeight; + } +} + +float Avatar::getUnscaledEyeHeightFromSkeleton() const { + // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. - // Makes assumption that the y = 0 plane in geometry is the ground plane. - // We also make that assumption in Rig::computeAvatarBoundingCapsule() - float avatarScale = getModelScale(); + if (_skeletonModel) { auto& rig = _skeletonModel->getRig(); + + // Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here. + AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans()); + AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose(); + + // This factor can be used to scale distances in the geometry frame into the unscaled rig frame. + // Typically it will be the unit conversion from cm to m. + float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor. + int headTopJoint = rig.indexOfJoint("HeadTop_End"); int headJoint = rig.indexOfJoint("Head"); int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye"); int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase"); + + // Makes assumption that the y = 0 plane in geometry is the ground plane. + // We also make that assumption in Rig::computeAvatarBoundingCapsule() + const float GROUND_Y = 0.0f; + + // Values from the skeleton are in the geometry coordinate frame. + auto skeleton = rig.getAnimSkeleton(); if (eyeJoint >= 0 && toeJoint >= 0) { - // measure from eyes to toes. - float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; - return eyeHeight; + // Measure from eyes to toes. + float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y; + return scaleFactor * eyeHeight; } else if (eyeJoint >= 0) { - // measure eyes to y = 0 plane. - float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; - float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - groundHeight; - return eyeHeight; + // Measure Eye joint to y = 0 plane. + float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y; + return scaleFactor * eyeHeight; } else if (headTopJoint >= 0 && toeJoint >= 0) { - // measure toe to top of head. Note: default poses already include avatar scale factor + // Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance. const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; - float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y; - return height - height * ratio; + float height = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y; + return scaleFactor * (height - height * ratio); } else if (headTopJoint >= 0) { + // Measure from HeadTop_End joint to the ground, then remove forehead distance. const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT; - float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; - float headHeight = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - groundHeight; - return headHeight - headHeight * ratio; + float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y; + return scaleFactor * (headHeight - headHeight * ratio); } else if (headJoint >= 0) { - float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y; + // Measure Head joint to the ground, then add in distance from neck to eye. const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT; - float neckHeight = rig.getAbsoluteDefaultPose(headJoint).trans().y - groundHeight; - return neckHeight + neckHeight * ratio; + float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y; + return scaleFactor * (neckHeight + neckHeight * ratio); } else { - return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; + return DEFAULT_AVATAR_EYE_HEIGHT; } } else { - return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT; + return DEFAULT_AVATAR_EYE_HEIGHT; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 8069c6b604..76ed381d3f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -255,12 +255,16 @@ public: bool isFading() const { return _isFading; } void updateFadingStatus(render::ScenePointer scene); - /**jsdoc - * Provides read only access to the current eye height of the avatar. - * @function Avatar.getEyeHeight - * @returns {number} eye height of avatar in meters - */ - Q_INVOKABLE float getEyeHeight() const; + Q_INVOKABLE virtual float getEyeHeight() const override; + + // returns eye height of avatar in meters, ignoreing avatar scale. + // if _targetScale is 1 then this will be identical to getEyeHeight; + virtual float getUnscaledEyeHeight() const override; + + // returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry, + // not all subclasses of AvatarData have access to this data. + virtual bool canMeasureEyeHeight() const override { return true; } + virtual float getModelScale() const { return _modelScale; } virtual void setModelScale(float scale) { _modelScale = scale; } @@ -279,6 +283,7 @@ public slots: void setModelURLFinished(bool success); protected: + float Avatar::getUnscaledEyeHeightFromSkeleton() const; virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send. QString _empty{}; virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! @@ -349,7 +354,7 @@ protected: RateCounter<> _skeletonModelSimulationRate; RateCounter<> _jointDataSimulationRate; -private: +protected: class AvatarEntityDataHash { public: AvatarEntityDataHash(uint32_t h) : hash(h) {}; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bd313ac133..7117fd01aa 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -117,6 +117,37 @@ void AvatarData::setTargetScale(float targetScale) { } } +float AvatarData::getDomainLimitedScale() const { + if (canMeasureEyeHeight()) { + const float unscaledEyeHeight = getUnscaledEyeHeight(); + + // Add in an estimate of forehead height. + const float ratio = unscaledEyeHeight / DEFAULT_AVATAR_HEIGHT; + const float unscaledHeight = unscaledEyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; + + const float minScale = _domainMinimumHeight / unscaledHeight; + const float maxScale = _domainMaximumHeight / unscaledHeight; + return glm::clamp(_targetScale, minScale, maxScale); + } else { + // We can't make a good estimate. + return _targetScale; + } +} + +void AvatarData::setDomainMinimumHeight(float domainMinimumHeight) { + _domainMinimumHeight = glm::clamp(domainMinimumHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); +} + +void AvatarData::setDomainMaximumHeight(float domainMaximumHeight) { + _domainMaximumHeight = glm::clamp(domainMaximumHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); +} + +float AvatarData::getHeight() const { + const float eyeHeight = getEyeHeight(); + const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT; + return eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; +} + glm::vec3 AvatarData::getHandPosition() const { return getOrientation() * _handPosition + getPosition(); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index bf3bb20ef9..e228fb42d5 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -257,9 +258,6 @@ namespace AvatarDataPacket { size_t maxJointDataSize(size_t numJoints); } -static const float MAX_AVATAR_SCALE = 1000.0f; -static const float MIN_AVATAR_SCALE = .005f; - const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; @@ -484,12 +482,34 @@ public: // Scale virtual void setTargetScale(float targetScale); - float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); } + float getDomainLimitedScale() const; - void setDomainMinimumScale(float domainMinimumScale) - { _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); } - void setDomainMaximumScale(float domainMaximumScale) - { _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); } + // returns eye height of avatar in meters, ignoreing avatar scale. + // if _targetScale is 1 then this will be identical to getEyeHeight; + virtual float getUnscaledEyeHeight() const { return DEFAULT_AVATAR_EYE_HEIGHT; } + + // returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry, + // not all subclasses of AvatarData have access to this data. + virtual bool canMeasureEyeHeight() const { return false; } + + /**jsdoc + * Provides read only access to the current eye height of the avatar. + * This height is only an estimate and might be incorrect for avatars that are missing standard joints. + * @function AvatarData.getEyeHeight + * @returns {number} eye height of avatar in meters + */ + Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); } + + /**jsdoc + * Provides read only access to the current height of the avatar. + * This height is only an estimate and might be incorrect for avatars that are missing standard joints. + * @function AvatarData.getHeight + * @returns {number} height of avatar in meters + */ + Q_INVOKABLE virtual float getHeight() const; + + void setDomainMinimumHeight(float domainMinimumHeight); + void setDomainMaximumHeight(float domainMaximumHeight); // Hand State Q_INVOKABLE void setHandState(char s) { _handState = s; } @@ -706,8 +726,8 @@ protected: // Body scale float _targetScale; - float _domainMinimumScale { MIN_AVATAR_SCALE }; - float _domainMaximumScale { MAX_AVATAR_SCALE }; + float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; + float _domainMaximumHeight { MAX_AVATAR_HEIGHT }; // Hand state (are we grabbing something or not) char _handState; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index c2c1d75726..2b9b96bed9 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -74,6 +74,8 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(AudioVersion::HighDynamicRangeVolume); case PacketType::ICEPing: return static_cast(IcePingVersion::SendICEPeerID); + case PacketType::DomainSettings: + return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height default: return 17; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 428fcc7a54..199cb29f53 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } -const float SCALE_CHANGE_EPSILON = 0.01f; +const float SCALE_CHANGE_EPSILON = 0.001f; void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index c537a928b3..fadd44b745 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -204,6 +204,9 @@ public: /// Returns the extents of the model's mesh Extents getMeshExtents() const; + /// Returns the unscaled extents of the model's mesh + Extents getUnscaledMeshExtents() const; + void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK @@ -276,9 +279,6 @@ protected: void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - /// Returns the unscaled extents of the model's mesh - Extents getUnscaledMeshExtents() const; - /// Clear the joint states void clearJointState(int index); diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index a7a80471be..4942c63e27 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -12,6 +12,8 @@ #ifndef hifi_AvatarConstants_h #define hifi_AvatarConstants_h +#include "GLMHelpers.h" + // 50th Percentile Man const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // meters @@ -52,5 +54,10 @@ const float DEFAULT_AVATAR_JUMP_HEIGHT = (DEFAULT_AVATAR_JUMP_SPEED * DEFAULT_AV const float DEFAULT_AVATAR_FALL_HEIGHT = 20.0f; // meters const float DEFAULT_AVATAR_MIN_HOVER_HEIGHT = 2.5f; // meters +static const float MAX_AVATAR_SCALE = 1000.0f; +static const float MIN_AVATAR_SCALE = 0.005f; + +static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meters +static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters #endif // hifi_AvatarConstants_h From b3896f664d8439b460e3eeacf236da71dc397719 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 22 Nov 2017 14:45:52 -0800 Subject: [PATCH 04/52] Made increaseScale and decreaseScale more responsive at limits --- interface/src/avatar/MyAvatar.cpp | 18 ++++++------------ interface/src/avatar/MyAvatar.h | 2 -- libraries/avatars/src/AvatarData.cpp | 26 ++++++++++++++++++-------- libraries/avatars/src/AvatarData.h | 4 ++++ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d77bfbe09d..5da7e37fec 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2162,16 +2162,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float // target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were // before they entered the limiting domain. -float MyAvatar::getDomainMinScale() { - const float unscaledHeight = getUnscaledEyeHeight(); - return _domainMinimumHeight / unscaledHeight; -} - -float MyAvatar::getDomainMaxScale() { - const float unscaledHeight = getUnscaledEyeHeight(); - return _domainMaximumHeight / unscaledHeight; -} - void MyAvatar::setGravity(float gravity) { _characterController.setGravity(gravity); } @@ -2184,7 +2174,9 @@ void MyAvatar::increaseSize() { float minScale = getDomainMinScale(); float maxScale = getDomainMaxScale(); - float newTargetScale = glm::clamp(_targetScale * (1.0f + SCALING_RATIO), minScale, maxScale); + float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale); + float newTargetScale = glm::clamp(clampedTargetScale * (1.0f + SCALING_RATIO), minScale, maxScale); + setTargetScale(newTargetScale); } @@ -2192,7 +2184,9 @@ void MyAvatar::decreaseSize() { float minScale = getDomainMinScale(); float maxScale = getDomainMaxScale(); - float newTargetScale = glm::clamp(_targetScale * (1.0f - SCALING_RATIO), minScale, maxScale); + float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale); + float newTargetScale = glm::clamp(clampedTargetScale * (1.0f - SCALING_RATIO), minScale, maxScale); + setTargetScale(newTargetScale); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e4e8f8d02c..3b5157fdeb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -558,8 +558,6 @@ public slots: void increaseSize(); void decreaseSize(); void resetSize(); - float getDomainMinScale(); - float getDomainMaxScale(); void setGravity(float gravity); float getGravity(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 7117fd01aa..c6b78de07c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -119,14 +119,8 @@ void AvatarData::setTargetScale(float targetScale) { float AvatarData::getDomainLimitedScale() const { if (canMeasureEyeHeight()) { - const float unscaledEyeHeight = getUnscaledEyeHeight(); - - // Add in an estimate of forehead height. - const float ratio = unscaledEyeHeight / DEFAULT_AVATAR_HEIGHT; - const float unscaledHeight = unscaledEyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; - - const float minScale = _domainMinimumHeight / unscaledHeight; - const float maxScale = _domainMaximumHeight / unscaledHeight; + const float minScale = getDomainMinScale(); + const float maxScale = getDomainMaxScale(); return glm::clamp(_targetScale, minScale, maxScale); } else { // We can't make a good estimate. @@ -142,6 +136,22 @@ void AvatarData::setDomainMaximumHeight(float domainMaximumHeight) { _domainMaximumHeight = glm::clamp(domainMaximumHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); } +float AvatarData::getDomainMinScale() const { + const float unscaledHeight = getUnscaledHeight(); + return _domainMinimumHeight / unscaledHeight; +} + +float AvatarData::getDomainMaxScale() const { + const float unscaledHeight = getUnscaledHeight(); + return _domainMaximumHeight / unscaledHeight; +} + +float AvatarData::getUnscaledHeight() const { + const float eyeHeight = getUnscaledEyeHeight(); + const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT; + return eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; +} + float AvatarData::getHeight() const { const float eyeHeight = getEyeHeight(); const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e228fb42d5..50704b98e3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -483,6 +483,8 @@ public: virtual void setTargetScale(float targetScale); float getDomainLimitedScale() const; + float getDomainMinScale() const; + float getDomainMaxScale() const; // returns eye height of avatar in meters, ignoreing avatar scale. // if _targetScale is 1 then this will be identical to getEyeHeight; @@ -508,6 +510,8 @@ public: */ Q_INVOKABLE virtual float getHeight() const; + float getUnscaledHeight() const; + void setDomainMinimumHeight(float domainMinimumHeight); void setDomainMaximumHeight(float domainMaximumHeight); From d4a4c8902ae069f97fe2f5baafe8cfa733d1929b Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 27 Nov 2017 17:46:19 -0800 Subject: [PATCH 05/52] fix reticle depth at different scales and x button click on mouse press --- .../src/display-plugins/CompositorHelper.cpp | 6 ++-- scripts/system/libraries/WebTablet.js | 34 ++++++------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 7b639e8308..f3f81c0b2e 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -437,9 +437,11 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const } else { d = glm::normalize(overlaySurfacePoint); } - reticlePosition = headPosition + (d * getReticleDepth()); + // Our sensor to world matrix always has uniform scale + float sensorSpaceReticleDepth = getReticleDepth() / extractScale(_sensorToWorldMatrix).x; + reticlePosition = headPosition + (d * sensorSpaceReticleDepth); quat reticleOrientation = cancelOutRoll(glm::quat_cast(_currentDisplayPlugin->getHeadPose())); - vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize * getReticleDepth()); + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize * sensorSpaceReticleDepth); return glm::inverse(eyePose) * createMatFromScaleQuatAndPos(reticleScale, reticleOrientation, reticlePosition); } else { static const float CURSOR_PIXEL_SIZE = 32.0f; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 01fdfb1845..4217ec503e 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -514,31 +514,17 @@ WebTablet.prototype.getPosition = function () { }; WebTablet.prototype.mousePressEvent = function (event) { - var pickRay = Camera.computePickRay(event.x, event.y); - var entityPickResults; - entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); - if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID || - entityPickResults.overlayID === this.tabletEntityID)) { - var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID], []); - if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) { - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var onHomeScreen = tablet.onHomeScreen(); - var isMessageOpen = tablet.isMessageDialogOpen(); - if (onHomeScreen) { - if (isMessageOpen === false) { - HMD.closeTablet(); - } - } else { - if (isMessageOpen === false) { - tablet.gotoHomeScreen(); - this.setHomeButtonTexture(); - } + if (!HMD.active) { + var pickRay = Camera.computePickRay(event.x, event.y); + var tabletBackPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); + if (tabletBackPickResults.intersects) { + var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID]); + if (!overlayPickResults.intersects) { + this.dragging = true; + var invCameraXform = new Xform(Camera.orientation, Camera.position).inv(); + this.initialLocalIntersectionPoint = invCameraXform.xformPoint(tabletBackPickResults.intersection); + this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition"); } - } else if (!HMD.active && (!overlayPickResults.intersects || overlayPickResults.overlayID !== this.webOverlayID)) { - this.dragging = true; - var invCameraXform = new Xform(Camera.orientation, Camera.position).inv(); - this.initialLocalIntersectionPoint = invCameraXform.xformPoint(entityPickResults.intersection); - this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition"); } } }; From d65101c4e9c7f8a259051e7c7f0c13f2555e1912 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 10:42:58 -0800 Subject: [PATCH 06/52] sort and throttle avatar updates in interface --- interface/src/avatar/AvatarManager.cpp | 63 +++++++++++++------------ libraries/shared/src/PrioritySortUtil.h | 6 +-- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9ffe74d470..6441415cb8 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -142,32 +143,36 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { PerformanceTimer perfTimer("otherAvatars"); - auto avatarMap = getHashCopy(); - QList avatarList = avatarMap.values(); + class SortableAvatar: public PrioritySortUtil::Sortable { + public: + SortableAvatar() = delete; + SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} + glm::vec3 getPosition() const override { return _avatar->getPosition(); } + float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } + uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } + const AvatarSharedPointer& getAvatar() const { return _avatar; } + private: + AvatarSharedPointer _avatar; + }; + ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, - - [](AvatarSharedPointer avatar)->uint64_t{ - return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); - }, - - [](AvatarSharedPointer avatar)->float{ - return std::static_pointer_cast(avatar)->getBoundingRadius(); - }, - - [this](AvatarSharedPointer avatar)->bool{ - const auto& castedAvatar = std::static_pointer_cast(avatar); - if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { - // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. - // DO NOT update or fade out uninitialized Avatars - return true; // ignore it - } - return false; - }); + // sort + auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); + while (itr != avatarMap.end()) { + const auto& avatar = std::static_pointer_cast(*itr); + // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. + // DO NOT update or fade out uninitialized Avatars + if (avatar != _myAvatar && avatar->isInitialized()) { + sortedAvatars.push(SortableAvatar(avatar)); + } + ++itr; + } + // process in sorted order uint64_t startTime = usecTimestampNow(); const uint64_t UPDATE_BUDGET = 2000; // usec uint64_t updateExpiry = startTime + UPDATE_BUDGET; @@ -176,8 +181,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { render::Transaction transaction; while (!sortedAvatars.empty()) { - const AvatarPriority& sortData = sortedAvatars.top(); - const auto& avatar = std::static_pointer_cast(sortData.avatar); + const SortableAvatar& sortData = sortedAvatars.top(); + const auto& avatar = std::static_pointer_cast(sortData.getAvatar()); bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); if (ignoring) { @@ -207,7 +212,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t now = usecTimestampNow(); if (now < updateExpiry) { // we're within budget - bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } @@ -221,7 +226,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // --> some avatar velocity measurements may be a little off // no time simulate, but we take the time to count how many were tragically missed - bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; } @@ -230,9 +235,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } sortedAvatars.pop(); while (inView && !sortedAvatars.empty()) { - const AvatarPriority& newSortData = sortedAvatars.top(); - const auto& newAvatar = std::static_pointer_cast(newSortData.avatar); - inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; + const SortableAvatar& newSortData = sortedAvatars.top(); + const auto& newAvatar = std::static_pointer_cast(newSortData.getAvatar()); + inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && newAvatar->hasNewJointData()) { numAVatarsNotUpdated++; } diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 1d11a04265..6026d2b21f 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -32,11 +32,10 @@ (2) Make a PrioritySortUtil::PriorityQueue and add them to the queue: - PrioritySortUtil::Prioritizer prioritizer(viewFrustum); + PrioritySortUtil::PriorityQueue sortedThings(viewFrustum); std::priority_queue< PrioritySortUtil::Sortable > sortedThings; for (thing in things) { - float priority = prioritizer.computePriority(PrioritySortUtil::PrioritizableThing(thing)); - sortedThings.push(PrioritySortUtil::Sortable entry(thing, priority)); + sortedThings.push(SortableWrapper(thing)); } (3) Loop over your priority queue and do timeboxed work: @@ -65,6 +64,7 @@ namespace PrioritySortUtil { virtual uint64_t getTimestamp() const = 0; void setPriority(float priority) { _priority = priority; } + float getPriority() const { return _priority; } bool operator<(const Sortable& other) const { return _priority < other._priority; } private: float _priority { 0.0f }; From e068eb879cb613176ad76b7a357e8a5c2e5d29d1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 14:23:56 -0800 Subject: [PATCH 07/52] use PrioritySortUtil for outgoing avatar updates --- .../src/avatars/AvatarMixerClientData.h | 2 +- .../src/avatars/AvatarMixerSlave.cpp | 90 ++++++++++++------- libraries/shared/src/PrioritySortUtil.h | 3 + 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index d5c7784da7..1a9da292ac 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -110,7 +110,7 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } - ViewFrustum getViewFrustom() const { return _currentViewFrustum; } + ViewFrustum getViewFrustum() const { return _currentViewFrustum; } quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index dd045c24ea..341231dc1b 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,10 @@ #include "AvatarMixerClientData.h" #include "AvatarMixerSlave.h" +namespace PrioritySortUtil { + // we declare this callback here but override it later + std::function getAvatarAgeCallback = [] (const AvatarSharedPointer& avatar) { return 0; }; +} void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { _begin = begin; @@ -185,7 +190,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes - QList avatarList; + std::vector avatarsToSort; std::unordered_map avatarDataToNodes; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { @@ -195,36 +200,59 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); - avatarList << otherAvatar; + avatarsToSort.push_back(otherAvatar); avatarDataToNodes[otherAvatar] = otherNode; } }); - AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); - ViewFrustum cameraView = nodeData->getViewFrustom(); - std::priority_queue sortedAvatars; - AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, - [&](AvatarSharedPointer avatar)->uint64_t { - auto avatarNode = avatarDataToNodes[avatar]; - assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); - }, [&](AvatarSharedPointer avatar)->float{ - glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale()); - return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); - }, [&](AvatarSharedPointer avatar)->bool { + // now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback + // with the true implementation + PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) { + auto avatarNode = avatarDataToNodes[avatar]; + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + }; + + class SortableAvatar: public PrioritySortUtil::Sortable { + public: + SortableAvatar() = delete; + SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} + glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } + float getRadius() const override { + glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale()); + return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); + } + uint64_t getTimestamp() const override { + // use the callback implemented above + return PrioritySortUtil::getAvatarAgeCallback(_avatar); + } + const AvatarSharedPointer& getAvatar() const { return _avatar; } + + private: + AvatarSharedPointer _avatar; + }; + + // prepare to sort + ViewFrustum cameraView = nodeData->getViewFrustum(); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + + // ignore or sort + const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); + for (size_t i = 0; i < avatarsToSort.size(); ++i) { + const AvatarSharedPointer& avatar = avatarsToSort[i]; if (avatar == thisAvatar) { - return true; // ignore ourselves... + // don't echo updates to self + continue; } bool shouldIgnore = false; - - // We will also ignore other nodes for a couple of different reasons: + // We ignore other nodes for a couple of reasons: // 1) ignore bubbles and ignore specific node // 2) the node hasn't really updated it's frame data recently, this can // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. - auto avatarNode = avatarDataToNodes[avatar]; + auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); @@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldIgnore = true; } else { - // Check to see if the space bubble is enabled // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { @@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); } } - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); if (!shouldIgnore) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); @@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) ++numAvatarsWithSkippedFrames; } } - return shouldIgnore; - }); + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + // sort this one for later + sortedAvatars.push(SortableAvatar(avatar)); + } + } // loop through our sorted avatars and allocate our bandwidth to them accordingly - int avatarRank = 0; - // this is overly conservative, because it includes some avatars we might not consider int remainingAvatars = (int)sortedAvatars.size(); - while (!sortedAvatars.empty()) { - AvatarPriority sortData = sortedAvatars.top(); + const auto& avatarData = sortedAvatars.top().getAvatar(); sortedAvatars.pop(); - const auto& avatarData = sortData.avatar; - avatarRank++; remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; @@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); } + // determine if avatar is in view which determines how much data to send glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); - - - // determine if avatar is in view, to determine how much data to include... glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); bool isInView = nodeData->otherAvatarInView(otherNodeBox); @@ -412,7 +436,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - }; + } quint64 startPacketSending = usecTimestampNow(); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 6026d2b21f..409450ac08 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -12,6 +12,9 @@ #define hifi_PrioritySortUtil_h #include +#include + +#include "NumericalConstants.h" #include "ViewFrustum.h" /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: From deccc549654700c92d30c929e80aceabdb65fef0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Nov 2017 15:08:52 -0800 Subject: [PATCH 08/52] remove cruft --- .../src/avatars/AvatarMixerSlave.cpp | 2 - libraries/avatars/src/AvatarData.cpp | 53 ------------------- libraries/avatars/src/AvatarData.h | 8 --- 3 files changed, 63 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 341231dc1b..77bf447547 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -189,10 +189,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes - // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes std::vector avatarsToSort; std::unordered_map avatarDataToNodes; - std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { // make sure this is an agent that we have avatar data for before considering it for inclusion if (otherNode->getType() == NodeType::Agent diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index cb43aeee5d..786b7b6912 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2391,59 +2391,6 @@ float AvatarData::_avatarSortCoefficientSize { 0.5f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; -void AvatarData::sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore) { - - PROFILE_RANGE(simulation, "sort"); - uint64_t now = usecTimestampNow(); - - glm::vec3 frustumCenter = cameraView.getPosition(); - const glm::vec3& forward = cameraView.getDirection(); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = avatarList.at(i); - - if (shouldIgnore(avatar)) { - continue; - } - - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - glm::vec3 avatarPosition = avatar->getWorldPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - - // FIXME - AvatarData has something equivolent to this - float radius = getBoundingRadius(avatar); - - float apparentSize = 2.0f * radius / distance; - float cosineAngle = glm::dot(offset, forward) / distance; - float age = (float)(now - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); - - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. - // These weights are pure magic tuning and should be hard coded in the relation below, - // but are currently exposed for anyone who would like to explore fine tuning: - float priority = _avatarSortCoefficientSize * apparentSize - + _avatarSortCoefficientCenter * cosineAngle - + _avatarSortCoefficientAge * age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; - } - } - sortedAvatarsOut.push(AvatarPriority(avatar, priority)); - } -} - QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue obj = engine->newObject(); for (auto entityID : value.keys()) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2f3154ad08..1df0e94496 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -629,14 +629,6 @@ public: static const float OUT_OF_VIEW_PENALTY; - static void sortAvatars( - QList avatarList, - const ViewFrustum& cameraView, - std::priority_queue& sortedAvatarsOut, - std::function getLastUpdated, - std::function getBoundingRadius, - std::function shouldIgnore); - // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. static float _avatarSortCoefficientSize; From e4436d264a9df7d2a3b856201eaa9a61b4c76bc3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Nov 2017 09:03:13 -0800 Subject: [PATCH 09/52] simplify for-loop format --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 77bf447547..9131358cac 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -236,8 +236,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // ignore or sort const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); - for (size_t i = 0; i < avatarsToSort.size(); ++i) { - const AvatarSharedPointer& avatar = avatarsToSort[i]; + for (const auto& avatar : avatarsToSort) { if (avatar == thisAvatar) { // don't echo updates to self continue; From e212ac67c1a3b781bb4146c75fb260e3af3be0e6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Nov 2017 11:30:06 -0800 Subject: [PATCH 10/52] enable custom avatar sort tuning --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 5 ++++- interface/src/avatar/AvatarManager.cpp | 7 +++++-- libraries/avatars/src/AvatarData.cpp | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 9131358cac..769789ef6f 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -232,7 +232,10 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // prepare to sort ViewFrustum cameraView = nodeData->getViewFrustum(); - PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge); // ignore or sort const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 6441415cb8..8a294182bd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -147,7 +147,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { public: SortableAvatar() = delete; SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {} - glm::vec3 getPosition() const override { return _avatar->getPosition(); } + glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } const AvatarSharedPointer& getAvatar() const { return _avatar; } @@ -157,7 +157,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - PrioritySortUtil::PriorityQueue sortedAvatars(cameraView); + PrioritySortUtil::PriorityQueue sortedAvatars(cameraView, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge); // sort auto avatarMap = getHashCopy(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 786b7b6912..f68dce8d26 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2387,7 +2387,7 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; -float AvatarData::_avatarSortCoefficientSize { 0.5f }; +float AvatarData::_avatarSortCoefficientSize { 1.0f }; float AvatarData::_avatarSortCoefficientCenter { 0.25 }; float AvatarData::_avatarSortCoefficientAge { 1.0f }; From 7327b79ce37076acf4c69567a887dfb9c7ae1931 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 11:37:58 -0800 Subject: [PATCH 11/52] WIP --- interface/src/avatar/MyAvatar.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index df2089223b..563bf30f76 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1956,11 +1956,14 @@ void MyAvatar::updateOrientation(float deltaTime) { // Use head/HMD roll to turn while flying, but not when standing still. if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) { + // Turn with head roll. const float MIN_CONTROL_SPEED = 0.01f; float speed = glm::length(getWorldVelocity()); if (speed >= MIN_CONTROL_SPEED) { // Feather turn when stopping moving. + + /* AJT hack float speedFactor; if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { _lastDrivenSpeed = speed; @@ -1968,15 +1971,28 @@ void MyAvatar::updateOrientation(float deltaTime) { } else { speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); } + */ float direction = glm::dot(getWorldVelocity(), getWorldOrientation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; - float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollAngle = asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT)); float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; rollAngle = fabsf(rollAngle); - rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; + //rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate; + const float MAX_ROLL_ANGLE = PI / 2.0f; + const float MAX_ANGULAR_SPEED = 1.5f; // radians per sec + + // apply a quadratic ease in curve. giving less roll and shallow angles and more roll at extreme angles. + float t = glm::clamp(rollAngle, 0.0f, MAX_ROLL_ANGLE) / MAX_ROLL_ANGLE; + float newT = t;//t < 0.5f ? 2.0f * t * t : -1 + (4.0f - 2.0f * t) * t; + float angularSpeed = rollSign * newT * MAX_ANGULAR_SPEED; + + qDebug() << "AJT: rollAngle =" << rollAngle << ", t =" << t << ", newT =" << newT << ", angularSpeed =" << angularSpeed; + + // AJT: remove _hmdRollControlDeadZone, _hmdRollControlRate, _lastDrivenSpeed + + totalBodyYaw += direction * glm::degrees(angularSpeed) * deltaTime; } } From 5cceceb868918fce2acf47b9173d1b805ad7e0d1 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 28 Nov 2017 13:00:41 -0800 Subject: [PATCH 12/52] secondary cam texture fixes --- .../src/model-networking/TextureCache.cpp | 22 +++++++++++++------ .../src/model-networking/TextureCache.h | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 4184351c2d..7e82d044c7 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1006,14 +1006,11 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) if (!_spectatorCameraNetworkTexture) { _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); } - if (_spectatorCameraFramebuffer) { - texture = _spectatorCameraFramebuffer->getRenderBuffer(0); - if (texture) { - texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString()); - _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); - return _spectatorCameraNetworkTexture; - } + if (!_spectatorCameraFramebuffer) { + getSpectatorCameraFramebuffer(); // initialize frame buffer } + updateSpectatorCameraNetworkTexture(); + return _spectatorCameraNetworkTexture; } // FIXME: Generalize this, DRY up this code if (resourceTextureUrl == HMD_PREVIEW_FRAME_URL) { @@ -1052,7 +1049,18 @@ const gpu::FramebufferPointer& TextureCache::getSpectatorCameraFramebuffer(int w // If we aren't taking a screenshot, we might need to resize or create the camera buffer if (!_spectatorCameraFramebuffer || _spectatorCameraFramebuffer->getWidth() != width || _spectatorCameraFramebuffer->getHeight() != height) { _spectatorCameraFramebuffer.reset(gpu::Framebuffer::create("spectatorCamera", gpu::Element::COLOR_SRGBA_32, width, height)); + updateSpectatorCameraNetworkTexture(); emit spectatorCameraFramebufferReset(); } return _spectatorCameraFramebuffer; } + +void TextureCache::updateSpectatorCameraNetworkTexture() { + if (_spectatorCameraFramebuffer && _spectatorCameraNetworkTexture) { + gpu::TexturePointer texture = _spectatorCameraFramebuffer->getRenderBuffer(0); + if (texture) { + texture->setSource(SPECTATOR_CAMERA_FRAME_URL.toString().toStdString()); + _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + } + } +} \ No newline at end of file diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1102694f86..5a96fcf5e6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -171,6 +171,7 @@ public: const gpu::FramebufferPointer& getHmdPreviewFramebuffer(int width, int height); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(); const gpu::FramebufferPointer& getSpectatorCameraFramebuffer(int width, int height); + void updateSpectatorCameraNetworkTexture(); static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 }; static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 }; From 710171929559cd934d72a89b3a32a578449722b7 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 28 Nov 2017 13:01:47 -0800 Subject: [PATCH 13/52] newline --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7e82d044c7..d25c5225d6 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -1063,4 +1063,4 @@ void TextureCache::updateSpectatorCameraNetworkTexture() { _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); } } -} \ No newline at end of file +} From dff49cafaa547a4f864a4cad44c438b067df9b77 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 17:11:44 -0800 Subject: [PATCH 14/52] Embiggen the stick deadspots for oculus touch controllers This effectively splits the controller into directional zones. This should make driving/flying navigation more predictable and less prone to drifting in unintentional directions, which could induce nausea. --- interface/resources/controllers/oculus_touch.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json index 03fc1cbefb..b818d371e3 100644 --- a/interface/resources/controllers/oculus_touch.json +++ b/interface/resources/controllers/oculus_touch.json @@ -13,11 +13,11 @@ { "from": "OculusTouch.LY", "to": "Standard.LY", "filters": [ - { "type": "deadZone", "min": 0.3 }, + { "type": "deadZone", "min": 0.7 }, "invert" ] }, - { "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.3 }, "to": "Standard.LX" }, + { "from": "OculusTouch.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" }, { "from": "OculusTouch.LT", "to": "Standard.LTClick", "peek": true, "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] @@ -29,11 +29,11 @@ { "from": "OculusTouch.RY", "to": "Standard.RY", "filters": [ - { "type": "deadZone", "min": 0.3 }, + { "type": "deadZone", "min": 0.7 }, "invert" ] }, - { "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.3 }, "to": "Standard.RX" }, + { "from": "OculusTouch.RX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.RX" }, { "from": "OculusTouch.RT", "to": "Standard.RTClick", "peek": true, "filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ] From a4d70e89df86953545816c25440990130dba7136 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 17:20:26 -0800 Subject: [PATCH 15/52] hmd turn roll tuning * Tighten action motor timescale while flying. This should cause less drift as the user changes direction and should lead to more comfort and control. * Hmd head roll is only enabled if you are forward or backward faster then 2 meters per second. This should prevent un-intentional turning/rolling when hovering or moving at slow speeds. * Documented hmdRoll tuning parameters accessible from JavaScript. * Removed roll feathering code, because it is non-linear and can perhaps induce nausea. * Tuned default dead spot and turning speeds to match Eagle Flight's defaults. --- interface/src/avatar/MyAvatar.cpp | 64 ++++++++++++++++--------------- interface/src/avatar/MyAvatar.h | 12 ++++-- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 563bf30f76..2f608aca15 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1516,9 +1516,19 @@ void MyAvatar::updateMotors() { _characterController.clearMotors(); glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { + + const float FLYING_MOTOR_TIMESCALE = 0.05f; + const float WALKING_MOTOR_TIMESCALE = 0.2f; + const float INVALID_MOTOR_TIMESCALE = 1.0e6f; + + float horizontalMotorTimescale; + float verticalMotorTimescale; + if (_characterController.getState() == CharacterController::State::Hover || _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { motorRotation = getMyHead()->getHeadOrientation(); + horizontalMotorTimescale = FLYING_MOTOR_TIMESCALE; + verticalMotorTimescale = FLYING_MOTOR_TIMESCALE; } else { // non-hovering = walking: follow camera twist about vertical but not lift // we decompose camera's rotation and store the twist part in motorRotation @@ -1529,11 +1539,12 @@ void MyAvatar::updateMotors() { glm::quat liftRotation; swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation); motorRotation = orientation * motorRotation; + horizontalMotorTimescale = WALKING_MOTOR_TIMESCALE; + verticalMotorTimescale = INVALID_MOTOR_TIMESCALE; } - const float DEFAULT_MOTOR_TIMESCALE = 0.2f; - const float INVALID_MOTOR_TIMESCALE = 1.0e6f; + if (_isPushing || _isBraking || !_isBeingPushed) { - _characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE); + _characterController.addMotor(_actionMotorVelocity, motorRotation, horizontalMotorTimescale, verticalMotorTimescale); } else { // _isBeingPushed must be true --> disable action motor by giving it a long timescale, // otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts @@ -1958,41 +1969,31 @@ void MyAvatar::updateOrientation(float deltaTime) { if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) { // Turn with head roll. - const float MIN_CONTROL_SPEED = 0.01f; - float speed = glm::length(getWorldVelocity()); - if (speed >= MIN_CONTROL_SPEED) { - // Feather turn when stopping moving. + const float MIN_CONTROL_SPEED = 2.0f; // meters / sec + const glm::vec3 characterForward = getWorldOrientation() * Vectors::UNIT_NEG_Z; + float forwardSpeed = glm::dot(characterForward, getWorldVelocity()); - /* AJT hack - float speedFactor; - if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { - _lastDrivenSpeed = speed; - speedFactor = 1.0f; - } else { - speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); - } - */ + // only enable roll-turns if we are moving forward or backward at greater then MIN_CONTROL_SPEED + if (fabsf(forwardSpeed) >= MIN_CONTROL_SPEED) { - float direction = glm::dot(getWorldVelocity(), getWorldOrientation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; - - float rollAngle = asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT)); + float direction = forwardSpeed > 0.0f ? 1.0f : -1.0f; + float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; rollAngle = fabsf(rollAngle); - //rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - const float MAX_ROLL_ANGLE = PI / 2.0f; - const float MAX_ANGULAR_SPEED = 1.5f; // radians per sec + const float MIN_ROLL_ANGLE = _hmdRollControlDeadZone; + const float MAX_ROLL_ANGLE = 90.0f; // degrees - // apply a quadratic ease in curve. giving less roll and shallow angles and more roll at extreme angles. - float t = glm::clamp(rollAngle, 0.0f, MAX_ROLL_ANGLE) / MAX_ROLL_ANGLE; - float newT = t;//t < 0.5f ? 2.0f * t * t : -1 + (4.0f - 2.0f * t) * t; - float angularSpeed = rollSign * newT * MAX_ANGULAR_SPEED; + if (rollAngle > MIN_ROLL_ANGLE) { + // rate of turning is linearly proportional to rolAngle + rollAngle = glm::clamp(rollAngle, MIN_ROLL_ANGLE, MAX_ROLL_ANGLE); - qDebug() << "AJT: rollAngle =" << rollAngle << ", t =" << t << ", newT =" << newT << ", angularSpeed =" << angularSpeed; + // scale rollAngle into a value from zero to one. + float t = (rollAngle - MIN_ROLL_ANGLE) / (MAX_ROLL_ANGLE - MIN_ROLL_ANGLE); - // AJT: remove _hmdRollControlDeadZone, _hmdRollControlRate, _lastDrivenSpeed - - totalBodyYaw += direction * glm::degrees(angularSpeed) * deltaTime; + float angularSpeed = rollSign * t * _hmdRollControlRate; + totalBodyYaw += direction * angularSpeed * deltaTime; + } } } @@ -2038,12 +2039,13 @@ void MyAvatar::updateActionMotor(float deltaTime) { _isBraking = _wasPushing || (_isBraking && speed > MIN_ACTION_BRAKE_SPEED); } + CharacterController::State state = _characterController.getState(); + // compute action input glm::vec3 forward = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FORWARD; glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT; glm::vec3 direction = forward + right; - CharacterController::State state = _characterController.getState(); if (state == CharacterController::State::Hover || _characterController.computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { // we can fly --> support vertical motion diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 563fb7fccd..16cb0edfad 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -110,6 +110,10 @@ class MyAvatar : public Avatar { * @property userEyeHeight {number} Estimated height of the users eyes in sensor space. (meters) * @property SELF_ID {string} READ-ONLY. UUID representing "my avatar". Only use for local-only entities and overlays in situations where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). * Note: Likely to be deprecated. + * @property hmdRollControlEnabled {bool} When enabled the roll angle of your HMD will turn your avatar while flying. + * @property hmdRollControlDeadZone {number} If hmdRollControlEnabled is true, this value can be used to tune what roll angle is required to begin turning. + * This angle is specified in degrees. + * @property hmdRollControlRate {number} If hmdRollControlEnabled is true, this value determines the maximum turn rate of your avatar when rolling your HMD in degrees per second. */ // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type @@ -158,7 +162,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT) - + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -737,12 +741,12 @@ private: bool _clearOverlayWhenMoving { true }; QString _dominantHand { DOMINANT_RIGHT_HAND }; - const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg - const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg + const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0; // degrees + const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec + bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; - float _lastDrivenSpeed { 0.0f }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; From 0ec72f25591617cc3e4b43af1667de7e3cfa6613 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 17:45:13 -0800 Subject: [PATCH 16/52] warning fix --- libraries/avatars-renderer/src/avatars-renderer/Avatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 76ed381d3f..85b48ce423 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -283,7 +283,7 @@ public slots: void setModelURLFinished(bool success); protected: - float Avatar::getUnscaledEyeHeightFromSkeleton() const; + float getUnscaledEyeHeightFromSkeleton() const; virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send. QString _empty{}; virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! From d1609fb9646005dfbabd6de3d4a018207be6e898 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 17:48:34 -0800 Subject: [PATCH 17/52] domain-server warning fix --- domain-server/src/DomainServerSettingsManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 674f3a18d1..0b644d57d8 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -315,13 +315,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList QVariant* avatarMinScale = _configMap.valueForKeyPath(AVATAR_MIN_SCALE_KEYPATH); if (avatarMinScale) { float scale = avatarMinScale->toFloat(); - QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); + _configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); } QVariant* avatarMaxScale = _configMap.valueForKeyPath(AVATAR_MAX_SCALE_KEYPATH); if (avatarMaxScale) { float scale = avatarMaxScale->toFloat(); - QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); + _configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT); } } From eb120b1bc10a995069af15c7188028b240f62d65 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 30 Nov 2017 11:03:27 -0800 Subject: [PATCH 18/52] use correct timestamp of avatar's outgoin data --- .../src/avatars/AvatarMixerClientData.cpp | 17 +++++++++++++++++ .../src/avatars/AvatarMixerClientData.h | 12 +++--------- .../src/avatars/AvatarMixerSlave.cpp | 6 +++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index a4bf8fa253..288652715a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -25,6 +25,23 @@ AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) : _avatar->setID(nodeID); } +uint64_t AvatarMixerClientData::getLastOtherAvatarEncodeTime(QUuid otherAvatar) const { + std::unordered_map::const_iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); + if (itr != _lastOtherAvatarEncodeTime.end()) { + return itr->second; + } + return 0; +} + +void AvatarMixerClientData::setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time) { + std::unordered_map::iterator itr = _lastOtherAvatarEncodeTime.find(otherAvatar); + if (itr != _lastOtherAvatarEncodeTime.end()) { + itr->second = time; + } else { + _lastOtherAvatarEncodeTime.emplace(std::pair(otherAvatar, time)); + } +} + void AvatarMixerClientData::queuePacket(QSharedPointer message, SharedNodePointer node) { if (!_packetQueue.node) { _packetQueue.node = node; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 1a9da292ac..acd9be0702 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -112,14 +112,8 @@ public: ViewFrustum getViewFrustum() const { return _currentViewFrustum; } - quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { - quint64 result = 0; - if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { - result = _lastOtherAvatarEncodeTime[otherAvatar]; - } - _lastOtherAvatarEncodeTime[otherAvatar] = usecTimestampNow(); - return result; - } + uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const; + void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time); QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { _lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount()); @@ -143,7 +137,7 @@ private: // this is a map of the last time we encoded an "other" avatar for // sending to "this" node - std::unordered_map _lastOtherAvatarEncodeTime; + std::unordered_map _lastOtherAvatarEncodeTime; std::unordered_map> _lastOtherAvatarSentJoints; uint64_t _identityChangeTimestamp; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 769789ef6f..9ea1ed3637 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -208,7 +208,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) { auto avatarNode = avatarDataToNodes[avatar]; assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + return nodeData->getLastOtherAvatarEncodeTime(avatarNode->getUUID()); }; class SortableAvatar: public PrioritySortUtil::Sortable { @@ -429,7 +429,11 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // set the last sent sequence number for this sender on the receiver nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); + nodeData->setLastOtherAvatarEncodeTime(otherNode->getUUID(), usecTimestampNow()); } + } else { + // TODO? this avatar is not included now, and will probably not be included next frame. + // It would be nice if we could tweak its future sort priority to put it at the back of the list. } avatarPacketList->endSegment(); From 05fbf8e511e2f842d6f78958840625e79f8379e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 30 Nov 2017 11:04:51 -0800 Subject: [PATCH 19/52] workaround for zero-size objects in priority formula --- libraries/shared/src/PrioritySortUtil.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 409450ac08..dc6a877bb9 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -112,11 +112,19 @@ namespace PrioritySortUtil { glm::vec3 position = thing.getPosition(); glm::vec3 offset = position - _view.getPosition(); float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - float radius = thing.getRadius(); + const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance) + float radius = glm::min(thing.getRadius(), MIN_RADIUS); + float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance); + float age = (float)(usecTimestampNow() - thing.getTimestamp()); - float priority = _angularWeight * (radius / distance) - + _centerWeight * (glm::dot(offset, _view.getDirection()) / distance) - + _ageWeight * (float)(usecTimestampNow() - thing.getTimestamp()); + // we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward + // at a reduced rate but we don't want the "age" term to go zero or negative so we clamp it + const float MIN_COSINE_ANGLE_FACTOR = 0.1f; + float cosineAngleFactor = glm::max(cosineAngle, MIN_COSINE_ANGLE_FACTOR); + + float priority = _angularWeight * glm::max(radius, MIN_RADIUS) / distance + + _centerWeight * cosineAngle + + _ageWeight * cosineAngleFactor * age; // decrement priority of things outside keyhole if (distance - radius > _view.getCenterRadius()) { From 532fc2ca5b9e77c42e69642ad381bcf25271493f Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 6 Nov 2017 18:04:59 -0500 Subject: [PATCH 20/52] [Case 7049] Remove duplicate color fields for shapes (details below). There are 2 types or styles of color picker controls. * color-control1: Composed of only a color picker * color-control2: Composed of a color picker and rgb fields. The color can be manipulated by either. Previously for shapes, the Properties tab utilized both types of color controls. Color-control1 was displayed within the quick properties section; whereas, color-control2 was displayed farther down within the more detailed sections of Properties tab. This commit removes the duplication of the fields, opting to utilize color-control2 within the quick properties portion of the Properties tab. Changes Committed: modified: scripts/system/html/entityProperties.html modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/entityProperties.html | 20 ++++++----------- scripts/system/html/js/entityProperties.js | 26 ++-------------------- 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 8b2a088d83..e95646e3fa 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -66,9 +66,14 @@ -
-
+
+
+
+
+
+
+
@@ -216,17 +221,6 @@
-
-
-
- -
-
-
-
-
-
-
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f54394a353..3286b06998 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -592,8 +592,7 @@ function loaded() { var elJSONEditor = document.getElementById("userdata-editor"); var elNewJSONEditor = document.getElementById('userdata-new-editor'); var elColorSections = document.querySelectorAll(".color-section"); - var elColorControl1 = document.getElementById("property-color-control1"); - var elColorControl2 = document.getElementById("property-color-control2"); + var elColorControlVariant2 = document.getElementById("property-color-control2"); var elColorRed = document.getElementById("property-color-red"); var elColorGreen = document.getElementById("property-color-green"); var elColorBlue = document.getElementById("property-color-blue"); @@ -977,7 +976,7 @@ function loaded() { elColorRed.value = properties.color.red; elColorGreen.value = properties.color.green; elColorBlue.value = properties.color.blue; - elColorControl1.style.backgroundColor = elColorControl2.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elColorControlVariant2.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; } if (properties.type == "Model") { @@ -1303,24 +1302,6 @@ function loaded() { elColorRed.addEventListener('change', colorChangeFunction); elColorGreen.addEventListener('change', colorChangeFunction); elColorBlue.addEventListener('change', colorChangeFunction); - colorPickers.push($('#property-color-control1').colpick({ - colorScheme: 'dark', - layout: 'hex', - color: '000000', - onShow: function(colpick) { - $('#property-color-control1').attr('active', 'true'); - }, - onHide: function(colpick) { - $('#property-color-control1').attr('active', 'false'); - }, - onSubmit: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - $(el).colpickHide(); - emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - // Keep the companion control in sync - elColorControl2.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; - } - })); colorPickers.push($('#property-color-control2').colpick({ colorScheme: 'dark', layout: 'hex', @@ -1335,9 +1316,6 @@ function loaded() { $(el).css('background-color', '#' + hex); $(el).colpickHide(); emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); - // Keep the companion control in sync - elColorControl1.style.backgroundColor = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; - } })); From b7a959255d51b57d4d0e65878b8163c15bc2a9e9 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 6 Nov 2017 19:57:41 -0500 Subject: [PATCH 21/52] [Case 7049] Remove basic color section from non-shape Properties tab. Changes Committed: modified: scripts/system/html/css/edit-style.css --- scripts/system/html/css/edit-style.css | 20 ++++++++++---------- scripts/system/html/entityProperties.html | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6aacaa5333..aad42ca85c 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1500,8 +1500,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.LightMenu .shape-group.shape-section.property.dropdown, -#properties-list.LightMenu color-section.color-control1 { +#properties-list.LightMenu #shape-list, +#properties-list.LightMenu #base-color-section { display: none } @@ -1536,8 +1536,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.ModelMenu .shape-group.shape-section.property.dropdown, -#properties-list.ModelMenu .color-section.color-control1 { +#properties-list.ModelMenu #shape-list, +#properties-list.ModelMenu #base-color-section { display: none } @@ -1572,8 +1572,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.ZoneMenu .shape-group.shape-section.property.dropdown, -#properties-list.ZoneMenu .color-section.color-control1 { +#properties-list.ZoneMenu #shape-list, +#properties-list.ZoneMenu #base-color-section { display: none } @@ -1608,8 +1608,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.WebMenu .shape-group.shape-section.property.dropdown, -#properties-list.WebMenu .color-section.color-control1 { +#properties-list.WebMenu #shape-list, +#properties-list.WebMenu #base-color-section { display: none; } @@ -1645,8 +1645,8 @@ input#reset-to-natural-dimensions { display: none; } /* items to hide */ -#properties-list.TextMenu .shape-group.shape-section.property.dropdown, -#properties-list.TextMenu .color-section.color-control1 { +#properties-list.TextMenu #shape-list, +#properties-list.TextMenu #base-color-section { display: none } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index e95646e3fa..18c3e8960e 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -44,7 +44,7 @@
-
-
+ +
+ CollisionM
From a1d90b5dd92d5b114ebad6301090f6b03ed05229 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Dec 2017 15:41:13 -0800 Subject: [PATCH 31/52] added extra bit --- libraries/entities/src/EntityItem.cpp | 81 ++++++++++++++--------- libraries/entities/src/EntityItem.h | 1 + libraries/entities/src/SimulationFlags.h | 1 + libraries/physics/src/ObjectMotionState.h | 2 +- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4f2b290635..330e1fa854 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1832,39 +1832,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - bool iAmHoldingThis = false; - // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the - // "bootstrapping" problem where you can shoot yourself across the room by grabbing something - // and holding it against your own avatar. - if (isChildOfMyAvatar()) { - iAmHoldingThis = true; - } - // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); - while (i != holdActions.end()) { - EntityDynamicPointer action = *i; - if (action->isMine()) { - iAmHoldingThis = true; - break; - } - i++; - } - QList farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB); - i = farGrabActions.begin(); - while (i != farGrabActions.end()) { - EntityDynamicPointer action = *i; - if (action->isMine()) { - iAmHoldingThis = true; - break; - } - i++; - } - - if (iAmHoldingThis) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - } + if (_dirtyFlags & Simulation::DIRTY_IGNORE_MY_AVATAR) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); } @@ -1960,6 +1929,17 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + + auto actionType = action->getType(); + if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { + _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + } + }); + } } else { qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; } @@ -2000,6 +1980,32 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a return success; } +bool EntityItem::stillHasGrabActions() { + bool stillHasGrabAction = false; + QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); + while (i != holdActions.end()) { + EntityDynamicPointer action = *i; + if (action->isMine()) { + stillHasGrabAction = true; + break; + } + i++; + } + QList farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB); + i = farGrabActions.begin(); + while (i != farGrabActions.end()) { + EntityDynamicPointer action = *i; + if (action->isMine()) { + stillHasGrabAction = true; + break; + } + i++; + } + + return stillHasGrabAction; +} + bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) { _previouslyDeletedActions.insert(actionID, usecTimestampNow()); if (_objectActions.contains(actionID)) { @@ -2023,6 +2029,15 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + if (stillHasGrabActions()) { + _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + } + }); + } setDynamicDataNeedsTransmit(true); return success; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 882b8e6812..ab6df1ab33 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -437,6 +437,7 @@ public: // if this entity is client-only, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + bool stillHasGrabActions(); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index e2b2224b4a..aeee1ae01b 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -27,6 +27,7 @@ namespace Simulation { const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed + const uint32_t DIRTY_IGNORE_MY_AVATAR = 0x4000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 0b91ede574..ae5496b076 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -47,7 +47,7 @@ enum MotionStateType { // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | - Simulation::DIRTY_COLLISION_GROUP); + Simulation::DIRTY_COLLISION_GROUP | Simulation::DIRTY_IGNORE_MY_AVATAR); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | From 1e6b5c0c75d269f37e10eada11c663210acdf72c Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Dec 2017 17:09:00 -0800 Subject: [PATCH 32/52] make requested changes --- libraries/entities/src/EntityItem.cpp | 32 ++++++++++++--------- libraries/entities/src/EntityItem.h | 2 +- libraries/entities/src/SimulationFlags.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 330e1fa854..e764cfff90 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1832,7 +1832,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (_dirtyFlags & Simulation::DIRTY_IGNORE_MY_AVATAR) { + if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); @@ -1932,11 +1932,11 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); } }); } @@ -1980,15 +1980,13 @@ bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& a return success; } -bool EntityItem::stillHasGrabActions() { - bool stillHasGrabAction = false; +bool EntityItem::stillHasGrabActions() const { QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); QList::const_iterator i = holdActions.begin(); while (i != holdActions.end()) { EntityDynamicPointer action = *i; if (action->isMine()) { - stillHasGrabAction = true; - break; + return true; } i++; } @@ -1997,13 +1995,12 @@ bool EntityItem::stillHasGrabActions() { while (i != farGrabActions.end()) { EntityDynamicPointer action = *i; if (action->isMine()) { - stillHasGrabAction = true; - break; + return true; } i++; } - return stillHasGrabAction; + return false; } bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) { @@ -2029,12 +2026,21 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions()) { - _dirtyFlags |= Simulation::DIRTY_IGNORE_MY_AVATAR; + if (stillHasGrabActions() && !(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::DIRTY_IGNORE_MY_AVATAR); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); + } + }); + } else if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); } }); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ab6df1ab33..4c7f37bd6a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -437,7 +437,6 @@ public: // if this entity is client-only, which avatar is it associated with? QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } - bool stillHasGrabActions(); virtual bool wantsHandControllerPointerEvents() const { return false; } virtual bool wantsKeyboardFocus() const { return false; } @@ -471,6 +470,7 @@ protected: void setSimulated(bool simulated) { _simulated = simulated; } const QByteArray getDynamicDataInternal() const; + bool stillHasGrabActions() const; void setDynamicDataInternal(QByteArray dynamicData); virtual void dimensionsChanged() override; diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index aeee1ae01b..aaa92000e7 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -27,7 +27,7 @@ namespace Simulation { const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed - const uint32_t DIRTY_IGNORE_MY_AVATAR = 0x4000; + const uint32_t NO_BOOTSTRAPPING = 0x4000; const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8ebce9f811..7e8b431ceb 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -700,7 +700,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { void EntityMotionState::clearIncomingDirtyFlags() { assert(entityTreeIsLocked()); if (_body && _entity) { - _entity->clearDirtyFlags(); + _entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS); } } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ae5496b076..0b91ede574 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -47,7 +47,7 @@ enum MotionStateType { // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | - Simulation::DIRTY_COLLISION_GROUP | Simulation::DIRTY_IGNORE_MY_AVATAR); + Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY | From a1bf54ff005df24d296da280e4d2970e44c6cb5a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 4 Dec 2017 11:16:41 -0800 Subject: [PATCH 33/52] fix issue of no_bootstrapping not being set correctly --- libraries/entities/src/EntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 6ab4b3ccbe..29b104b421 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1834,7 +1834,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } } - if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); @@ -2028,7 +2028,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions() && !(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + if (stillHasGrabActions()) { _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { @@ -2036,7 +2036,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); } }); - } else if (_dirtyFlags & Simulation::NO_BOOTSTRAPPING) { + } else if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { From eb8438a9e85246c2af14eeacf2ff8b9874cf4f62 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Mon, 4 Dec 2017 18:25:51 -0500 Subject: [PATCH 34/52] [Case 7049] Addresses code review feedback. Changes Committed: modified: scripts/system/html/js/entityProperties.js --- scripts/system/html/js/entityProperties.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 4475e010bb..0ab9f7a9cb 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -455,7 +455,7 @@ function saveJSONUserData(noUpdate) { savedJSONTimer = setTimeout(function() { $('#userdata-saved').hide(); - }, 1500); + }, EDITOR_TIMEOUT_DURATION); } function bindAllNonJSONEditorElements() { @@ -747,12 +747,12 @@ function loaded() { for (var i = 0; i < selections.length; i++) { ids.push(selections[i].id); - var curSelectedType = selections[i].properties.type; - if (types[curSelectedType] === undefined) { - types[curSelectedType] = 0; + var currentSelectedType = selections[i].properties.type; + if (types[currentSelectedType] === undefined) { + types[currentSelectedType] = 0; numTypes += 1; } - types[curSelectedType]++; + types[currentSelectedType]++; } var type = "Multiple"; @@ -1748,9 +1748,9 @@ function loaded() { span.textContent = options[selectedOption].firstChild.textContent; dt.appendChild(span); - var spanCaratDn = document.createElement("span"); - spanCaratDn.textContent = "5"; // caratDn - dt.appendChild(spanCaratDn); + var spanCaratDown = document.createElement("span"); + spanCaratDown.textContent = "5"; // caratDn + dt.appendChild(spanCaratDown); var dd = document.createElement("dd"); dl.appendChild(dd); From 486cdf14d2a71824b079af5ba0b427787b373a91 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 4 Dec 2017 15:34:53 -0800 Subject: [PATCH 35/52] make sure that NO_BOOTSTRAPPING is disbaled correctly if parent is changed --- libraries/entities/src/EntityItem.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 29b104b421..82e364486b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1605,6 +1605,25 @@ void EntityItem::setParentID(const QUuid& value) { if (tree && !oldParentID.isNull()) { tree->removeFromChildrenOfAvatars(getThisPointer()); } + + bool enableNoBootStrapping = false; + if (!value.isNull() && tree) { + EntityItemPointer entity = tree->findEntityByEntityItemID(value); + if (entity) { + enableNoBootStrapping = (bool)(entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING); + } + } + + enableNoBootStrapping ? markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + enableNoBootStrapping ? entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + } + }); + SpatiallyNestable::setParentID(value); // children are forced to be kinematic // may need to not collide with own avatar From c58286d371ef8145df2f91f832f1a3698afe19fb Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Dec 2017 09:31:35 -0800 Subject: [PATCH 36/52] dont do work if not needed --- libraries/entities/src/EntityItem.cpp | 39 ++++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 82e364486b..453e78b305 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1606,23 +1606,42 @@ void EntityItem::setParentID(const QUuid& value) { tree->removeFromChildrenOfAvatars(getThisPointer()); } - bool enableNoBootStrapping = false; + uint32_t oldParentNoBootstrapping = 0; + uint32_t newParentNoBootstrapping = 0; if (!value.isNull() && tree) { EntityItemPointer entity = tree->findEntityByEntityItemID(value); if (entity) { - enableNoBootStrapping = (bool)(entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING); + newParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; } } - enableNoBootStrapping ? markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); - forEachDescendant([&](SpatiallyNestablePointer object) { - if (object->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(object); - entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); - enableNoBootStrapping ? entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING) : - entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + if (!oldParentID.isNull() && tree) { + EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID); + if (entity) { + oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::NO_BOOTSTRAPPING; } - }); + } + + if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { + if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { + markDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP | Simulation::NO_BOOTSTRAPPING); + } + }); + } else { + clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + forEachDescendant([&](SpatiallyNestablePointer object) { + if (object->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(object); + entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); + entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); + } + }); + } + } SpatiallyNestable::setParentID(value); // children are forced to be kinematic From 2752dc7cacfcfb9cdcc62655f57644cfa405377c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 12:28:55 +1300 Subject: [PATCH 37/52] Fix dimensions of cloned cube, sphere, shape, and model overlays --- interface/src/ui/overlays/ModelOverlay.cpp | 5 +++-- interface/src/ui/overlays/Volume3DOverlay.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 09d9ba574a..2cd9b0f7f5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -34,8 +34,9 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), - _updateModel(false), - _loadPriority(modelOverlay->getLoadPriority()) + _scaleToFit(modelOverlay->_scaleToFit), + _loadPriority(modelOverlay->_loadPriority), + _updateModel(false) { _model->init(); _model->setLoadingPriority(_loadPriority); diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index b580545288..49c76e6108 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -14,7 +14,8 @@ #include Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) : - Base3DOverlay(volume3DOverlay) + Base3DOverlay(volume3DOverlay), + _localBoundingBox(volume3DOverlay->_localBoundingBox) { } From e074bd85f9683cab2e21b1df4ec4948917d00b7f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 12:29:09 +1300 Subject: [PATCH 38/52] Fix shape of cloned shape overlay --- interface/src/ui/overlays/Shape3DOverlay.cpp | 5 +++-- interface/src/ui/overlays/Shape3DOverlay.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index ca6446d215..07949d5349 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -18,8 +18,9 @@ QString const Shape3DOverlay::TYPE = "shape"; -Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) : - Volume3DOverlay(Shape3DOverlay) +Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* shape3DOverlay) : + Volume3DOverlay(shape3DOverlay), + _shape(shape3DOverlay->_shape) { } diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index e9e26e3c94..7fc95ec981 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -23,7 +23,7 @@ public: virtual QString getType() const override { return TYPE; } Shape3DOverlay() {} - Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay); + Shape3DOverlay(const Shape3DOverlay* shape3DOverlay); virtual void render(RenderArgs* args) override; virtual const render::ShapeKey getShapeKey() override; From 8af1d8d8d02c9590d5b0602956c7233d4b4eb545 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Dec 2017 15:48:09 -0800 Subject: [PATCH 39/52] dont run code if not nesscassry --- libraries/entities/src/EntityItem.cpp | 37 +++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 453e78b305..55b345cff5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1968,17 +1968,19 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDyn if (success) { _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar auto actionType = action->getType(); if (actionType == DYNAMIC_TYPE_HOLD || actionType == DYNAMIC_TYPE_FAR_GRAB) { - _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); - } - }); + if (!(bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar + forEachDescendant([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + EntityItemPointer entity = std::static_pointer_cast(child); + entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); + } + }); + } } } else { qCDebug(entities) << "EntityItem::addActionInternal -- serializeActions failed"; @@ -2056,7 +2058,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi action->setOwnerEntity(nullptr); action->setIsMine(false); - _objectActions.remove(actionID); if (simulation) { action->removeFromSimulation(simulation); @@ -2065,17 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi bool success = true; serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - if (stillHasGrabActions()) { - _dirtyFlags |= Simulation::NO_BOOTSTRAPPING; - forEachDescendant([&](SpatiallyNestablePointer child) { - if (child->getNestableType() == NestableType::Entity) { - EntityItemPointer entity = std::static_pointer_cast(child); - entity->markDirtyFlags(Simulation::NO_BOOTSTRAPPING | Simulation::DIRTY_COLLISION_GROUP); - } - }); - } else if ((bool)(_dirtyFlags & Simulation::NO_BOOTSTRAPPING)) { + auto removedActionType = action->getType(); + if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) { _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar forEachDescendant([&](SpatiallyNestablePointer child) { if (child->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast(child); @@ -2083,7 +2077,12 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi entity->clearDirtyFlags(Simulation::NO_BOOTSTRAPPING); } }); + } else { + // NO-OP: we assume NO_BOOTSTRAPPING bits and collision group are correct + // because they should have been set correctly when the action was added + // and/or when children were linked } + _objectActions.remove(actionID); setDynamicDataNeedsTransmit(true); return success; } From cafebf6a7e89653fc6af55ca84ec68902ad4bf76 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 6 Dec 2017 13:38:42 +1300 Subject: [PATCH 40/52] Fix Unix build warnings and tidy initializers --- interface/src/ui/overlays/ModelOverlay.cpp | 4 ++-- interface/src/ui/overlays/ModelOverlay.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 2cd9b0f7f5..f27e26280a 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -34,9 +34,9 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), + _updateModel(false), _scaleToFit(modelOverlay->_scaleToFit), - _loadPriority(modelOverlay->_loadPriority), - _updateModel(false) + _loadPriority(modelOverlay->_loadPriority) { _model->init(); _model->setLoadingPriority(_loadPriority); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index ea0eff170c..c4506d9621 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -75,8 +75,8 @@ private: QVariantMap _modelTextures; QUrl _url; - bool _updateModel = { false }; - bool _scaleToFit = { false }; + bool _updateModel { false }; + bool _scaleToFit { false }; float _loadPriority { 0.0f }; AnimationPointer _animation; @@ -87,7 +87,7 @@ private: bool _animationRunning { false }; bool _animationLoop { false }; float _animationFirstFrame { 0.0f }; - float _animationLastFrame = { 0.0f }; + float _animationLastFrame { 0.0f }; bool _animationHold { false }; bool _animationAllowTranslation { false }; uint64_t _lastAnimated { 0 }; From 0f3a70553c624769bb825afa54b3b526eb50af96 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 6 Dec 2017 11:18:15 -0800 Subject: [PATCH 41/52] fix dynamic entities that are parented to your avatar --- libraries/entities/src/EntityItem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 55b345cff5..48370b02fd 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1622,6 +1622,10 @@ void EntityItem::setParentID(const QUuid& value) { } } + if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) { + newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING; + } + if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) { if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) { markDirtyFlags(Simulation::NO_BOOTSTRAPPING); From efd72be96fd73068820973a88fd0d78a30b5078b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Dec 2017 12:05:46 +1300 Subject: [PATCH 42/52] Fix image overlay's subImage property's width and height support --- .../qml/hifi/overlays/ImageOverlay.qml | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml index 8c638679f9..6899c38e67 100644 --- a/interface/resources/qml/hifi/overlays/ImageOverlay.qml +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -9,33 +9,30 @@ Overlay { Image { id: image - property bool scaleFix: true; - property real xOffset: 0 - property real yOffset: 0 + property bool scaleFix: true + property real xStart: 0 + property real yStart: 0 + property real xSize: 0 + property real ySize: 0 property real imageScale: 1.0 property var resizer: Timer { interval: 50 repeat: false running: false onTriggered: { - var targetAspect = root.width / root.height; - var sourceAspect = image.sourceSize.width / image.sourceSize.height; - if (sourceAspect <= targetAspect) { - if (root.width === image.sourceSize.width) { - return; - } - image.imageScale = root.width / image.sourceSize.width; - } else if (sourceAspect > targetAspect){ - if (root.height === image.sourceSize.height) { - return; - } - image.imageScale = root.height / image.sourceSize.height; + if (image.xSize === 0) { + image.xSize = image.sourceSize.width - image.xStart; } - image.sourceSize = Qt.size(image.sourceSize.width * image.imageScale, image.sourceSize.height * image.imageScale); + if (image.ySize === 0) { + image.ySize = image.sourceSize.height - image.yStart; + } + + image.anchors.leftMargin = -image.xStart * root.width / image.xSize; + image.anchors.topMargin = -image.yStart * root.height / image.ySize; + image.anchors.rightMargin = (image.xStart + image.xSize - image.sourceSize.width) * root.width / image.xSize; + image.anchors.bottomMargin = (image.yStart + image.ySize - image.sourceSize.height) * root.height / image.ySize; } } - x: -1 * xOffset * imageScale - y: -1 * yOffset * imageScale onSourceSizeChanged: { if (sourceSize.width !== 0 && sourceSize.height !== 0 && progress === 1.0 && scaleFix) { @@ -43,6 +40,8 @@ Overlay { resizer.start(); } } + + anchors.fill: parent } ColorOverlay { @@ -57,8 +56,10 @@ Overlay { var key = keys[i]; var value = subImage[key]; switch (key) { - case "x": image.xOffset = value; break; - case "y": image.yOffset = value; break; + case "x": image.xStart = value; break; + case "y": image.yStart = value; break; + case "width": image.xSize = value; break; + case "height": image.ySize = value; break; } } } From d61f0584c3f5f5ec42b5a4684a87718f17b5a0e9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 7 Dec 2017 12:26:29 +1300 Subject: [PATCH 43/52] Fix diectory.js script's icon URL --- scripts/system/directory.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/directory.js b/scripts/system/directory.js index 8b9ec17f05..f84429ab95 100644 --- a/scripts/system/directory.js +++ b/scripts/system/directory.js @@ -63,7 +63,7 @@ var toolBar = (function() { y: -TOOLBAR_MARGIN_Y - toolHeight }); browseDirectoryButton = toolBar.addTool({ - imageURL: toolIconUrl + "directory-01.svg", + imageURL: toolIconUrl + "directory.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, From ed76ca8018df6eeee3e96482f594cdda3e357cdb Mon Sep 17 00:00:00 2001 From: H Q Date: Wed, 6 Dec 2017 15:37:19 -0800 Subject: [PATCH 44/52] return to main mktplace page from rez page --- scripts/system/marketplaces/marketplaces.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 646e5452df..16787e1bfc 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -421,7 +421,8 @@ break; case 'checkout_itemLinkClicked': case 'checkout_continueShopping': - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + //tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); //tablet.popFromStack(); break; case 'purchases_itemInfoClicked': From 99c52f86021fc1f6a919d8aea6de53d8633f56a8 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 7 Dec 2017 09:38:14 -0800 Subject: [PATCH 45/52] coding standards --- interface/src/ui/overlays/Web3DOverlay.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 33ab849e25..4098e98488 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -91,9 +91,9 @@ private: gpu::TexturePointer _texture; QString _url; QString _scriptURL; - float _dpi { 30 }; + float _dpi { 30.0f }; int _geometryId { 0 }; - bool _showKeyboardFocusHighlight{ true }; + bool _showKeyboardFocusHighlight { true }; QTouchDevice _touchDevice; From 21077da41ad47664c353cf0866246fd142b69384 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 7 Dec 2017 09:48:06 -0800 Subject: [PATCH 46/52] Don't send requests to backend if cached public keys is empty --- interface/src/commerce/QmlCommerce.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 435d15d161..69089df9c2 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -83,19 +83,28 @@ void QmlCommerce::buy(const QString& assetId, int cost, const bool controlledFai void QmlCommerce::balance() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); - ledger->balance(wallet->listPublicKeys()); + QStringList cachedPublicKeys = wallet->listPublicKeys(); + if (!cachedPublicKeys.isEmpty()) { + ledger->balance(cachedPublicKeys); + } } void QmlCommerce::inventory() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); - ledger->inventory(wallet->listPublicKeys()); + QStringList cachedPublicKeys = wallet->listPublicKeys(); + if (!cachedPublicKeys.isEmpty()) { + ledger->inventory(cachedPublicKeys); + } } void QmlCommerce::history() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); - ledger->history(wallet->listPublicKeys()); + QStringList cachedPublicKeys = wallet->listPublicKeys(); + if (!cachedPublicKeys.isEmpty()) { + ledger->history(cachedPublicKeys); + } } void QmlCommerce::changePassphrase(const QString& oldPassphrase, const QString& newPassphrase) { From 095bedcd3fb062e3182e9a2d0c4171fef34fd833 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 7 Dec 2017 10:08:18 -0800 Subject: [PATCH 47/52] code review feedback --- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 44e75e5919..437ac623e3 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1604,7 +1604,7 @@ float Avatar::getUnscaledEyeHeight() const { // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. // This helps prevent absurdly large avatars from exceeding the domain height limit. - const float MESH_SLOP_RATIO = 1.5; + const float MESH_SLOP_RATIO = 1.5f; if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) { return meshHeight; } else { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 06fe8b597b..410c57c343 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -138,11 +138,19 @@ void AvatarData::setDomainMaximumHeight(float domainMaximumHeight) { float AvatarData::getDomainMinScale() const { const float unscaledHeight = getUnscaledHeight(); + const float EPSILON = 1.0e-4f; + if (unscaledHeight <= EPSILON) { + unscaledHeight = DEFAULT_AVATAR_HEIGHT; + } return _domainMinimumHeight / unscaledHeight; } float AvatarData::getDomainMaxScale() const { const float unscaledHeight = getUnscaledHeight(); + const float EPSILON = 1.0e-4f; + if (unscaledHeight <= EPSILON) { + unscaledHeight = DEFAULT_AVATAR_HEIGHT; + } return _domainMaximumHeight / unscaledHeight; } From b5ffda69116e7b53de6117a74439759810b4455b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 7 Dec 2017 10:09:09 -0800 Subject: [PATCH 48/52] const fix --- libraries/avatars/src/AvatarData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 410c57c343..f2053e29d7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -137,7 +137,7 @@ void AvatarData::setDomainMaximumHeight(float domainMaximumHeight) { } float AvatarData::getDomainMinScale() const { - const float unscaledHeight = getUnscaledHeight(); + float unscaledHeight = getUnscaledHeight(); const float EPSILON = 1.0e-4f; if (unscaledHeight <= EPSILON) { unscaledHeight = DEFAULT_AVATAR_HEIGHT; @@ -146,7 +146,7 @@ float AvatarData::getDomainMinScale() const { } float AvatarData::getDomainMaxScale() const { - const float unscaledHeight = getUnscaledHeight(); + float unscaledHeight = getUnscaledHeight(); const float EPSILON = 1.0e-4f; if (unscaledHeight <= EPSILON) { unscaledHeight = DEFAULT_AVATAR_HEIGHT; From 47b52238d91bab462be296e85971bd0bb0229fea Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 7 Dec 2017 10:49:23 -0800 Subject: [PATCH 49/52] code review feedback --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2f608aca15..6d5a202128 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1969,7 +1969,7 @@ void MyAvatar::updateOrientation(float deltaTime) { if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) { // Turn with head roll. - const float MIN_CONTROL_SPEED = 2.0f; // meters / sec + const float MIN_CONTROL_SPEED = 2.0f * getSensorToWorldScale(); // meters / sec const glm::vec3 characterForward = getWorldOrientation() * Vectors::UNIT_NEG_Z; float forwardSpeed = glm::dot(characterForward, getWorldVelocity()); @@ -1985,13 +1985,13 @@ void MyAvatar::updateOrientation(float deltaTime) { const float MAX_ROLL_ANGLE = 90.0f; // degrees if (rollAngle > MIN_ROLL_ANGLE) { - // rate of turning is linearly proportional to rolAngle + // rate of turning is linearly proportional to rollAngle rollAngle = glm::clamp(rollAngle, MIN_ROLL_ANGLE, MAX_ROLL_ANGLE); // scale rollAngle into a value from zero to one. - float t = (rollAngle - MIN_ROLL_ANGLE) / (MAX_ROLL_ANGLE - MIN_ROLL_ANGLE); + float rollFactor = (rollAngle - MIN_ROLL_ANGLE) / (MAX_ROLL_ANGLE - MIN_ROLL_ANGLE); - float angularSpeed = rollSign * t * _hmdRollControlRate; + float angularSpeed = rollSign * rollFactor * _hmdRollControlRate; totalBodyYaw += direction * angularSpeed * deltaTime; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 16cb0edfad..0902865f9e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -741,7 +741,7 @@ private: bool _clearOverlayWhenMoving { true }; QString _dominantHand { DOMINANT_RIGHT_HAND }; - const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0; // degrees + const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec bool _hmdRollControlEnabled { true }; From 72ed3b077df4328d4b28fbbe7c3956787acb8c57 Mon Sep 17 00:00:00 2001 From: H Q Date: Thu, 7 Dec 2017 11:28:40 -0800 Subject: [PATCH 50/52] Addressed PR change requests --- scripts/system/marketplaces/marketplaces.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 16787e1bfc..7a85462395 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -420,8 +420,9 @@ tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); break; case 'checkout_itemLinkClicked': + tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + break; case 'checkout_continueShopping': - //tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); //tablet.popFromStack(); break; From fd38b89f35383d1847be5174a08b4625bae07f0f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 8 Dec 2017 10:58:52 +1300 Subject: [PATCH 51/52] Move mouse and hover slots out of Overlays API --- interface/src/ui/overlays/Overlays.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index a8f504bbc5..e1996e6bfc 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -305,13 +305,6 @@ public slots: OverlayID getKeyboardFocusOverlay(); void setKeyboardFocusOverlay(const OverlayID& id); - void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event); - void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event); - signals: /**jsdoc * Emitted when an overlay is deleted @@ -358,6 +351,14 @@ private: OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray); + +private slots: + void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void hoverEnterPointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event); }; #endif // hifi_Overlays_h From 42b509639b136c5b2c09fab5657997fe6ffe26ca Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 7 Dec 2017 17:22:00 -0800 Subject: [PATCH 52/52] Fix certificate hash --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5ab4bdee01..9f7ba1cc80 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2486,7 +2486,7 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const { ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL); ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL); ADD_INT_PROPERTY(editionNumber, EditionNumber); - ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber); + ADD_INT_PROPERTY(entityInstanceNumber, EntityInstanceNumber); ADD_STRING_PROPERTY(itemArtist, ItemArtist); ADD_STRING_PROPERTY(itemCategories, ItemCategories); ADD_STRING_PROPERTY(itemDescription, ItemDescription);