make overlays near-grabbable. make tablet be an overlay

This commit is contained in:
Seth Alves 2017-02-22 16:50:02 -08:00
parent aed1d69377
commit bfc4853947
6 changed files with 165 additions and 35 deletions

View file

@ -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();
}

View file

@ -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

View file

@ -803,3 +803,25 @@ void Overlays::mouseMoveEvent(QMouseEvent* event) {
}
}
}
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) const {
QVector<QUuid> result;
glm::vec3 penetration;
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
i.toBack();
while (i.hasPrevious()) {
i.previous();
OverlayID thisID = i.key();
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(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;
}

View file

@ -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<QUuid> 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

View file

@ -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}
});
}
}
}

View file

@ -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);