From bfc485394746204e4ebbede093e82b016080f6f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 22 Feb 2017 16:50:02 -0800 Subject: [PATCH] make overlays near-grabbable. make tablet be an overlay --- interface/src/ui/overlays/Base3DOverlay.cpp | 11 +- interface/src/ui/overlays/Base3DOverlay.h | 3 + interface/src/ui/overlays/Overlays.cpp | 22 +++ interface/src/ui/overlays/Overlays.h | 10 ++ .../system/controllers/handControllerGrab.js | 131 ++++++++++++++---- scripts/system/libraries/WebTablet.js | 23 ++- 6 files changed, 165 insertions(+), 35 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index ff5177ed3a..cf4291cce7 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -39,7 +39,8 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), _drawInFront(base3DOverlay->_drawInFront), - _isAA(base3DOverlay->_isAA) + _isAA(base3DOverlay->_isAA), + _isGrabbable(base3DOverlay->_isGrabbable) { setTransform(base3DOverlay->getTransform()); } @@ -125,6 +126,11 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { needRenderItemUpdate = true; } + auto isGrabbable = properties["grabbable"]; + if (isGrabbable.isValid()) { + setIsGrabbable(isGrabbable.toBool()); + } + if (properties["position"].isValid()) { setLocalPosition(vec3FromVariant(properties["position"])); needRenderItemUpdate = true; @@ -227,6 +233,9 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "drawInFront") { return _drawInFront; } + if (property == "grabbable") { + return _isGrabbable; + } if (property == "parentID") { return getParentID(); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 7906b9d6c0..a1c23e5cd8 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -38,6 +38,7 @@ public: bool getIsSolidLine() const { return !_isDashedLine; } bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; } bool getDrawInFront() const { return _drawInFront; } + bool getIsGrabbable() const { return _isGrabbable; } virtual bool isAA() const { return _isAA; } @@ -47,6 +48,7 @@ public: void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; } void setDrawInFront(bool value) { _drawInFront = value; } void setIsAA(bool value) { _isAA = value; } + void setIsGrabbable(bool value) { _isGrabbable = value; } virtual AABox getBounds() const override = 0; @@ -71,6 +73,7 @@ protected: bool _ignoreRayIntersection; bool _drawInFront; bool _isAA; + bool _isGrabbable { false }; }; #endif // hifi_Base3DOverlay_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index c18d9ddaef..00ffaddff3 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -803,3 +803,25 @@ void Overlays::mouseMoveEvent(QMouseEvent* event) { } } } + +QVector Overlays::findOverlays(const glm::vec3& center, float radius) const { + QVector result; + glm::vec3 penetration; + + QMapIterator i(_overlaysWorld); + i.toBack(); + while (i.hasPrevious()) { + i.previous(); + OverlayID thisID = i.key(); + auto thisOverlay = std::dynamic_pointer_cast(i.value()); + if (thisOverlay && thisOverlay->getVisible() && !thisOverlay->getIgnoreRayIntersection() && thisOverlay->isLoaded()) { + // AABox overlayAABox; + // overlayAABox.findSpherePenetration(center, radius, penetration); + if (glm::distance(thisOverlay->getPosition(), center) <= radius) { + result.append(thisID); + } + } + } + + return result; +} diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 7c6ba34f58..38f62fee48 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -206,6 +206,16 @@ public slots: bool visibleOnly = false, bool collidableOnly = false); + /**jsdoc + * Return a list of overlays with centers within a given distance of a point + * + * @function Overlays.findOverlays + * @param {Vec3} center the point to search from. + * @param {float} radius search radius + * @return {List of Overlays.OverlayID} list of overlays withing the radius + */ + QVector findOverlays(const glm::vec3& center, float radius) const; + /**jsdoc * Check whether an overlay's assets have been loaded. For example, if the * overlay is an "image" overlay, this will indicate whether the its image diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ea76490b7b..ba22abf426 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications, - Menu, HMD */ + Menu, HMD, isInEditMode */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -28,7 +28,7 @@ Script.include("/~/system/libraries/controllers.js"); // var WANT_DEBUG = false; -var WANT_DEBUG_STATE = false; +var WANT_DEBUG_STATE = true; var WANT_DEBUG_SEARCH_NAME = null; var FORCE_IGNORE_IK = false; @@ -399,7 +399,7 @@ function entityHasActions(entityID) { function findRayIntersection(pickRay, precise, include, exclude) { var entities = Entities.findRayIntersection(pickRay, precise, include, exclude, true); - var overlays = Overlays.findRayIntersection(pickRay); + var overlays = Overlays.findRayIntersection(pickRay, precise, [], [HMD.tabletID]); if (!overlays.intersects || (entities.intersects && (entities.distance <= overlays.distance))) { return entities; } @@ -853,6 +853,9 @@ function MyController(hand) { }; this.callEntityMethodOnGrabbed = function(entityMethodName) { + if (this.grabbedIsOverlay) { + return; + } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args); }; @@ -1175,6 +1178,13 @@ function MyController(hand) { } } + var candidateOverlays = Overlays.findOverlays(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE); + for (var j = 0; j < candidateOverlays.length; j++) { + if (this.isTablet(candidateOverlays[j])) { + nearWeb = true; + } + } + if (nearWeb) { this.showStylus(); var rayPickInfo = this.calcRayPickInfo(this.hand); @@ -1622,6 +1632,13 @@ function MyController(hand) { return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); }); + var candidateOverlays = Overlays.findOverlays(handPosition, /*NEAR_GRAB_RADIUS*/ 0.4); + var grabbableOverlays = candidateOverlays.filter(function(overlayID) { + return Overlays.getProperty(overlayID, "grabbable"); + }); + + print("candidateOverlays: " + candidateOverlays.length + ", grabbableOverlays: " + grabbableOverlays.length); + if (rayPickInfo.entityID) { this.intersectionDistance = rayPickInfo.distance; if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { @@ -1633,6 +1650,16 @@ function MyController(hand) { this.intersectionDistance = 0; } + if (grabbableOverlays.length > 0) { + this.grabbedEntity = grabbableOverlays[0]; // XXX + this.grabbedIsOverlay = true; + if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { + this.setState(STATE_NEAR_GRABBING, "near grab overlay '" + + Overlays.getProperty(this.grabbedEntity, "name") + "'"); + return; + } + } + var entity; if (grabbableEntities.length > 0) { // sort by distance @@ -1644,6 +1671,7 @@ function MyController(hand) { entity = grabbableEntities[0]; name = entityPropertiesCache.getProps(entity).name; this.grabbedEntity = entity; + this.grabbedIsOverlay = false; if (this.entityWantsTrigger(entity)) { if (this.triggerSmoothedGrab()) { this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); @@ -1654,7 +1682,7 @@ function MyController(hand) { } else { // If near something grabbable, grab it! if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + this.setState(STATE_NEAR_GRABBING, "near grab entity '" + name + "'"); return; } else { // potentialNearGrabEntity = entity; @@ -1678,6 +1706,7 @@ function MyController(hand) { if (this.entityWantsTrigger(entity)) { if (this.triggerSmoothedGrab()) { this.grabbedEntity = entity; + this.grabbedIsOverlay = false; this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); return; } else { @@ -1686,6 +1715,7 @@ function MyController(hand) { } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { this.grabbedEntity = entity; + this.grabbedIsOverlay = false; this.grabbedDistance = rayPickInfo.distance; this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); return; @@ -1766,6 +1796,7 @@ function MyController(hand) { } this.grabbedEntity = entity; + this.grabbedIsOverlay = false; this.setState(STATE_ENTITY_STYLUS_TOUCHING, "begin touching entity '" + name + "'"); return true; @@ -1894,6 +1925,7 @@ function MyController(hand) { if (this.triggerSmoothedGrab() && (!isEditing() || this.isTablet(entity))) { this.grabbedEntity = entity; + this.grabbedIsOverlay = false; this.setState(STATE_ENTITY_LASER_TOUCHING, "begin touching entity '" + name + "'"); return true; } @@ -2282,12 +2314,25 @@ function MyController(hand) { this.grabbedEntity = saveGrabbedID; } - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - if (FORCE_IGNORE_IK) { + var grabbedProperties; + if (this.grabbedIsOverlay) { + grabbedProperties = { + position: Overlays.getProperty(this.grabbedEntity, "position"), + rotation: Overlays.getProperty(this.grabbedEntity, "rotation"), + parentID: Overlays.getProperty(this.grabbedEntity, "parentID"), + parentJointIndex: Overlays.getProperty(this.grabbedEntity, "parentJointIndex"), + dynamic: false, + shapeType: "none" + }; this.ignoreIK = true; } else { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; + grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + if (FORCE_IGNORE_IK) { + this.ignoreIK = true; + } else { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; + } } var handRotation; @@ -2326,7 +2371,8 @@ function MyController(hand) { this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); } - var isPhysical = propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity); + var isPhysical = propsArePhysical(grabbedProperties) || + (!this.grabbedIsOverlay && entityHasActions(this.grabbedEntity)); if (isPhysical && this.state == STATE_NEAR_GRABBING && grabbedProperties.parentID === NULL_UUID) { // grab entity via action if (!this.setupHoldAction()) { @@ -2359,7 +2405,12 @@ function MyController(hand) { reparentProps.localPosition = this.offsetPosition; reparentProps.localRotation = this.offsetRotation; } - Entities.editEntity(this.grabbedEntity, reparentProps); + + if (this.grabbedIsOverlay) { + Overlays.editOverlay(this.grabbedEntity, reparentProps); + } else { + Entities.editEntity(this.grabbedEntity, reparentProps); + } if (this.thisHandIsParent(grabbedProperties)) { // this should never happen, but if it does, don't set previous parent to be this hand. @@ -2377,11 +2428,13 @@ function MyController(hand) { })); } - Entities.editEntity(this.grabbedEntity, { - velocity: { x: 0, y: 0, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0 }, - // dynamic: false - }); + if (!this.grabbedIsOverlay) { + Entities.editEntity(this.grabbedEntity, { + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + // dynamic: false + }); + } if (this.state == STATE_NEAR_GRABBING) { this.callEntityMethodOnGrabbed("startNearGrab"); @@ -2464,9 +2517,22 @@ function MyController(hand) { this.prevDropDetected = dropDetected; } - var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "parentJointIndex", + var props; + if (this.grabbedIsOverlay) { + props = { + localPosition: Overlays.getProperty(this.grabbedEntity, "localPosition"), + parentID: Overlays.getProperty(this.grabbedEntity, "parentID"), + parentJointIndex: Overlays.getProperty(this.grabbedEntity, "parentJointIndex"), + position: Overlays.getProperty(this.grabbedEntity, "position"), + rotation: Overlays.getProperty(this.grabbedEntity, "rotation"), + dimensions: Overlays.getProperty(this.grabbedEntity, "dimensions"), + registrationPoint: { x: 0.5, y: 0.5, z: 0.5 } + }; + } else { + props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "parentJointIndex", "position", "rotation", "dimensions", "registrationPoint"]); + } if (!props.position) { // server may have reset, taking our equipped entity with it. move back to "off" state this.callEntityMethodOnGrabbed("releaseGrab"); @@ -2581,7 +2647,7 @@ function MyController(hand) { }; this.maybeScale = function(props) { - if (!objectScalingEnabled || this.isTablet(this.grabbedEntity)) { + if (!objectScalingEnabled || this.isTablet(this.grabbedEntity) || this.grabbedIsOverlay) { return; } @@ -3006,20 +3072,27 @@ function MyController(hand) { Entities.deleteAction(this.grabbedEntity, this.actionID); } else { // no action, so it's a parenting grab - if (this.previousParentID[this.grabbedEntity] === NULL_UUID) { - Entities.editEntity(this.grabbedEntity, { - parentID: this.previousParentID[this.grabbedEntity], - parentJointIndex: this.previousParentJointIndex[this.grabbedEntity] + if (this.grabbedIsOverlay) { + Overlays.editOverlay(this.grabbedEntity, { + parentID: NULL_UUID, + parentJointIndex: -1 }); - this.ensureDynamic(); } else { - // we're putting this back as a child of some other parent, so zero its velocity - Entities.editEntity(this.grabbedEntity, { - parentID: this.previousParentID[this.grabbedEntity], - parentJointIndex: this.previousParentJointIndex[this.grabbedEntity], - velocity: {x: 0, y: 0, z: 0}, - angularVelocity: {x: 0, y: 0, z: 0} - }); + if (this.previousParentID[this.grabbedEntity] === NULL_UUID) { + Entities.editEntity(this.grabbedEntity, { + parentID: this.previousParentID[this.grabbedEntity], + parentJointIndex: this.previousParentJointIndex[this.grabbedEntity] + }); + this.ensureDynamic(); + } else { + // we're putting this back as a child of some other parent, so zero its velocity + Entities.editEntity(this.grabbedEntity, { + parentID: this.previousParentID[this.grabbedEntity], + parentJointIndex: this.previousParentJointIndex[this.grabbedEntity], + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0} + }); + } } } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 0990440801..26d6174787 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -7,8 +7,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global getControllerWorldLocation, setEntityCustomData, Tablet, WebTablet:true, HMD, Settings, Script, - Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp */ +/* global getControllerWorldLocation, Tablet, WebTablet:true, HMD, Settings, Script, + Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp, Controller, Mat4 */ Script.include(Script.resolvePath("../libraries/utils.js")); Script.include(Script.resolvePath("../libraries/controllers.js")); @@ -110,6 +110,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { name: "WebTablet Tablet", type: "Model", modelURL: TABLET_MODEL_PATH, + url: TABLET_MODEL_PATH, // for overlay + grabbable: true, // for overlay userData: JSON.stringify({ "grabbableKey": {"grabbable": true} }), @@ -121,7 +123,14 @@ WebTablet = function (url, width, dpi, hand, clientOnly) { this.calculateTabletAttachmentProperties(hand, true, tabletProperties); this.cleanUpOldTablets(); - this.tabletEntityID = Entities.addEntity(tabletProperties, clientOnly); + + // this.tabletEntityID = Entities.addEntity(tabletProperties, clientOnly); + // this.tabletIsOverlay = false; + + tabletProperties.parentID = "{00000000-0000-0000-0000-000000000000}"; + // tabletProperties.parentJointIndex = -2; + this.tabletEntityID = Overlays.addOverlay("model", tabletProperties); + this.tabletIsOverlay = true; if (this.webOverlayID) { Overlays.deleteOverlay(this.webOverlayID); @@ -236,7 +245,11 @@ WebTablet.prototype.getOverlayObject = function () { WebTablet.prototype.destroy = function () { Overlays.deleteOverlay(this.webOverlayID); - Entities.deleteEntity(this.tabletEntityID); + if (this.tabletIsOverlay) { + Overlays.deleteOverlay(this.tabletEntityID); + } else { + Entities.deleteEntity(this.tabletEntityID); + } Overlays.deleteOverlay(this.homeButtonEntity); HMD.displayModeChanged.disconnect(this.myOnHmdChanged); @@ -432,7 +445,7 @@ WebTablet.prototype.mousePressEvent = function (event) { tablet.gotoHomeScreen(); this.setHomeButtonTexture(); } - } else if (!HMD.active && (!overlayPickResults.intersects || !overlayPickResults.overlayID === this.webOverlayID)) { + } 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);