diff --git a/interface/src/avatar/MyAvatarHeadTransformNode.cpp b/interface/src/avatar/MyAvatarHeadTransformNode.cpp new file mode 100644 index 0000000000..9c202ba94a --- /dev/null +++ b/interface/src/avatar/MyAvatarHeadTransformNode.cpp @@ -0,0 +1,23 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// + +#include "MyAvatarHeadTransformNode.h" + +#include "DependencyManager.h" +#include "AvatarManager.h" +#include "MyAvatar.h" + +Transform MyAvatarHeadTransformNode::getTransform() { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + + glm::vec3 pos = myAvatar->getHeadPosition(); + glm::quat headOri = myAvatar->getHeadOrientation(); + glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT); + + return Transform(ori, glm::vec3(1.0f), pos); +} \ No newline at end of file diff --git a/interface/src/avatar/MyAvatarHeadTransformNode.h b/interface/src/avatar/MyAvatarHeadTransformNode.h new file mode 100644 index 0000000000..a7d7144521 --- /dev/null +++ b/interface/src/avatar/MyAvatarHeadTransformNode.h @@ -0,0 +1,19 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// +#ifndef hifi_MyAvatarHeadTransformNode_h +#define hifi_MyAvatarHeadTransformNode_h + +#include "TransformNode.h" + +class MyAvatarHeadTransformNode : public TransformNode { +public: + MyAvatarHeadTransformNode() { } + Transform getTransform() override; +}; + +#endif // hifi_MyAvatarHeadTransformNode_h \ No newline at end of file diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 9f2e6da2e8..7d0276875b 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -32,9 +32,6 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu } intersects = entityIntersections.size() || avatarIntersections.size(); - if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { - loadState = (LoadState)newCollisionResult->loadState; - } return std::make_shared(*this); } @@ -80,23 +77,42 @@ QVariantMap CollisionPickResult::toVariantMap() const { } variantMap["intersectingObjects"] = qIntersectingObjects; - variantMap["loaded"] = (loadState == LOAD_STATE_LOADED); variantMap["collisionRegion"] = pickVariant; return variantMap; } -bool CollisionPick::isShapeInfoReady() { +bool CollisionPick::isLoaded() const { + return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded()); +} + +bool CollisionPick::getShapeInfoReady() { if (_mathPick.shouldComputeShapeInfo()) { if (_cachedResource && _cachedResource->isLoaded()) { computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource); - return true; + _mathPick.loaded = true; + } else { + _mathPick.loaded = false; } - - return false; + } else { + computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource); + _mathPick.loaded = true; } - return true; + return _mathPick.loaded; +} + +void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { + ShapeType type = shapeInfo.getType(); + glm::vec3 dimensions = pick.transform.getScale(); + QString modelURL = (resource ? resource->getURL().toString() : ""); + if (type == SHAPE_TYPE_COMPOUND) { + shapeInfo.setParams(type, dimensions, modelURL); + } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + shapeInfo.setParams(type, 0.5f * dimensions, modelURL); + } else { + shapeInfo.setParams(type, 0.5f * dimensions, modelURL); + } } void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { @@ -328,8 +344,25 @@ void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo } } +CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : + Pick(filter, maxDistance, enabled), + _mathPick(collisionRegion), + _physicsEngine(physicsEngine) { + if (collisionRegion.shouldComputeShapeInfo()) { + _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); + } + _mathPick.loaded = isLoaded(); +} + CollisionRegion CollisionPick::getMathematicalPick() const { - return _mathPick; + CollisionRegion mathPick = _mathPick; + mathPick.loaded = isLoaded(); + if (!parentTransform) { + return mathPick; + } else { + mathPick.transform = parentTransform->getTransform().worldTransform(mathPick.transform); + return mathPick; + } } void CollisionPick::filterIntersections(std::vector& intersections) const { @@ -356,31 +389,37 @@ void CollisionPick::filterIntersections(std::vector& intersec } PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { - if (!isShapeInfoReady()) { + if (!pick.loaded) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), std::vector(), std::vector()); } + getShapeInfoReady(); - auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); + auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(entityIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); + return std::make_shared(pick, entityIntersections, std::vector()); } PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, std::vector(), std::vector()); } PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { - if (!isShapeInfoReady()) { + if (!pick.loaded) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, std::vector(), std::vector()); } + getShapeInfoReady(); - auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); + auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(avatarIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); + return std::make_shared(pick, std::vector(), avatarIntersections); } PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), std::vector(), std::vector()); +} + +Transform CollisionPick::getResultTransform() const { + return Transform(getMathematicalPick().transform); } \ No newline at end of file diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 6631238737..ce8b3bd199 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -11,35 +11,28 @@ #include #include #include +#include #include class CollisionPickResult : public PickResult { public: - enum LoadState { - LOAD_STATE_UNKNOWN, - LOAD_STATE_NOT_LOADED, - LOAD_STATE_LOADED - }; - CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : + CollisionPickResult(const CollisionRegion& searchRegion, const std::vector& entityIntersections, const std::vector& avatarIntersections) : PickResult(searchRegion.toVariantMap()), - loadState(loadState), intersects(entityIntersections.size() || avatarIntersections.size()), entityIntersections(entityIntersections), - avatarIntersections(avatarIntersections) { + avatarIntersections(avatarIntersections) + { } CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) { avatarIntersections = collisionPickResult.avatarIntersections; entityIntersections = collisionPickResult.entityIntersections; intersects = collisionPickResult.intersects; - loadState = collisionPickResult.loadState; } - LoadState loadState { LOAD_STATE_UNKNOWN }; bool intersects { false }; std::vector entityIntersections; std::vector avatarIntersections; @@ -54,28 +47,24 @@ public: class CollisionPick : public Pick { public: - CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : - Pick(filter, maxDistance, enabled), - _mathPick(collisionRegion), - _physicsEngine(physicsEngine) { - if (collisionRegion.shouldComputeShapeInfo()) { - _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); - } - } + CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine); CollisionRegion getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { - return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector(), std::vector()); + return std::make_shared(pickVariant, std::vector(), std::vector()); } PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override; PickResultPointer getHUDIntersection(const CollisionRegion& pick) override; - + Transform getResultTransform() const override; protected: - // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. - bool isShapeInfoReady(); + // Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed. + bool isLoaded() const; + // Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use. + bool getShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); + void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); void filterIntersections(std::vector& intersections) const; CollisionRegion _mathPick; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 40a5e5cb69..5fbe3a90b5 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -35,6 +35,37 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& } } +QVariantMap LaserPointer::toVariantMap() const { + QVariantMap qVariantMap; + + QVariantMap qRenderStates; + for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { + auto renderState = iter->second; + QVariantMap qRenderState; + qRenderState["start"] = renderState->getStartID(); + qRenderState["path"] = std::static_pointer_cast(renderState)->getPathID(); + qRenderState["end"] = renderState->getEndID(); + qRenderStates[iter->first.c_str()] = qRenderState; + } + qVariantMap["renderStates"] = qRenderStates; + + QVariantMap qDefaultRenderStates; + for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { + float distance = iter->second.first; + auto defaultRenderState = iter->second.second; + QVariantMap qDefaultRenderState; + + qDefaultRenderState["distance"] = distance; + qDefaultRenderState["start"] = defaultRenderState->getStartID(); + qDefaultRenderState["path"] = std::static_pointer_cast(defaultRenderState)->getPathID(); + qDefaultRenderState["end"] = defaultRenderState->getEndID(); + qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState; + } + qVariantMap["defaultRenderStates"] = qDefaultRenderStates; + + return qVariantMap; +} + glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const { auto rayPickResult = std::static_pointer_cast(pickResult); return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 95a5bccc6c..c0ac3259d9 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -42,6 +42,8 @@ public: LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled); + QVariantMap toVariantMap() const override; + static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: diff --git a/interface/src/raypick/MouseTransformNode.cpp b/interface/src/raypick/MouseTransformNode.cpp new file mode 100644 index 0000000000..9caa4865a2 --- /dev/null +++ b/interface/src/raypick/MouseTransformNode.cpp @@ -0,0 +1,27 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// + +#include "MouseTransformNode.h" + +#include "Application.h" +#include "display-plugins/CompositorHelper.h" +#include "RayPick.h" + +Transform MouseTransformNode::getTransform() { + QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition(); + if (position.isValid()) { + Transform transform; + QVariantMap posMap = position.toMap(); + PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat()); + transform.setTranslation(pickRay.origin); + transform.setRotation(rotationBetween(Vectors::UP, pickRay.direction)); + return transform; + } + + return Transform(); +} \ No newline at end of file diff --git a/interface/src/raypick/MouseTransformNode.h b/interface/src/raypick/MouseTransformNode.h new file mode 100644 index 0000000000..4f340339e4 --- /dev/null +++ b/interface/src/raypick/MouseTransformNode.h @@ -0,0 +1,18 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// +#ifndef hifi_MouseTransformNode_h +#define hifi_MouseTransformNode_h + +#include "TransformNode.h" + +class MouseTransformNode : public TransformNode { +public: + Transform getTransform() override; +}; + +#endif // hifi_MouseTransformNode_h \ No newline at end of file diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index f4db1df25a..4b5eeec828 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -70,4 +70,16 @@ glm::vec3 ParabolaPick::getAcceleration() const { return scale * (DependencyManager::get()->getMyAvatar()->getWorldOrientation() * _accelerationAxis); } return scale * _accelerationAxis; +} + +Transform ParabolaPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto parabolaResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(parabolaResult->intersection); + return transform; } \ No newline at end of file diff --git a/interface/src/raypick/ParabolaPick.h b/interface/src/raypick/ParabolaPick.h index 99a42a5380..01454390f9 100644 --- a/interface/src/raypick/ParabolaPick.h +++ b/interface/src/raypick/ParabolaPick.h @@ -83,6 +83,7 @@ public: PickResultPointer getOverlayIntersection(const PickParabola& pick) override; PickResultPointer getAvatarIntersection(const PickParabola& pick) override; PickResultPointer getHUDIntersection(const PickParabola& pick) override; + Transform getResultTransform() const override; protected: float _speed; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 57d57e11c4..888b3ddbe8 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -60,6 +60,35 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria } } +QVariantMap ParabolaPointer::toVariantMap() const { + QVariantMap qVariantMap; + + QVariantMap qRenderStates; + for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { + auto renderState = iter->second; + QVariantMap qRenderState; + qRenderState["start"] = renderState->getStartID(); + qRenderState["end"] = renderState->getEndID(); + qRenderStates[iter->first.c_str()] = qRenderState; + } + qVariantMap["renderStates"] = qRenderStates; + + QVariantMap qDefaultRenderStates; + for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { + float distance = iter->second.first; + auto defaultRenderState = iter->second.second; + QVariantMap qDefaultRenderState; + + qDefaultRenderState["distance"] = distance; + qDefaultRenderState["start"] = defaultRenderState->getStartID(); + qDefaultRenderState["end"] = defaultRenderState->getEndID(); + qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState; + } + qVariantMap["defaultRenderStates"] = qDefaultRenderStates; + + return qVariantMap; +} + glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) const { auto parabolaPickResult = std::static_pointer_cast(pickResult); return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index 93f9a7b055..526abe3b0d 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -97,6 +97,8 @@ public: ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled); + QVariantMap toVariantMap() const override; + static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 0ed35e5589..6dedf3fca1 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -23,6 +23,13 @@ #include "MouseParabolaPick.h" #include "CollisionPick.h" +#include "SpatialParentFinder.h" +#include "NestableTransformNode.h" +#include "PickTransformNode.h" +#include "MouseTransformNode.h" +#include "avatar/MyAvatarHeadTransformNode.h" +#include "avatar/AvatarManager.h" + #include unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { @@ -276,8 +283,10 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert } CollisionRegion collisionRegion(propMap); + auto collisionPick = std::make_shared(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine()); + collisionPick->parentTransform = createTransformNode(propMap); - return DependencyManager::get()->addPick(PickQuery::Collision, std::make_shared(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine())); + return DependencyManager::get()->addPick(PickQuery::Collision, collisionPick); } void PickScriptingInterface::enablePick(unsigned int uid) { @@ -351,3 +360,43 @@ unsigned int PickScriptingInterface::getPerFrameTimeBudget() const { void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) { DependencyManager::get()->setPerFrameTimeBudget(numUsecs); } + +std::shared_ptr PickScriptingInterface::createTransformNode(const QVariantMap& propMap) { + if (propMap["parentID"].isValid()) { + QUuid parentUuid = propMap["parentID"].toUuid(); + if (!parentUuid.isNull()) { + // Infer object type from parentID + // For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids. + bool success; + std::weak_ptr nestablePointer = DependencyManager::get()->find(parentUuid, success, nullptr); + int parentJointIndex = 0; + if (propMap["parentJointIndex"].isValid()) { + parentJointIndex = propMap["parentJointIndex"].toInt(); + } + auto sharedNestablePointer = nestablePointer.lock(); + if (success && sharedNestablePointer) { + return std::make_shared(nestablePointer, parentJointIndex); + } + } + + unsigned int pickID = propMap["parentID"].toUInt(); + if (pickID != 0) { + return std::make_shared(pickID); + } + } + + if (propMap["joint"].isValid()) { + QString joint = propMap["joint"].toString(); + if (joint == "Mouse") { + return std::make_shared(); + } else if (joint == "Avatar") { + return std::make_shared(); + } else if (!joint.isNull()) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + int jointIndex = myAvatar->getJointIndex(joint); + return std::make_shared(myAvatar, jointIndex); + } + } + + return std::shared_ptr(); +} \ No newline at end of file diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 1cad4abf7c..4d99309618 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -152,9 +152,6 @@ public: * @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection. */ - // TODO: Add this to the CollisionPickResult jsdoc once model collision picks are working - //* @property {boolean} loaded If the CollisionRegion was successfully loaded (may be false if a model was used) - /**jsdoc * Information about the Collision Pick's intersection with an object * @@ -320,6 +317,9 @@ public slots: * @returns {number} */ static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; } + +protected: + static std::shared_ptr createTransformNode(const QVariantMap& propMap); }; #endif // hifi_PickScriptingInterface_h diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a9b9b6e9ea..7209e402a1 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -76,16 +76,19 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined. */ /**jsdoc - * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. + * A set of properties which define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. * * @typedef {object} Pointers.RayPointerRenderState - * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} - * @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * An overlay to represent the beginning of the Ray Pointer, if desired. - * @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". - * An overlay to represent the path of the Ray Pointer, if desired. - * @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * An overlay to represent the end of the Ray Pointer, if desired. + * @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} + * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. @@ -99,8 +102,12 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. 0-1. If 0 or 1, * the normal will follow exactly. * @property {boolean} [enabled=false] - * @property {Pointers.RayPointerRenderState[]} [renderStates] A list of different visual states to switch between. - * @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. + * @property {Pointers.RayPointerRenderState[]|Object.} [renderStates] A collection of different visual states to switch between. + * When using {@link Pointers.createPointer}, a list of RayPointerRenderStates. + * When returned from {@link Pointers.getPointerProperties}, a map between render state names and RayPointRenderStates. + * @property {Pointers.DefaultRayPointerRenderState[]|Object.} [defaultRenderStates] A collection of different visual states to use if there is no intersection. + * When using {@link Pointers.createPointer}, a list of DefaultRayPointerRenderStates. + * When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultRayPointRenderStates. * @property {boolean} [hover=false] If this Pointer should generate hover events. * @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation. */ @@ -224,12 +231,15 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is intersecting something. * * @typedef {object} Pointers.ParabolaPointerRenderState -* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} -* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* An overlay to represent the beginning of the Parabola Pointer, if desired. -* @property {Pointers.ParabolaProperties} [path] The rendering properties of the parabolic path defined by the Parabola Pointer. -* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* An overlay to represent the end of the Parabola Pointer, if desired. +* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} +* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer, +* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). +* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. +* @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer. +* Not defined in {@link Pointers.getPointerProperties}. +* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Parabola Pointer, +* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). +* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. @@ -243,8 +253,12 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. 0-1. If 0 or 1, * the normal will follow exactly. * @property {boolean} [enabled=false] -* @property {Pointers.ParabolaPointerRenderState[]} [renderStates] A list of different visual states to switch between. -* @property {Pointers.DefaultParabolaPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. +* @property {Pointers.ParabolaPointerRenderState[]|Object.} [renderStates] A collection of different visual states to switch between. +* When using {@link Pointers.createPointer}, a list of ParabolaPointerRenderStates. +* When returned from {@link Pointers.getPointerProperties}, a map between render state names and ParabolaPointerRenderStates. +* @property {Pointers.DefaultParabolaPointerRenderState[]|Object.} [defaultRenderStates] A collection of different visual states to use if there is no intersection. +* When using {@link Pointers.createPointer}, a list of DefaultParabolaPointerRenderStates. +* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultParabolaPointerRenderStates. * @property {boolean} [hover=false] If this Pointer should generate hover events. * @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation. */ @@ -375,4 +389,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const result = pickResult->toVariantMap(); } return result; +} + +QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const { + return DependencyManager::get()->getPointerProperties(uid); } \ No newline at end of file diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 9fe05182c7..94f1d62552 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -203,6 +203,14 @@ public: */ Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } + /**jsdoc + * Returns information about an existing Pointer + * @function Pointers.getPointerState + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer. + * Currently only includes renderStates and defaultRenderStates with associated overlay IDs. + */ + Q_INVOKABLE QVariantMap getPointerProperties(unsigned int uid) const; }; #endif // hifi_PointerScriptingInterface_h diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index fde1da3f87..cabfb3cbd5 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -53,6 +53,18 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) { return std::make_shared(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick); } +Transform RayPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto rayResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(rayResult->intersection); + return transform; +} + glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration) { // TODO: take into account registration glm::vec3 n = rotation * Vectors::FRONT; diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 9410d39c1a..11f985cec2 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -77,6 +77,7 @@ public: PickResultPointer getOverlayIntersection(const PickRay& pick) override; PickResultPointer getAvatarIntersection(const PickRay& pick) override; PickResultPointer getHUDIntersection(const PickRay& pick) override; + Transform getResultTransform() const override; // These are helper functions for projecting and intersecting rays static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index 9021c922a6..69f605e7f9 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -225,4 +225,16 @@ PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) { PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) { return std::make_shared(pick.toVariantMap()); +} + +Transform StylusPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto stylusResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(stylusResult->intersection); + return transform; } \ No newline at end of file diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index f19e343f8d..ca80e9fbea 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -66,6 +66,7 @@ public: PickResultPointer getOverlayIntersection(const StylusTip& pick) override; PickResultPointer getAvatarIntersection(const StylusTip& pick) override; PickResultPointer getHUDIntersection(const StylusTip& pick) override; + Transform getResultTransform() const override; bool isLeftHand() const override { return _side == Side::Left; } bool isRightHand() const override { return _side == Side::Right; } diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index c74b6d8509..06e3e52d21 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -207,6 +207,10 @@ void StylusPointer::setRenderState(const std::string& state) { } } +QVariantMap StylusPointer::toVariantMap() const { + return QVariantMap(); +} + glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) { switch (pickedObject.type) { case ENTITY: diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index b1903840b3..4095acb529 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -33,6 +33,8 @@ public: void setRenderState(const std::string& state) override; void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {} + QVariantMap toVariantMap() const override; + static OverlayID buildStylusOverlay(const QVariantMap& properties); protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index d5ce60bef9..58c197d6f4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -898,11 +898,12 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) { } struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { - AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : + AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject, float threshold) : btCollisionWorld::ContactResultCallback(), collisionObject(), contacts(), - myAvatarCollisionObject(myAvatarCollisionObject) { + myAvatarCollisionObject(myAvatarCollisionObject), + threshold(threshold) { const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); collisionObject.setCollisionShape(const_cast(collisionShape)); @@ -924,8 +925,13 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { btCollisionObject collisionObject; std::vector contacts; btCollisionObject* myAvatarCollisionObject; + btScalar threshold; btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { + if (cp.m_distance1 > -threshold) { + return 0; + } + const btCollisionObject* otherBody; btVector3 penetrationPoint; btVector3 otherPenetrationPoint; @@ -968,14 +974,14 @@ protected: } }; -std::vector PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { +std::vector PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group, float threshold) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) { myAvatarCollisionObject = _myAvatarController->getCollisionObject(); } - auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject); + auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject, threshold); _dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback); return contactCallback.contacts; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 317f7ef312..09b68d9a8b 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -142,7 +142,7 @@ public: // Function for getting colliding objects in the world of specified type // See PhysicsCollisionGroups.h for mask flags. - std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; + std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const; private: QList removeDynamicsForBody(btRigidBody* body); diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index fc09064bd1..099a791407 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -17,6 +17,7 @@ #include #include +#include enum IntersectionType { NONE = 0, @@ -213,6 +214,10 @@ public: virtual bool isRightHand() const { return false; } virtual bool isMouse() const { return false; } + virtual Transform getResultTransform() const = 0; + + std::shared_ptr parentTransform; + private: PickFilter _filter; const float _maxDistance; diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 7a52cfc410..c74e84937b 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -57,8 +57,8 @@ bool PickCacheOptimizer::checkAndCompareCachedResults(T& pick, PickCache& cac } template -void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr> pick) { - if (intersects) { +void PickCacheOptimizer::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr> pick) { + if (needToCompareResults) { cache[mathPick][key] = resTemp; res = res->compareAndProcessNewResult(resTemp); } else { diff --git a/libraries/pointers/src/PickManager.cpp b/libraries/pointers/src/PickManager.cpp index a384eb661f..f7553cb66e 100644 --- a/libraries/pointers/src/PickManager.cpp +++ b/libraries/pointers/src/PickManager.cpp @@ -90,6 +90,14 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector& includ } } +Transform PickManager::getResultTransform(unsigned int uid) const { + auto pick = findPick(uid); + if (pick) { + return pick->getResultTransform(); + } + return Transform(); +} + void PickManager::update() { uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget; std::unordered_map>> cachedPicks; diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index e8d32771e0..cf911236a1 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -43,6 +43,8 @@ public: void setIgnoreItems(unsigned int uid, const QVector& ignore) const; void setIncludeItems(unsigned int uid, const QVector& include) const; + Transform getResultTransform(unsigned int uid) const; + bool isLeftHand(unsigned int uid); bool isRightHand(unsigned int uid); bool isMouse(unsigned int uid); diff --git a/libraries/pointers/src/PickTransformNode.cpp b/libraries/pointers/src/PickTransformNode.cpp new file mode 100644 index 0000000000..fe011b7fcd --- /dev/null +++ b/libraries/pointers/src/PickTransformNode.cpp @@ -0,0 +1,26 @@ +// +// Created by Sabrina Shanman 8/22/2018 +// Copyright 2018 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 +// + +#include "PickTransformNode.h" + +#include "DependencyManager.h" +#include "PickManager.h" + +PickTransformNode::PickTransformNode(unsigned int uid) : + _uid(uid) +{ +} + +Transform PickTransformNode::getTransform() { + auto pickManager = DependencyManager::get(); + if (!pickManager) { + return Transform(); + } + + return pickManager->getResultTransform(_uid); +} \ No newline at end of file diff --git a/libraries/pointers/src/PickTransformNode.h b/libraries/pointers/src/PickTransformNode.h new file mode 100644 index 0000000000..b4c92f3ba7 --- /dev/null +++ b/libraries/pointers/src/PickTransformNode.h @@ -0,0 +1,23 @@ +// +// Created by Sabrina Shanman 8/22/2018 +// Copyright 2018 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 +// +#ifndef hifi_PickTransformNode_h +#define hifi_PickTransformNode_h + +#include "TransformNode.h" + +// TODO: Remove this class when Picks are converted to SpatiallyNestables +class PickTransformNode : public TransformNode { +public: + PickTransformNode(unsigned int uid); + Transform getTransform() override; + +protected: + unsigned int _uid; +}; + +#endif // hifi_PickTransformNode_h \ No newline at end of file diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 0c842dbd88..4264a60079 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -50,6 +50,8 @@ public: virtual void setRenderState(const std::string& state) = 0; virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0; + virtual QVariantMap toVariantMap() const = 0; + virtual void setPrecisionPicking(bool precisionPicking); virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; diff --git a/libraries/pointers/src/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp index be890da392..922f0bb5bc 100644 --- a/libraries/pointers/src/PointerManager.cpp +++ b/libraries/pointers/src/PointerManager.cpp @@ -77,6 +77,15 @@ PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const { return result; } +QVariantMap PointerManager::getPointerProperties(unsigned int uid) const { + auto pointer = find(uid); + if (pointer) { + return pointer->toVariantMap(); + } else { + return QVariantMap(); + } +} + void PointerManager::update() { auto cachedPointers = resultWithReadLock>>([&] { return _pointers; diff --git a/libraries/pointers/src/PointerManager.h b/libraries/pointers/src/PointerManager.h index b98558622f..2d0b2a107e 100644 --- a/libraries/pointers/src/PointerManager.h +++ b/libraries/pointers/src/PointerManager.h @@ -30,6 +30,7 @@ public: void setRenderState(unsigned int uid, const std::string& renderState) const; void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; PickResultPointer getPrevPickResult(unsigned int uid) const; + QVariantMap getPointerProperties(unsigned int uid) const; void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; void setIgnoreItems(unsigned int uid, const QVector& ignoreEntities) const; diff --git a/libraries/shared/src/NestableTransformNode.cpp b/libraries/shared/src/NestableTransformNode.cpp new file mode 100644 index 0000000000..17456d69ce --- /dev/null +++ b/libraries/shared/src/NestableTransformNode.cpp @@ -0,0 +1,31 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// + +#include "NestableTransformNode.h" + +NestableTransformNode::NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex) : + _spatiallyNestable(spatiallyNestable), + _jointIndex(jointIndex) +{ +} + +Transform NestableTransformNode::getTransform() { + auto nestable = _spatiallyNestable.lock(); + if (!nestable) { + return Transform(); + } + + bool success; + Transform jointWorldTransform = nestable->getTransform(_jointIndex, success); + + if (success) { + return jointWorldTransform; + } else { + return Transform(); + } +} \ No newline at end of file diff --git a/libraries/shared/src/NestableTransformNode.h b/libraries/shared/src/NestableTransformNode.h new file mode 100644 index 0000000000..131de9e786 --- /dev/null +++ b/libraries/shared/src/NestableTransformNode.h @@ -0,0 +1,25 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// +#ifndef hifi_NestableTransformNode_h +#define hifi_NestableTransformNode_h + +#include "TransformNode.h" + +#include "SpatiallyNestable.h" + +class NestableTransformNode : public TransformNode { +public: + NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex); + Transform getTransform() override; + +protected: + SpatiallyNestableWeakPointer _spatiallyNestable; + int _jointIndex; +}; + +#endif // hifi_NestableTransformNode_h \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index e78dbafd75..02f5fa570c 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -253,18 +253,36 @@ public: } }; -/**jsdoc -* A CollisionPick defines a volume for checking collisions in the physics simulation. +// TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported. -* @typedef {object} CollisionPick +/**jsdoc +* A CollisionRegion defines a volume for checking collisions in the physics simulation. + +* @typedef {object} CollisionRegion * @property {Shape} shape - The information about the collision region's size and shape. -* @property {Vec3} position - The position of the collision region. -* @property {Quat} orientation - The orientation of the collision region. +* @property {Vec3} position - The position of the collision region, relative to a parent if defined. +* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. +* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. +* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. +* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) +* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. */ class CollisionRegion : public MathPick { public: CollisionRegion() { } + + CollisionRegion(const CollisionRegion& collisionRegion) : + loaded(collisionRegion.loaded), + modelURL(collisionRegion.modelURL), + shapeInfo(std::make_shared()), + transform(collisionRegion.transform), + threshold(collisionRegion.threshold) + { + shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString()); + } + CollisionRegion(const QVariantMap& pickVariant) { + // "loaded" is not deserialized here because there is no way to know if the shape is actually loaded if (pickVariant["shape"].isValid()) { auto shape = pickVariant["shape"].toMap(); if (!shape.empty()) { @@ -287,6 +305,10 @@ public: } } + if (pickVariant["threshold"].isValid()) { + threshold = glm::max(0.0f, pickVariant["threshold"].toFloat()); + } + if (pickVariant["position"].isValid()) { transform.setTranslation(vec3FromVariant(pickVariant["position"])); } @@ -304,6 +326,9 @@ public: shape["dimensions"] = vec3toVariant(transform.getScale()); collisionRegion["shape"] = shape; + collisionRegion["loaded"] = loaded; + + collisionRegion["threshold"] = threshold; collisionRegion["position"] = vec3toVariant(transform.getTranslation()); collisionRegion["orientation"] = quatToVariant(transform.getRotation()); @@ -312,13 +337,16 @@ public: } operator bool() const override { - return !(glm::any(glm::isnan(transform.getTranslation())) || + return !std::isnan(threshold) && + !(glm::any(glm::isnan(transform.getTranslation())) || glm::any(glm::isnan(transform.getRotation())) || shapeInfo->getType() == SHAPE_TYPE_NONE); } bool operator==(const CollisionRegion& other) const { - return glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) && + return loaded == other.loaded && + threshold == other.threshold && + glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) && glm::all(glm::equal(transform.getRotation(), other.transform.getRotation())) && glm::all(glm::equal(transform.getScale(), other.transform.getScale())) && shapeInfo->getType() == other.shapeInfo->getType() && @@ -337,11 +365,13 @@ public: } // We can't load the model here because it would create a circular dependency, so we delegate that responsibility to the owning CollisionPick + bool loaded { false }; QUrl modelURL; // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick std::shared_ptr shapeInfo = std::make_shared(); Transform transform; + float threshold; }; namespace std { diff --git a/libraries/shared/src/TransformNode.h b/libraries/shared/src/TransformNode.h new file mode 100644 index 0000000000..73223a8cee --- /dev/null +++ b/libraries/shared/src/TransformNode.h @@ -0,0 +1,18 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 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 +// +#ifndef hifi_TransformNode_h +#define hifi_TransformNode_h + +#include "Transform.h" + +class TransformNode { +public: + virtual Transform getTransform() = 0; +}; + +#endif // hifi_TransformNode_h \ No newline at end of file