From 9bafd82b2c03c1f786785acfd4e485e0030d864b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 9 Aug 2016 15:49:00 -0700 Subject: [PATCH 01/14] marketplace now pops up a marketplace web entity --- scripts/system/libraries/WebBuddy.js | 95 ++++++++++++++++++++++++++++ scripts/system/marketplace.js | 37 ++++++++--- 2 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 scripts/system/libraries/WebBuddy.js diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js new file mode 100644 index 0000000000..d584bcc564 --- /dev/null +++ b/scripts/system/libraries/WebBuddy.js @@ -0,0 +1,95 @@ +// +// WebBuddy.js +// +// Created by Anthony J. Thibault on 8/8/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +var NEGATIVE_ONE = 65535; + +// ctor +WebBuddy = function (url) { + + var ASPECT = 4.0 / 3.0; + var WIDTH = 0.4; + + var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), + {x: 0, y: 0.5, z: 0}); + + var webEntityPosition = spawnPoint; + var webEntityRotation = MyAvatar.orientation; + this.webEntityID = Entities.addEntity({ + type: "Web", + sourceUrl: url, + dimensions: {x: WIDTH, y: WIDTH * ASPECT, z: 0.1}, + position: webEntityPosition, + rotation: webEntityRotation, + name: "web", + dynamic: true, + angularDamping: 0.9, + damping: 0.9, + gravity: {x: 0, y: 0, z: 0}, + shapeType: "box", + userData: JSON.stringify({ + "grabbableKey": {"grabbable": true} + }), + parentID: MyAvatar.sessionUUID, + parentJointIndex: NEGATIVE_ONE + }); + + this.state = "idle"; + + // compute the room/sensor matrix of the entity. + var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); + var entityWorldMat = Mat4.createFromRotAndTrans(webEntityRotation, webEntityPosition); + this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); + + var _this = this; + this.updateFunc = function (dt) { + _this.update(dt); + }; + Script.update.connect(this.updateFunc); +}; + +WebBuddy.prototype.destroy = function () { + Entities.deleteEntity(this.webEntityID); + Script.update.disconnect(this.updateFunc); +}; + +WebBuddy.prototype.update = function (dt) { + + var props = Entities.getEntityProperties(this.webEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); + var entityWorldMat; + + if (this.state === "idle") { + + if (props.parentID !== MyAvatar.sessionUUID || props.parentJointIndex !== NEGATIVE_ONE) { + this.state = "held"; + return; + } + + // convert the sensor/room matrix of the entity into world space, using the current sensorToWorldMatrix + var roomMat = MyAvatar.sensorToWorldMatrix; + entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); + + // slam the world space position and orientation + Entities.editEntity(this.webEntityID, { + position: Mat4.extractTranslation(entityWorldMat), + rotation: Mat4.extractRotation(entityWorldMat) + }); + } else if (this.state === "held") { + if (props.parentID === MyAvatar.sessionUUID && props.parentJointIndex === NEGATIVE_ONE) { + + // re-compute the room/sensor matrix for the avatar now that it has been released. + var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); + entityWorldMat = Mat4.createFromRotAndTrans(props.rotation, props.position); + this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); + + this.state = "idle"; + return; + } + } +}; + diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 356ed8e8cf..26d46d6219 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -8,6 +8,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global WebBuddy */ +Script.include("./libraries/WebBuddy.js"); + var toolIconUrl = Script.resolvePath("assets/images/tools/"); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; @@ -22,26 +25,44 @@ var marketplaceWindow = new OverlayWebWindow({ var toolHeight = 50; var toolWidth = 50; var TOOLBAR_MARGIN_Y = 0; +var marketplaceVisible = false; +var marketplaceWebBuddy; +function shouldShowWebBuddyMarketplace() { + var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); + var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); + return HMD.active && (leftPose.valid || rightPose.valid); +} function showMarketplace(marketplaceID) { - var url = MARKETPLACE_URL; - if (marketplaceID) { - url = url + "/items/" + marketplaceID; + if (shouldShowWebBuddyMarketplace()) { + marketplaceWebBuddy = new WebBuddy("https://metaverse.highfidelity.com/marketplace"); + } else { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); } - marketplaceWindow.setURL(url); - marketplaceWindow.setVisible(true); + marketplaceVisible = true; UserActivityLogger.openedMarketplace(); } function hideMarketplace() { - marketplaceWindow.setVisible(false); - marketplaceWindow.setURL("about:blank"); + if (marketplaceWindow.visible) { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); + } else { + marketplaceWebBuddy.destroy(); + marketplaceWebBuddy = null; + } + marketplaceVisible = false; } function toggleMarketplace() { - if (marketplaceWindow.visible) { + if (marketplaceVisible) { hideMarketplace(); } else { showMarketplace(); From 6704ae9ad4774628d07c3d5851cb0a23cd9536a2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 11:07:23 -0700 Subject: [PATCH 02/14] Added dpi parameter to userData (this is temporary) --- .../src/RenderableWebEntityItem.cpp | 8 +++---- libraries/entities/src/WebEntityItem.cpp | 23 +++++++++++++++++++ libraries/entities/src/WebEntityItem.h | 1 + scripts/system/libraries/WebBuddy.js | 3 ++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 79af620661..b78cff3b25 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -26,7 +26,6 @@ #include "EntityTreeRenderer.h" -const float DPI = 30.47f; const float METERS_TO_INCHES = 39.3701f; static uint32_t _currentWebCount { 0 }; // Don't allow more than 100 concurrent web views @@ -87,7 +86,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { QTouchEvent::TouchPoint point; point.setId(event.getID()); point.setState(Qt::TouchPointReleased); - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); point.setPos(windowPoint); QList touchPoints; @@ -125,7 +124,8 @@ void RenderableWebEntityItem::render(RenderArgs* args) { _lastRenderTime = usecTimestampNow(); glm::vec2 dims = glm::vec2(getDimensions()); - dims *= METERS_TO_INCHES * DPI; + + dims *= METERS_TO_INCHES * _dpi; // The offscreen surface is idempotent for resizes (bails early // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. @@ -185,7 +185,7 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { return; } - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); if (event.getType() == PointerEvent::Move) { diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index b31e47608f..c97eedc56a 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -20,8 +21,26 @@ #include "EntityTree.h" #include "EntityTreeElement.h" +const float DEFAULT_DPI = 30.47f; + const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com"); +static float parseDPIFromUserData(QString str) { + QJsonParseError error; + auto doc = QJsonDocument::fromJson(str.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + return DEFAULT_DPI; + } + QJsonObject obj = doc.object(); + + QJsonValue dpiValue = obj.value("dpi"); + if (!dpiValue.isDouble()) { + return DEFAULT_DPI; + } + double dpi = dpiValue.toDouble(); + return (float)dpi; +} + EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity { new WebEntityItem(entityID) }; entity->setProperties(properties); @@ -30,6 +49,7 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Web; + _dpi = DEFAULT_DPI; } const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; @@ -62,6 +82,9 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(properties._lastEdited); } + // AJT: TODO MAKE THIS A REAL PROPERTY + _dpi = parseDPIFromUserData(getUserData()); + return somethingChanged; } diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 7c4e9f801a..2179f34e36 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -56,6 +56,7 @@ public: protected: QString _sourceUrl; + float _dpi; }; #endif // hifi_WebEntityItem_h diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index d584bcc564..33b6c74c93 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -33,7 +33,8 @@ WebBuddy = function (url) { gravity: {x: 0, y: 0, z: 0}, shapeType: "box", userData: JSON.stringify({ - "grabbableKey": {"grabbable": true} + "grabbableKey": {"grabbable": true}, + "dpi": 75 }), parentID: MyAvatar.sessionUUID, parentJointIndex: NEGATIVE_ONE From 31ed378dbf02b267ef21e4d78a42a395380d768e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 15:08:44 -0700 Subject: [PATCH 03/14] Added dpi entity property for web-entities --- .../entities/src/EntityItemProperties.cpp | 14 ++++++++ libraries/entities/src/EntityItemProperties.h | 2 ++ .../src/EntityItemPropertiesDefaults.h | 2 ++ libraries/entities/src/EntityPropertyFlags.h | 1 + libraries/entities/src/WebEntityItem.cpp | 36 ++++++++----------- libraries/entities/src/WebEntityItem.h | 7 +++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/libraries/WebBuddy.js | 4 +-- 9 files changed, 43 insertions(+), 26 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index b3d810c0eb..06c91b4f32 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -335,6 +335,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); + CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -504,6 +505,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Web only if (_type == EntityTypes::Web) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi); } // PolyVoxel only @@ -726,6 +728,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); + COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); + _lastEdited = usecTimestampNow(); } @@ -903,6 +907,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); + ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -1065,6 +1071,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem if (properties.getType() == EntityTypes::Web) { APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); + APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } if (properties.getType() == EntityTypes::Text) { @@ -1364,6 +1371,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Web) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } if (properties.getType() == EntityTypes::Text) { @@ -1642,6 +1650,8 @@ void EntityItemProperties::markAllChanged() { _clientOnlyChanged = true; _owningAvatarIDChanged = true; + + _dpiChanged = true; } // The minimum bounding box for the entity. @@ -1977,6 +1987,10 @@ QList EntityItemProperties::listChangedProperties() { out += "ghostingAllowed"; } + if (dpiChanged()) { + out += "dpi"; + } + if (shapeChanged()) { out += "shape"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 22d740e0dd..4591dabc51 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -215,6 +215,8 @@ public: DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false); DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); + DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI); + static QString getBackgroundModeString(BackgroundMode mode); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 4ec0bea7df..3ab827a222 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -73,4 +73,6 @@ const bool ENTITY_ITEM_DEFAULT_BILLBOARDED = false; const QString ENTITY_ITEM_DEFAULT_NAME = QString(""); +const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30; + #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 0baef0fa25..e09db1e867 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -176,6 +176,7 @@ enum EntityPropertyList { PROP_OWNING_AVATAR_ID, // doesn't go over wire PROP_SHAPE, + PROP_DPI, PROP_LOCAL_VELOCITY, // only used to convert values to and from scripts PROP_LOCAL_ANGULAR_VELOCITY, // only used to convert values to and from scripts diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index c97eedc56a..1a8bf074d2 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -21,26 +21,8 @@ #include "EntityTree.h" #include "EntityTreeElement.h" -const float DEFAULT_DPI = 30.47f; - const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com"); -static float parseDPIFromUserData(QString str) { - QJsonParseError error; - auto doc = QJsonDocument::fromJson(str.toUtf8(), &error); - if (error.error != QJsonParseError::NoError) { - return DEFAULT_DPI; - } - QJsonObject obj = doc.object(); - - QJsonValue dpiValue = obj.value("dpi"); - if (!dpiValue.isDouble()) { - return DEFAULT_DPI; - } - double dpi = dpiValue.toDouble(); - return (float)dpi; -} - EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity { new WebEntityItem(entityID) }; entity->setProperties(properties); @@ -49,7 +31,7 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Web; - _dpi = DEFAULT_DPI; + _dpi = ENTITY_ITEM_DEFAULT_DPI; } const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f; @@ -62,6 +44,7 @@ void WebEntityItem::setDimensions(const glm::vec3& value) { EntityItemProperties WebEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI); return properties; } @@ -70,6 +53,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI); if (somethingChanged) { bool wantDebug = false; @@ -82,9 +66,6 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(properties._lastEdited); } - // AJT: TODO MAKE THIS A REAL PROPERTY - _dpi = parseDPIFromUserData(getUserData()); - return somethingChanged; } @@ -97,6 +78,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl); + READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI); return bytesRead; } @@ -106,6 +88,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_SOURCE_URL; + requestedProperties += PROP_DPI; return requestedProperties; } @@ -119,6 +102,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl); + APPEND_ENTITY_PROPERTY(PROP_DPI, _dpi); } bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -146,3 +130,11 @@ void WebEntityItem::setSourceUrl(const QString& value) { } const QString& WebEntityItem::getSourceUrl() const { return _sourceUrl; } + +void WebEntityItem::setDPI(uint16_t value) { + _dpi = value; +} + +uint16_t WebEntityItem::getDPI() const { + return _dpi; +} diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 2179f34e36..5a8097a9f1 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -54,9 +54,14 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; + virtual bool wantsHandControllerPointerEvents() const override { return true; } + + void setDPI(uint16_t value); + uint16_t getDPI() const; + protected: QString _sourceUrl; - float _dpi; + uint16_t _dpi; }; #endif // hifi_WebEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e9d61a827a..217c2ca3fb 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -47,7 +47,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS; + return VERSION_WEB_ENTITIES_SUPPORT_DPI; case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 40524e2288..54fdfb84c7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -186,6 +186,7 @@ const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59; const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61; const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62; +const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index 33b6c74c93..3525a3d726 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -34,10 +34,10 @@ WebBuddy = function (url) { shapeType: "box", userData: JSON.stringify({ "grabbableKey": {"grabbable": true}, - "dpi": 75 }), parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: NEGATIVE_ONE, + dpi: 45 }); this.state = "idle"; From d607abc8f7c51484fcc0f4e8a1092a8849763621 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 10 Aug 2016 16:25:20 -0700 Subject: [PATCH 04/14] Add dpi to edit.js Also, cap the maximum webEntity texture size to 4096 --- .../src/RenderableWebEntityItem.cpp | 21 ++++++++++++++++--- .../src/RenderableWebEntityItem.h | 1 + scripts/system/html/entityProperties.html | 4 ++++ scripts/system/html/js/entityProperties.js | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index b78cff3b25..bb3f508d9b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -33,6 +33,8 @@ static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100; // If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND; +static int MAX_WINDOW_SIZE = 4096; + EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity{ new RenderableWebEntityItem(entityID) }; entity->setProperties(properties); @@ -98,6 +100,19 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { return true; } +glm::vec2 RenderableWebEntityItem::getWindowSize() const { + 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; + } + + return dims; +} + void RenderableWebEntityItem::render(RenderArgs* args) { checkFading(); @@ -123,13 +138,13 @@ void RenderableWebEntityItem::render(RenderArgs* args) { } _lastRenderTime = usecTimestampNow(); - glm::vec2 dims = glm::vec2(getDimensions()); - dims *= METERS_TO_INCHES * _dpi; + glm::vec2 windowSize = getWindowSize(); + // The offscreen surface is idempotent for resizes (bails early // if it's a no-op), so it's safe to just call resize every frame // without worrying about excessive overhead. - _webSurface->resize(QSize(dims.x, dims.y)); + _webSurface->resize(QSize(windowSize.x, windowSize.y)); PerformanceTimer perfTimer("RenderableWebEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Web); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index ee8a484109..7bfd40864b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -47,6 +47,7 @@ public: private: bool buildWebSurface(EntityTreeRenderer* renderer); void destroyWebSurface(); + glm::vec2 getWindowSize() const; OffscreenQmlSurface* _webSurface{ nullptr }; QMetaObject::Connection _connection; diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 01826b8e10..55af62eb54 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -543,6 +543,10 @@ +
+ + +
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 5898f85e90..961758e16d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -427,6 +427,7 @@ function loaded() { var elWebSections = document.querySelectorAll(".web-section"); allSections.push(elWebSections); var elWebSourceURL = document.getElementById("property-web-source-url"); + var elWebDPI = document.getElementById("property-web-dpi"); var elDescription = document.getElementById("property-description"); @@ -706,6 +707,7 @@ function loaded() { } elWebSourceURL.value = properties.sourceUrl; + elWebDPI.value = properties.dpi; } else if (properties.type == "Text") { for (var i = 0; i < elTextSections.length; i++) { elTextSections[i].style.display = 'table'; @@ -959,6 +961,7 @@ function loaded() { elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); + elWebDPI.addEventListener('change', createEmitNumberPropertyUpdateFunction('dpi')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); From eafae1fcf373df514fdee375f8ebb6ed22d76384 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 13:26:35 -0700 Subject: [PATCH 05/14] Added tablet shell around web-buddy --- scripts/system/libraries/WebBuddy.js | 84 +++++++++++++++++++++------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebBuddy.js index 3525a3d726..cb6f29d943 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebBuddy.js @@ -7,44 +7,89 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + var NEGATIVE_ONE = 65535; +var RAD_TO_DEG = 180 / Math.PI; +var X_AXIS = {x: 1, y: 0, z: 0}; +var Y_AXIS = {x: 0, y: 1, z: 0}; + +var TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx"; + +// returns object with two fields: +// * position - position in front of the user +// * rotation - rotation of entity so it faces the user. +function calcSpawnInfo() { + var front; + var pitchBackRotation = Quat.angleAxis(20.0, X_AXIS); + if (HMD.active) { + front = Quat.getFront(HMD.orientation); + var yawOnlyRotation = Quat.angleAxis(Math.atan2(front.x, front.z) * RAD_TO_DEG, Y_AXIS); + return { + position: Vec3.sum(Vec3.sum(HMD.position, Vec3.multiply(0.6, front)), Vec3.multiply(-0.5, Y_AXIS)), + rotation: Quat.multiply(yawOnlyRotation, pitchBackRotation) + }; + } else { + front = Quat.getFront(MyAvatar.orientation); + return { + position: Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(0.6, front)), {x: 0, y: 0.6, z: 0}), + rotation: Quat.multiply(MyAvatar.orientation, pitchBackRotation) + }; + } +} + // ctor WebBuddy = function (url) { var ASPECT = 4.0 / 3.0; var WIDTH = 0.4; + var HEIGHT = WIDTH * ASPECT; + var DEPTH = 0.025; - var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), - {x: 0, y: 0.5, z: 0}); + var spawnInfo = calcSpawnInfo(); + + var tabletEntityPosition = spawnInfo.position; + var tabletEntityRotation = spawnInfo.rotation; + this.tabletEntityID = Entities.addEntity({ + name: "tablet", + type: "Model", + modelURL: TABLET_URL, + position: tabletEntityPosition, + rotation: tabletEntityRotation, + userData: JSON.stringify({ + "grabbableKey": {"grabbable": true} + }), + dimensions: {x: WIDTH, y: HEIGHT, z: DEPTH}, + parentID: MyAvatar.sessionUUID, + parentJointIndex: NEGATIVE_ONE + }); + + var WEB_ENTITY_REDUCTION_FACTOR = {x: 0.78, y: 0.85}; + var WEB_ENTITY_Z_OFFSET = -0.01; + + var webEntityRotation = Quat.multiply(spawnInfo.rotation, Quat.angleAxis(180, Y_AXIS)); + var webEntityPosition = Vec3.sum(spawnInfo.position, Vec3.multiply(WEB_ENTITY_Z_OFFSET, Quat.getFront(webEntityRotation))); - var webEntityPosition = spawnPoint; - var webEntityRotation = MyAvatar.orientation; this.webEntityID = Entities.addEntity({ + name: "web", type: "Web", sourceUrl: url, - dimensions: {x: WIDTH, y: WIDTH * ASPECT, z: 0.1}, + dimensions: {x: WIDTH * WEB_ENTITY_REDUCTION_FACTOR.x, + y: HEIGHT * WEB_ENTITY_REDUCTION_FACTOR.y, + z: 0.1}, position: webEntityPosition, rotation: webEntityRotation, - name: "web", - dynamic: true, - angularDamping: 0.9, - damping: 0.9, - gravity: {x: 0, y: 0, z: 0}, shapeType: "box", - userData: JSON.stringify({ - "grabbableKey": {"grabbable": true}, - }), - parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE, - dpi: 45 + dpi: 45, + parentID: this.tabletEntityID, + parentJointIndex: NEGATIVE_ONE }); this.state = "idle"; // compute the room/sensor matrix of the entity. var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - var entityWorldMat = Mat4.createFromRotAndTrans(webEntityRotation, webEntityPosition); + var entityWorldMat = Mat4.createFromRotAndTrans(tabletEntityRotation, tabletEntityPosition); this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); var _this = this; @@ -56,12 +101,13 @@ WebBuddy = function (url) { WebBuddy.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); + Entities.deleteEntity(this.tabletEntityID); Script.update.disconnect(this.updateFunc); }; WebBuddy.prototype.update = function (dt) { - var props = Entities.getEntityProperties(this.webEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); + var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); var entityWorldMat; if (this.state === "idle") { @@ -76,7 +122,7 @@ WebBuddy.prototype.update = function (dt) { entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); // slam the world space position and orientation - Entities.editEntity(this.webEntityID, { + Entities.editEntity(this.tabletEntityID, { position: Mat4.extractTranslation(entityWorldMat), rotation: Mat4.extractRotation(entityWorldMat) }); From af2c31f29b925c979ed70787270f61d1ca9b8a14 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 13:33:16 -0700 Subject: [PATCH 06/14] Renamed WebBuddy to WebTablet --- .../libraries/{WebBuddy.js => WebTablet.js} | 8 ++++---- scripts/system/marketplace.js | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) rename scripts/system/libraries/{WebBuddy.js => WebTablet.js} (97%) diff --git a/scripts/system/libraries/WebBuddy.js b/scripts/system/libraries/WebTablet.js similarity index 97% rename from scripts/system/libraries/WebBuddy.js rename to scripts/system/libraries/WebTablet.js index cb6f29d943..6a0a3fcbb1 100644 --- a/scripts/system/libraries/WebBuddy.js +++ b/scripts/system/libraries/WebTablet.js @@ -1,5 +1,5 @@ // -// WebBuddy.js +// WebTablet.js // // Created by Anthony J. Thibault on 8/8/2016 // Copyright 2016 High Fidelity, Inc. @@ -39,7 +39,7 @@ function calcSpawnInfo() { } // ctor -WebBuddy = function (url) { +WebTablet = function (url) { var ASPECT = 4.0 / 3.0; var WIDTH = 0.4; @@ -99,13 +99,13 @@ WebBuddy = function (url) { Script.update.connect(this.updateFunc); }; -WebBuddy.prototype.destroy = function () { +WebTablet.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); Entities.deleteEntity(this.tabletEntityID); Script.update.disconnect(this.updateFunc); }; -WebBuddy.prototype.update = function (dt) { +WebTablet.prototype.update = function (dt) { var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); var entityWorldMat; diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 26d46d6219..af0be21077 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -8,8 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global WebBuddy */ -Script.include("./libraries/WebBuddy.js"); +/* global WebTablet */ +Script.include("./libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("assets/images/tools/"); @@ -26,17 +26,17 @@ var toolHeight = 50; var toolWidth = 50; var TOOLBAR_MARGIN_Y = 0; var marketplaceVisible = false; -var marketplaceWebBuddy; +var marketplaceWebTablet; -function shouldShowWebBuddyMarketplace() { +function shouldShowWebTablet() { var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); return HMD.active && (leftPose.valid || rightPose.valid); } function showMarketplace(marketplaceID) { - if (shouldShowWebBuddyMarketplace()) { - marketplaceWebBuddy = new WebBuddy("https://metaverse.highfidelity.com/marketplace"); + if (shouldShowWebTablet()) { + marketplaceWebTablet = new WebTablet("https://metaverse.highfidelity.com/marketplace"); } else { var url = MARKETPLACE_URL; if (marketplaceID) { @@ -55,8 +55,8 @@ function hideMarketplace() { marketplaceWindow.setVisible(false); marketplaceWindow.setURL("about:blank"); } else { - marketplaceWebBuddy.destroy(); - marketplaceWebBuddy = null; + marketplaceWebTablet.destroy(); + marketplaceWebTablet = null; } marketplaceVisible = false; } From df17913801b8ae2edc76f6b50445bbed68f7af2e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 18:18:13 -0700 Subject: [PATCH 07/14] Mipmap generation, trilinear,anisotropic filtering for OffscreenQmlSurface --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 4 ++- libraries/gl/src/gl/OglplusHelpers.cpp | 32 ++++++++++++++------- libraries/gl/src/gl/OglplusHelpers.h | 2 ++ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 366bc96ca3..c47312f9f6 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -166,7 +166,7 @@ private: QMyQuickRenderControl* _renderControl{ nullptr }; FramebufferPtr _fbo; RenderbufferPtr _depthStencil; - TextureRecycler _textures; + TextureRecycler _textures { true }; GLTextureEscrow _escrow; uint64_t _lastRenderTime{ 0 }; @@ -399,6 +399,8 @@ void OffscreenQmlRenderThread::render() { glGetError(); } + Context::Bound(oglplus::Texture::Target::_2D, *texture).GenerateMipmap(); + // FIXME probably unecessary DefaultFramebuffer().Bind(Framebuffer::Target::Draw); _quickWindow->resetOpenGLState(); diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 1154042b4a..d31800dc4c 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -509,16 +509,28 @@ TexturePtr TextureRecycler::getNextTexture() { using namespace oglplus; if (_readyTextures.empty()) { TexturePtr newTexture(new Texture()); - Context::Bound(oglplus::Texture::Target::_2D, *newTexture) - .MinFilter(TextureMinFilter::Linear) - .MagFilter(TextureMagFilter::Linear) - .WrapS(TextureWrap::ClampToEdge) - .WrapT(TextureWrap::ClampToEdge) - .Image2D( - 0, PixelDataInternalFormat::RGBA8, - _size.x, _size.y, - 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr - ); + + if (_useMipmaps) { + Context::Bound(oglplus::Texture::Target::_2D, *newTexture) + .MinFilter(TextureMinFilter::LinearMipmapLinear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Anisotropy(8.0f) + .LODBias(-0.2f) + .Image2D(0, PixelDataInternalFormat::RGBA8, + _size.x, _size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr); + } else { + Context::Bound(oglplus::Texture::Target::_2D, *newTexture) + .MinFilter(TextureMinFilter::Linear) + .MagFilter(TextureMagFilter::Linear) + .WrapS(TextureWrap::ClampToEdge) + .WrapT(TextureWrap::ClampToEdge) + .Image2D(0, PixelDataInternalFormat::RGBA8, + _size.x, _size.y, + 0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr); + } GLuint texId = GetName(*newTexture); _allTextures[texId] = TexInfo{ newTexture, _size }; _readyTextures.push(newTexture); diff --git a/libraries/gl/src/gl/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h index fe5822c4be..ab47689312 100644 --- a/libraries/gl/src/gl/OglplusHelpers.h +++ b/libraries/gl/src/gl/OglplusHelpers.h @@ -190,6 +190,7 @@ using BasicFramebufferWrapperPtr = std::shared_ptr; class TextureRecycler { public: + TextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {} void setSize(const uvec2& size); void clear(); TexturePtr getNextTexture(); @@ -212,4 +213,5 @@ private: Map _allTextures; Queue _readyTextures; uvec2 _size{ 1920, 1080 }; + bool _useMipmaps; }; From e7dd9c44780b5c32a06f292617edc20758e3d1f9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 17 Aug 2016 18:19:10 -0700 Subject: [PATCH 08/14] handheld marketplace should work for hydras --- scripts/system/marketplace.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index af0be21077..cb82ab9060 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -31,7 +31,8 @@ var marketplaceWebTablet; function shouldShowWebTablet() { var rightPose = Controller.getPoseValue(Controller.Standard.RightHand); var leftPose = Controller.getPoseValue(Controller.Standard.LeftHand); - return HMD.active && (leftPose.valid || rightPose.valid); + var hasHydra = !!Controller.Hardware.Hydra; + return HMD.active && (leftPose.valid || rightPose.valid || hasHydra); } function showMarketplace(marketplaceID) { From c6ea64926c6770834f2879f4e5c44fcfc93a32ef Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 17:04:56 -0700 Subject: [PATCH 09/14] Sensor space entity support You can do this by parenting an entity to an avatar's -2 joint index. This will mean that the entity will follow the avatar as it moves in the world, but will not follow the avatar's position as it moves in sensor space. Essentially, this gives you the ability to place objects in the user's physical room. WebTablets now are located in this feature and no longer jitter. --- interface/src/avatar/Avatar.cpp | 32 ++++++++--- interface/src/avatar/MyAvatar.cpp | 11 ++-- interface/src/avatar/MyAvatar.h | 13 ++--- libraries/avatars/src/AvatarData.cpp | 44 +++++++++++---- libraries/avatars/src/AvatarData.h | 9 ++++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- scripts/system/libraries/WebTablet.js | 53 +------------------ 8 files changed, 81 insertions(+), 86 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d3a38271a0..31fb2abe53 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -59,6 +59,8 @@ const float DISPLAYNAME_ALPHA = 1.0f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); +const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; + namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { return ItemKey::Builder::opaqueShape(); @@ -851,15 +853,33 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { - glm::quat rotation; - _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation); - return Quaternions::Y_180 * rotation; + if (index == SENSOR_TO_WORLD_MATRIX_INDEX) { + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + bool success; + Transform avatarTransform; + Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); + glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); + return glmExtractRotation(invAvatarMat * sensorToWorldMatrix); + } else { + glm::quat rotation; + _skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation); + return Quaternions::Y_180 * rotation; + } } glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const { - glm::vec3 translation; - _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation); - return Quaternions::Y_180 * translation; + if (index == SENSOR_TO_WORLD_MATRIX_INDEX) { + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + bool success; + Transform avatarTransform; + Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform()); + glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix(); + return extractTranslation(invAvatarMat * sensorToWorldMatrix); + } else { + glm::vec3 translation; + _skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation); + return Quaternions::Y_180 * translation; + } } int Avatar::getJointIndex(const QString& name) const { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 782ecbcc64..5687b5025c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -107,7 +107,6 @@ MyAvatar::MyAvatar(RigPointer rig) : _hmdSensorOrientation(), _hmdSensorPosition(), _bodySensorMatrix(), - _sensorToWorldMatrix(), _goToPending(false), _goToPosition(), _goToOrientation(), @@ -511,13 +510,9 @@ void MyAvatar::simulate(float deltaTime) { updateAvatarEntities(); } -// thread-safe -glm::mat4 MyAvatar::getSensorToWorldMatrix() const { - return _sensorToWorldMatrixCache.get(); -} - -// As far as I know no HMD system supports a play area of a kilometer in radius. +// As far as I know no HMD system supports a play area of a kilometer in radius. static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f; + // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. @@ -526,7 +521,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorMatrix = hmdSensorMatrix; auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix); - if (newHmdSensorPosition != _hmdSensorPosition && + if (newHmdSensorPosition != _hmdSensorPosition && glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) { qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition; // Ignore unreasonable HMD sensor data diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d8ff679db6..1f212a1fec 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -79,8 +79,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose) Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose) - Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) - Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) @@ -110,9 +108,6 @@ public: const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; } - // thread safe - Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; - Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); Q_INVOKABLE QVariant getOrientationVar() const; @@ -415,6 +410,10 @@ private: bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + // working copy of sensorToWorldMatrix. + // See AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access + glm::mat4 _sensorToWorldMatrix; + // cache of the current HMD sensor position and orientation // in sensor space. glm::mat4 _hmdSensorMatrix; @@ -427,10 +426,6 @@ private: // in sensor space. glm::mat4 _bodySensorMatrix; - // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. - glm::mat4 _sensorToWorldMatrix; - ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; - struct FollowHelper { FollowHelper(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d0c7b3912c..e90f063cff 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -53,15 +53,18 @@ namespace AvatarDataPacket { // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. PACKED_BEGIN struct Header { - float position[3]; // skeletal model's position - float globalPosition[3]; // avatar's position - uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to - uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag. - float lookAtPosition[3]; // world space position that eyes are focusing on. - float audioLoudness; // current loundess of microphone + float position[3]; // skeletal model's position + float globalPosition[3]; // avatar's position + uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to + uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag. + float lookAtPosition[3]; // world space position that eyes are focusing on. + float audioLoudness; // current loundess of microphone + uint8_t sensorToWorldQuat[6]; // 6 byte compressed quaternion part of sensor to world matrix + uint16_t sensorToWorldScale; // uniform scale of sensor to world matrix + float sensorToWorldTrans[3]; // fourth column of sensor to world matrix uint8_t flags; } PACKED_END; - const size_t HEADER_SIZE = 49; + const size_t HEADER_SIZE = 69; // only present if HAS_REFERENTIAL flag is set in header.flags PACKED_BEGIN struct ParentInfo { @@ -93,6 +96,9 @@ namespace AvatarDataPacket { */ } +static const int TRANSLATION_COMPRESSION_RADIX = 12; +static const int SENSOR_TO_WORLD_SCALE_RADIX = 10; + #define ASSERT(COND) do { if (!(COND)) { abort(); } } while(0) AvatarData::AvatarData() : @@ -210,6 +216,14 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { header->lookAtPosition[2] = _headData->_lookAtPosition.z; header->audioLoudness = _headData->_audioLoudness; + glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix(); + packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); + glm::vec3 scale = extractScale(sensorToWorldMatrix); + packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); + header->sensorToWorldTrans[0] = sensorToWorldMatrix[0][3]; + header->sensorToWorldTrans[1] = sensorToWorldMatrix[1][3]; + header->sensorToWorldTrans[2] = sensorToWorldMatrix[2][3]; + setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); // hand state bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; @@ -346,8 +360,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { *destinationBuffer++ = validity; } - const int TRANSLATION_COMPRESSION_RADIX = 12; - validityBit = 0; validity = *validityPosition++; for (int i = 0; i < _jointData.size(); i ++) { @@ -500,6 +512,14 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } _headData->_audioLoudness = audioLoudness; + glm::quat sensorToWorldQuat; + unpackOrientationQuatFromSixBytes(header->sensorToWorldQuat, sensorToWorldQuat); + float sensorToWorldScale; + unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); + glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]); + glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); + _sensorToWorldMatrixCache.set(sensorToWorldMatrix); + { // bitFlags and face data uint8_t bitItems = header->flags; @@ -616,7 +636,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // each joint translation component is stored in 6 bytes. const int COMPRESSED_TRANSLATION_SIZE = 6; PACKET_READ_CHECK(JointTranslation, numValidJointTranslations * COMPRESSED_TRANSLATION_SIZE); - const int TRANSLATION_COMPRESSION_RADIX = 12; for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; @@ -1718,6 +1737,11 @@ AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { return result; } +// thread-safe +glm::mat4 AvatarData::getSensorToWorldMatrix() const { + return _sensorToWorldMatrixCache.get(); +} + QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) { QScriptValue obj = engine->newObject(); obj.setProperty("intersects", value.intersects); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b88cec3e05..572657e921 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -54,6 +54,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include "AABox.h" #include "HeadData.h" @@ -171,6 +172,8 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) + Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) + public: static const QString FRAME_NAME; @@ -351,6 +354,9 @@ public: void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyDetachedIDs(); + // thread safe + Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -425,6 +431,9 @@ protected: bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; + // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. + ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 217c2ca3fb..0d25d4f1be 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -52,7 +52,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::AbsoluteSixByteRotations); + return static_cast(AvatarMixerPacketVersion::SensorToWorldMat); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 54fdfb84c7..3ecdb75a18 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -192,7 +192,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, SoftAttachmentSupport, AvatarEntities, - AbsoluteSixByteRotations + AbsoluteSixByteRotations, + SensorToWorldMat }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 6a0a3fcbb1..34368e475b 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -8,8 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var NEGATIVE_ONE = 65535; - var RAD_TO_DEG = 180 / Math.PI; var X_AXIS = {x: 1, y: 0, z: 0}; var Y_AXIS = {x: 0, y: 1, z: 0}; @@ -61,7 +59,7 @@ WebTablet = function (url) { }), dimensions: {x: WIDTH, y: HEIGHT, z: DEPTH}, parentID: MyAvatar.sessionUUID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: -2 }); var WEB_ENTITY_REDUCTION_FACTOR = {x: 0.78, y: 0.85}; @@ -82,61 +80,14 @@ WebTablet = function (url) { shapeType: "box", dpi: 45, parentID: this.tabletEntityID, - parentJointIndex: NEGATIVE_ONE + parentJointIndex: -1 }); this.state = "idle"; - - // compute the room/sensor matrix of the entity. - var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - var entityWorldMat = Mat4.createFromRotAndTrans(tabletEntityRotation, tabletEntityPosition); - this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); - - var _this = this; - this.updateFunc = function (dt) { - _this.update(dt); - }; - Script.update.connect(this.updateFunc); }; WebTablet.prototype.destroy = function () { Entities.deleteEntity(this.webEntityID); Entities.deleteEntity(this.tabletEntityID); - Script.update.disconnect(this.updateFunc); -}; - -WebTablet.prototype.update = function (dt) { - - var props = Entities.getEntityProperties(this.tabletEntityID, ["position", "rotation", "parentID", "parentJointIndex"]); - var entityWorldMat; - - if (this.state === "idle") { - - if (props.parentID !== MyAvatar.sessionUUID || props.parentJointIndex !== NEGATIVE_ONE) { - this.state = "held"; - return; - } - - // convert the sensor/room matrix of the entity into world space, using the current sensorToWorldMatrix - var roomMat = MyAvatar.sensorToWorldMatrix; - entityWorldMat = Mat4.multiply(roomMat, this.entityRoomMat); - - // slam the world space position and orientation - Entities.editEntity(this.tabletEntityID, { - position: Mat4.extractTranslation(entityWorldMat), - rotation: Mat4.extractRotation(entityWorldMat) - }); - } else if (this.state === "held") { - if (props.parentID === MyAvatar.sessionUUID && props.parentJointIndex === NEGATIVE_ONE) { - - // re-compute the room/sensor matrix for the avatar now that it has been released. - var invRoomMat = Mat4.inverse(MyAvatar.sensorToWorldMatrix); - entityWorldMat = Mat4.createFromRotAndTrans(props.rotation, props.position); - this.entityRoomMat = Mat4.multiply(invRoomMat, entityWorldMat); - - this.state = "idle"; - return; - } - } }; From 6be737993ebc0580e4f1d50305a0660690f9b208 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 18:18:49 -0700 Subject: [PATCH 10/14] bug fix for sensorToWorld translation transmission --- libraries/avatars/src/AvatarData.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e90f063cff..9088ee0577 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -220,9 +220,9 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix)); glm::vec3 scale = extractScale(sensorToWorldMatrix); packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX); - header->sensorToWorldTrans[0] = sensorToWorldMatrix[0][3]; - header->sensorToWorldTrans[1] = sensorToWorldMatrix[1][3]; - header->sensorToWorldTrans[2] = sensorToWorldMatrix[2][3]; + header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0]; + header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1]; + header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2]; setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState); // hand state @@ -518,6 +518,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]); glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); + _sensorToWorldMatrixCache.set(sensorToWorldMatrix); { // bitFlags and face data From b2dff8aa77f28c130efddc474f330d940b790752 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 15:25:46 -0700 Subject: [PATCH 11/14] Easier to click buttons on web entities with shaky hand controllers There is an angular and time dead spot on webEntity for scrolling vs clicking. Currently, it's 150 ms and 3 degrees. See POINTER_PRESS_TO_MOVE_DELAY and POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE * Fix for warnings when clicking on window.open() links in WebEntity --- interface/resources/qml/controls/WebView.qml | 4 +- .../src/RenderableWebEntityItem.cpp | 2 + .../system/controllers/handControllerGrab.js | 60 +++++++++++++------ .../controllers/handControllerPointer.js | 2 +- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index bba91c64f6..7285db22d2 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -55,9 +55,11 @@ WebEngineView { } onNewViewRequested:{ + if (desktop) { var component = Qt.createComponent("../Browser.qml"); var newWindow = component.createObject(desktop); - request.openIn(newWindow.webView) + request.openIn(newWindow.webView); + } } // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bb3f508d9b..9221fec140 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); + _webSurface->getRootContext()->setContextProperty("desktop", QVariant()); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { _texture = textureId; }); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ab1fe76a91..ac1c844cc9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -234,6 +234,10 @@ CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { updateMethod: "entityTouching" }; +function angleBetween(a, b) { + return Math.acos(Vec3.dot(Vec3.normalize(a), Vec3.normalize(b))); +} + function projectOntoEntityXYPlane(entityID, worldPos) { var props = entityPropertiesCache.getProps(entityID); var invRot = Quat.inverse(props.rotation); @@ -1975,7 +1979,8 @@ function MyController(hand) { var handPosition = this.getHandPosition(); // the center of the equipped object being far from the hand isn't enough to auto-unequip -- we also // need to fail the findEntities test. - var nearPickedCandidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + var TEAR_AWAY_DISTANCE = 0.04; + var nearPickedCandidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS + TEAR_AWAY_DISTANCE); if (nearPickedCandidateEntities.indexOf(this.grabbedEntity) == -1) { // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." + @@ -2128,6 +2133,11 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + + this.touchingEnterTimer = 0; + this.touchingEnterPointerEvent = pointerEvent; + this.touchingEnterPointerEvent.button = "None"; + this.deadspotExpired = false; } }; @@ -2135,27 +2145,37 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); if (intersectInfo) { - var pointerEvent = { - type: "Release", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; + var pointerEvent; + if (this.deadspotExpired) { + pointerEvent = { + type: "Release", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: false, + isSecondaryButton: false, + isTertiaryButton: false + }; + } else { + pointerEvent = this.touchingEnterPointerEvent; + pointerEvent.button = "Primary"; + pointerEvent.isPrimaryButton = false; + } Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); - this.focusedEntity = null; } + this.focusedEntity = null; }; - this.entityTouching = function() { + this.entityTouching = function(dt) { + + this.touchingEnterTimer += dt; + entityPropertiesCache.addEntity(this.grabbedEntity); if (!this.triggerSmoothedGrab()) { @@ -2184,8 +2204,14 @@ function MyController(hand) { isTertiaryButton: false }; - Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + var POINTER_PRESS_TO_MOVE_DELAY = 0.15; // seconds + var POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE = 0.05; // radians ~ 3 degrees + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + angleBetween(pointerEvent.direction, this.touchingEnterPointerEvent.direction) > POINTER_PRESS_TO_MOVE_DEADSPOT_ANGLE) { + Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + this.deadspotExpired = true; + } this.intersectionDistance = intersectInfo.distance; this.searchIndicatorOn(intersectInfo.searchRay); diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 4d6e9d7f60..5888b53a27 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -121,7 +121,7 @@ function ignoreMouseActivity() { return true; } var pos = Reticle.position; - if (pos.x == -1 && pos.y == -1) { + if (!pos || (pos.x == -1 && pos.y == -1)) { return true; } // Only we know if we moved it, which is why this script has to replace depthReticle.js From 1939c6915aee67d19b85701915d0aec8dee1a25d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 10:05:27 -0700 Subject: [PATCH 12/14] Bug-fixes for marketplace toggling --- scripts/system/marketplace.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index cb82ab9060..11b5a084cf 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -55,7 +55,7 @@ function hideMarketplace() { if (marketplaceWindow.visible) { marketplaceWindow.setVisible(false); marketplaceWindow.setURL("about:blank"); - } else { + } else if (marketplaceWebTablet) { marketplaceWebTablet.destroy(); marketplaceWebTablet = null; } @@ -81,16 +81,19 @@ var browseExamplesButton = toolBar.addButton({ alpha: 0.9 }); -function onExamplesWindowVisibilityChanged() { +function onMarketplaceWindowVisibilityChanged() { browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1); browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1); browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3); + marketplaceVisible = marketplaceWindow.visible; } + function onClick() { toggleMarketplace(); } + browseExamplesButton.clicked.connect(onClick); -marketplaceWindow.visibleChanged.connect(onExamplesWindowVisibilityChanged); +marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); Script.scriptEnding.connect(function () { toolBar.removeButton("marketplace"); From fce207ba5daf9d2219d5edb895f1099b2ab07f92 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 15:31:13 -0700 Subject: [PATCH 13/14] eslint fix --- scripts/system/marketplace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplace.js b/scripts/system/marketplace.js index 11b5a084cf..e4f439f30d 100644 --- a/scripts/system/marketplace.js +++ b/scripts/system/marketplace.js @@ -98,5 +98,5 @@ marketplaceWindow.visibleChanged.connect(onMarketplaceWindowVisibilityChanged); Script.scriptEnding.connect(function () { toolBar.removeButton("marketplace"); browseExamplesButton.clicked.disconnect(onClick); - marketplaceWindow.visibleChanged.disconnect(onExamplesWindowVisibilityChanged); + marketplaceWindow.visibleChanged.disconnect(onMarketplaceWindowVisibilityChanged); }); From c234f1b65deaa5a035a4a88bce395ee389d1ec9c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 24 Aug 2016 16:26:53 -0700 Subject: [PATCH 14/14] marketplace.js: merge fix for WebTablet.js path --- scripts/system/marketplaces/marketplace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplace.js b/scripts/system/marketplaces/marketplace.js index bb036f92d8..2bd6033e62 100644 --- a/scripts/system/marketplaces/marketplace.js +++ b/scripts/system/marketplaces/marketplace.js @@ -9,7 +9,7 @@ // /* global WebTablet */ -Script.include("./libraries/WebTablet.js"); +Script.include("../libraries/WebTablet.js"); var toolIconUrl = Script.resolvePath("../assets/images/tools/");