Merge pull request #13873 from sabrina-shanman/stt_parenting

Collision Pick Parenting
This commit is contained in:
Sam Gondelman 2018-08-31 14:43:55 -07:00 committed by GitHub
commit 7cdc7dcc66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 564 additions and 79 deletions

View file

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

View file

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

View file

@ -32,9 +32,6 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu
} }
intersects = entityIntersections.size() || avatarIntersections.size(); intersects = entityIntersections.size() || avatarIntersections.size();
if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) {
loadState = (LoadState)newCollisionResult->loadState;
}
return std::make_shared<CollisionPickResult>(*this); return std::make_shared<CollisionPickResult>(*this);
} }
@ -80,23 +77,42 @@ QVariantMap CollisionPickResult::toVariantMap() const {
} }
variantMap["intersectingObjects"] = qIntersectingObjects; variantMap["intersectingObjects"] = qIntersectingObjects;
variantMap["loaded"] = (loadState == LOAD_STATE_LOADED);
variantMap["collisionRegion"] = pickVariant; variantMap["collisionRegion"] = pickVariant;
return variantMap; return variantMap;
} }
bool CollisionPick::isShapeInfoReady() { bool CollisionPick::isLoaded() const {
return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded());
}
bool CollisionPick::getShapeInfoReady() {
if (_mathPick.shouldComputeShapeInfo()) { if (_mathPick.shouldComputeShapeInfo()) {
if (_cachedResource && _cachedResource->isLoaded()) { if (_cachedResource && _cachedResource->isLoaded()) {
computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource); computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource);
return true; _mathPick.loaded = true;
} else {
_mathPick.loaded = false;
}
} else {
computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource);
_mathPick.loaded = true;
} }
return false; return _mathPick.loaded;
} }
return true; void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> 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<GeometryResource> resource) { void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> 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<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
}
_mathPick.loaded = isLoaded();
}
CollisionRegion CollisionPick::getMathematicalPick() const { 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<ContactTestResult>& intersections) const { void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
@ -356,31 +389,37 @@ void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersec
} }
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) { if (!pick.loaded) {
// Cannot compute result // Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
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); filterIntersections(entityIntersections);
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
} }
PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) {
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
if (!isShapeInfoReady()) { if (!pick.loaded) {
// Cannot compute result // Cannot compute result
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
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); filterIntersections(avatarIntersections);
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector<ContactTestResult>(), avatarIntersections); return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections);
} }
PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) {
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
}
Transform CollisionPick::getResultTransform() const {
return Transform(getMathematicalPick().transform);
} }

View file

@ -11,35 +11,28 @@
#include <PhysicsEngine.h> #include <PhysicsEngine.h>
#include <model-networking/ModelCache.h> #include <model-networking/ModelCache.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <TransformNode.h>
#include <Pick.h> #include <Pick.h>
class CollisionPickResult : public PickResult { class CollisionPickResult : public PickResult {
public: public:
enum LoadState {
LOAD_STATE_UNKNOWN,
LOAD_STATE_NOT_LOADED,
LOAD_STATE_LOADED
};
CollisionPickResult() {} CollisionPickResult() {}
CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {}
CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) : CollisionPickResult(const CollisionRegion& searchRegion, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) :
PickResult(searchRegion.toVariantMap()), PickResult(searchRegion.toVariantMap()),
loadState(loadState),
intersects(entityIntersections.size() || avatarIntersections.size()), intersects(entityIntersections.size() || avatarIntersections.size()),
entityIntersections(entityIntersections), entityIntersections(entityIntersections),
avatarIntersections(avatarIntersections) { avatarIntersections(avatarIntersections)
{
} }
CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) { CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) {
avatarIntersections = collisionPickResult.avatarIntersections; avatarIntersections = collisionPickResult.avatarIntersections;
entityIntersections = collisionPickResult.entityIntersections; entityIntersections = collisionPickResult.entityIntersections;
intersects = collisionPickResult.intersects; intersects = collisionPickResult.intersects;
loadState = collisionPickResult.loadState;
} }
LoadState loadState { LOAD_STATE_UNKNOWN };
bool intersects { false }; bool intersects { false };
std::vector<ContactTestResult> entityIntersections; std::vector<ContactTestResult> entityIntersections;
std::vector<ContactTestResult> avatarIntersections; std::vector<ContactTestResult> avatarIntersections;
@ -54,28 +47,24 @@ public:
class CollisionPick : public Pick<CollisionRegion> { class CollisionPick : public Pick<CollisionRegion> {
public: public:
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : 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<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
}
}
CollisionRegion getMathematicalPick() const override; CollisionRegion getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override {
return std::make_shared<CollisionPickResult>(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector<ContactTestResult>(), std::vector<ContactTestResult>()); return std::make_shared<CollisionPickResult>(pickVariant, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
} }
PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; PickResultPointer getEntityIntersection(const CollisionRegion& pick) override;
PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override;
PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override; PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override;
PickResultPointer getHUDIntersection(const CollisionRegion& pick) override; PickResultPointer getHUDIntersection(const CollisionRegion& pick) override;
Transform getResultTransform() const override;
protected: protected:
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. // Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed.
bool isShapeInfoReady(); 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<GeometryResource> resource); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void filterIntersections(std::vector<ContactTestResult>& intersections) const; void filterIntersections(std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick; CollisionRegion _mathPick;

View file

@ -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>(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<RenderState>(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 { glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const {
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult); auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f));

View file

@ -42,6 +42,8 @@ public:
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, 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); bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
QVariantMap toVariantMap() const override;
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap); static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
protected: protected:

View file

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

View file

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

View file

@ -71,3 +71,15 @@ glm::vec3 ParabolaPick::getAcceleration() const {
} }
return scale * _accelerationAxis; return scale * _accelerationAxis;
} }
Transform ParabolaPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {
return Transform();
}
auto parabolaResult = std::static_pointer_cast<ParabolaPickResult>(result);
Transform transform;
transform.setTranslation(parabolaResult->intersection);
return transform;
}

View file

@ -83,6 +83,7 @@ public:
PickResultPointer getOverlayIntersection(const PickParabola& pick) override; PickResultPointer getOverlayIntersection(const PickParabola& pick) override;
PickResultPointer getAvatarIntersection(const PickParabola& pick) override; PickResultPointer getAvatarIntersection(const PickParabola& pick) override;
PickResultPointer getHUDIntersection(const PickParabola& pick) override; PickResultPointer getHUDIntersection(const PickParabola& pick) override;
Transform getResultTransform() const override;
protected: protected:
float _speed; float _speed;

View file

@ -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 { glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) const {
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult); auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f));

View file

@ -97,6 +97,8 @@ public:
ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, 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); bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
QVariantMap toVariantMap() const override;
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap); static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
protected: protected:

View file

@ -23,6 +23,13 @@
#include "MouseParabolaPick.h" #include "MouseParabolaPick.h"
#include "CollisionPick.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 <ScriptEngine.h> #include <ScriptEngine.h>
unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { 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); CollisionRegion collisionRegion(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine());
collisionPick->parentTransform = createTransformNode(propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine())); return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, collisionPick);
} }
void PickScriptingInterface::enablePick(unsigned int uid) { void PickScriptingInterface::enablePick(unsigned int uid) {
@ -351,3 +360,43 @@ unsigned int PickScriptingInterface::getPerFrameTimeBudget() const {
void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) { void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs); DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
} }
std::shared_ptr<TransformNode> 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<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->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<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
return std::make_shared<PickTransformNode>(pickID);
}
}
if (propMap["joint"].isValid()) {
QString joint = propMap["joint"].toString();
if (joint == "Mouse") {
return std::make_shared<MouseTransformNode>();
} else if (joint == "Avatar") {
return std::make_shared<MyAvatarHeadTransformNode>();
} else if (!joint.isNull()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(joint);
return std::make_shared<NestableTransformNode>(myAvatar, jointIndex);
}
}
return std::shared_ptr<TransformNode>();
}

View file

@ -152,9 +152,6 @@ public:
* @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection. * @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 /**jsdoc
* Information about the Collision Pick's intersection with an object * Information about the Collision Pick's intersection with an object
* *
@ -320,6 +317,9 @@ public slots:
* @returns {number} * @returns {number}
*/ */
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; } static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
protected:
static std::shared_ptr<TransformNode> createTransformNode(const QVariantMap& propMap);
}; };
#endif // hifi_PickScriptingInterface_h #endif // hifi_PickScriptingInterface_h

View file

@ -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. * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
*/ */
/**jsdoc /**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 * @typedef {object} Pointers.RayPointerRenderState
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} * @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} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field). * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer,
* An overlay to represent the beginning of the Ray Pointer, if desired. * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>. * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
* An overlay to represent the path of the Ray Pointer, if desired. * @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer,
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field). * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
* An overlay to represent the end of the Ray Pointer, if desired. * 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 <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
*/ */
/**jsdoc /**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. * 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. <code>0-1</code>. If 0 or 1, * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly. * the normal will follow exactly.
* @property {boolean} [enabled=false] * @property {boolean} [enabled=false]
* @property {Pointers.RayPointerRenderState[]} [renderStates] A list of different visual states to switch between. * @property {Pointers.RayPointerRenderState[]|Object.<string, Pointers.RayPointerRenderState>} [renderStates] A collection of different visual states to switch between.
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. * 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.<string, Pointers.DefaultRayPointerRenderState>} [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 {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. * @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. * 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 * @typedef {object} Pointers.ParabolaPointerRenderState
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} * @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} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field). * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer,
* An overlay to represent the beginning of the Parabola Pointer, if desired. * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
* @property {Pointers.ParabolaProperties} [path] The rendering properties of the parabolic path defined by the Parabola Pointer. * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field). * @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer.
* An overlay to represent the end of the Parabola Pointer, if desired. * 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 <code>type</code> field).
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
*/ */
/**jsdoc /**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. * 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. <code>0-1</code>. If 0 or 1, * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly. * the normal will follow exactly.
* @property {boolean} [enabled=false] * @property {boolean} [enabled=false]
* @property {Pointers.ParabolaPointerRenderState[]} [renderStates] A list of different visual states to switch between. * @property {Pointers.ParabolaPointerRenderState[]|Object.<string, Pointers.ParabolaPointerRenderState>} [renderStates] A collection of different visual states to switch between.
* @property {Pointers.DefaultParabolaPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. * 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.<string, Pointers.DefaultParabolaPointerRenderState>} [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 {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. * @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation.
*/ */
@ -376,3 +390,7 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
} }
return result; return result;
} }
QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const {
return DependencyManager::get<PointerManager>()->getPointerProperties(uid);
}

View file

@ -203,6 +203,14 @@ public:
*/ */
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); } Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->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 #endif // hifi_PointerScriptingInterface_h

View file

@ -53,6 +53,18 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) {
return std::make_shared<RayPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick); return std::make_shared<RayPickResult>(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<RayPickResult>(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) { 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 // TODO: take into account registration
glm::vec3 n = rotation * Vectors::FRONT; glm::vec3 n = rotation * Vectors::FRONT;

View file

@ -77,6 +77,7 @@ public:
PickResultPointer getOverlayIntersection(const PickRay& pick) override; PickResultPointer getOverlayIntersection(const PickRay& pick) override;
PickResultPointer getAvatarIntersection(const PickRay& pick) override; PickResultPointer getAvatarIntersection(const PickRay& pick) override;
PickResultPointer getHUDIntersection(const PickRay& pick) override; PickResultPointer getHUDIntersection(const PickRay& pick) override;
Transform getResultTransform() const override;
// These are helper functions for projecting and intersecting rays // These are helper functions for projecting and intersecting rays
static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction);

View file

@ -226,3 +226,15 @@ PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) {
PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) { PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) {
return std::make_shared<StylusPickResult>(pick.toVariantMap()); return std::make_shared<StylusPickResult>(pick.toVariantMap());
} }
Transform StylusPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {
return Transform();
}
auto stylusResult = std::static_pointer_cast<StylusPickResult>(result);
Transform transform;
transform.setTranslation(stylusResult->intersection);
return transform;
}

View file

@ -66,6 +66,7 @@ public:
PickResultPointer getOverlayIntersection(const StylusTip& pick) override; PickResultPointer getOverlayIntersection(const StylusTip& pick) override;
PickResultPointer getAvatarIntersection(const StylusTip& pick) override; PickResultPointer getAvatarIntersection(const StylusTip& pick) override;
PickResultPointer getHUDIntersection(const StylusTip& pick) override; PickResultPointer getHUDIntersection(const StylusTip& pick) override;
Transform getResultTransform() const override;
bool isLeftHand() const override { return _side == Side::Left; } bool isLeftHand() const override { return _side == Side::Left; }
bool isRightHand() const override { return _side == Side::Right; } bool isRightHand() const override { return _side == Side::Right; }

View file

@ -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) { glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {
switch (pickedObject.type) { switch (pickedObject.type) {
case ENTITY: case ENTITY:

View file

@ -33,6 +33,8 @@ public:
void setRenderState(const std::string& state) override; void setRenderState(const std::string& state) override;
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) 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); static OverlayID buildStylusOverlay(const QVariantMap& properties);
protected: protected:

View file

@ -898,11 +898,12 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) {
} }
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { 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(), btCollisionWorld::ContactResultCallback(),
collisionObject(), collisionObject(),
contacts(), contacts(),
myAvatarCollisionObject(myAvatarCollisionObject) { myAvatarCollisionObject(myAvatarCollisionObject),
threshold(threshold) {
const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
collisionObject.setCollisionShape(const_cast<btCollisionShape*>(collisionShape)); collisionObject.setCollisionShape(const_cast<btCollisionShape*>(collisionShape));
@ -924,8 +925,13 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
btCollisionObject collisionObject; btCollisionObject collisionObject;
std::vector<ContactTestResult> contacts; std::vector<ContactTestResult> contacts;
btCollisionObject* myAvatarCollisionObject; btCollisionObject* myAvatarCollisionObject;
btScalar threshold;
btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { 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; const btCollisionObject* otherBody;
btVector3 penetrationPoint; btVector3 penetrationPoint;
btVector3 otherPenetrationPoint; btVector3 otherPenetrationPoint;
@ -968,14 +974,14 @@ protected:
} }
}; };
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { std::vector<ContactTestResult> 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 // TODO: Give MyAvatar a motion state so we don't have to do this
btCollisionObject* myAvatarCollisionObject = nullptr; btCollisionObject* myAvatarCollisionObject = nullptr;
if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) { if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) {
myAvatarCollisionObject = _myAvatarController->getCollisionObject(); 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); _dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback);
return contactCallback.contacts; return contactCallback.contacts;

View file

@ -142,7 +142,7 @@ public:
// Function for getting colliding objects in the world of specified type // Function for getting colliding objects in the world of specified type
// See PhysicsCollisionGroups.h for mask flags. // See PhysicsCollisionGroups.h for mask flags.
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const;
private: private:
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body); QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);

View file

@ -17,6 +17,7 @@
#include <QVariant> #include <QVariant>
#include <shared/ReadWriteLockable.h> #include <shared/ReadWriteLockable.h>
#include <TransformNode.h>
enum IntersectionType { enum IntersectionType {
NONE = 0, NONE = 0,
@ -213,6 +214,10 @@ public:
virtual bool isRightHand() const { return false; } virtual bool isRightHand() const { return false; }
virtual bool isMouse() const { return false; } virtual bool isMouse() const { return false; }
virtual Transform getResultTransform() const = 0;
std::shared_ptr<TransformNode> parentTransform;
private: private:
PickFilter _filter; PickFilter _filter;
const float _maxDistance; const float _maxDistance;

View file

@ -57,8 +57,8 @@ bool PickCacheOptimizer<T>::checkAndCompareCachedResults(T& pick, PickCache& cac
} }
template<typename T> template<typename T>
void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) { void PickCacheOptimizer<T>::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
if (intersects) { if (needToCompareResults) {
cache[mathPick][key] = resTemp; cache[mathPick][key] = resTemp;
res = res->compareAndProcessNewResult(resTemp); res = res->compareAndProcessNewResult(resTemp);
} else { } else {

View file

@ -90,6 +90,14 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
} }
} }
Transform PickManager::getResultTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {
return pick->getResultTransform();
}
return Transform();
}
void PickManager::update() { void PickManager::update() {
uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget; uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget;
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks; std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;

View file

@ -43,6 +43,8 @@ public:
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const; void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const; void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
Transform getResultTransform(unsigned int uid) const;
bool isLeftHand(unsigned int uid); bool isLeftHand(unsigned int uid);
bool isRightHand(unsigned int uid); bool isRightHand(unsigned int uid);
bool isMouse(unsigned int uid); bool isMouse(unsigned int uid);

View file

@ -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<PickManager>();
if (!pickManager) {
return Transform();
}
return pickManager->getResultTransform(_uid);
}

View file

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

View file

@ -50,6 +50,8 @@ public:
virtual void setRenderState(const std::string& state) = 0; 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 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 setPrecisionPicking(bool precisionPicking);
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const; virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const; virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;

View file

@ -77,6 +77,15 @@ PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const {
return result; return result;
} }
QVariantMap PointerManager::getPointerProperties(unsigned int uid) const {
auto pointer = find(uid);
if (pointer) {
return pointer->toVariantMap();
} else {
return QVariantMap();
}
}
void PointerManager::update() { void PointerManager::update() {
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] { auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
return _pointers; return _pointers;

View file

@ -30,6 +30,7 @@ public:
void setRenderState(unsigned int uid, const std::string& renderState) const; 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; 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; PickResultPointer getPrevPickResult(unsigned int uid) const;
QVariantMap getPointerProperties(unsigned int uid) const;
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const; void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;

View file

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

View file

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

View file

@ -253,18 +253,36 @@ public:
} }
}; };
/**jsdoc // TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported.
* A CollisionPick defines a volume for checking collisions in the physics simulation.
* @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 {Shape} shape - The information about the collision region's size and shape.
* @property {Vec3} position - The position 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. * @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 { class CollisionRegion : public MathPick {
public: public:
CollisionRegion() { } CollisionRegion() { }
CollisionRegion(const CollisionRegion& collisionRegion) :
loaded(collisionRegion.loaded),
modelURL(collisionRegion.modelURL),
shapeInfo(std::make_shared<ShapeInfo>()),
transform(collisionRegion.transform),
threshold(collisionRegion.threshold)
{
shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString());
}
CollisionRegion(const QVariantMap& pickVariant) { 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()) { if (pickVariant["shape"].isValid()) {
auto shape = pickVariant["shape"].toMap(); auto shape = pickVariant["shape"].toMap();
if (!shape.empty()) { if (!shape.empty()) {
@ -287,6 +305,10 @@ public:
} }
} }
if (pickVariant["threshold"].isValid()) {
threshold = glm::max(0.0f, pickVariant["threshold"].toFloat());
}
if (pickVariant["position"].isValid()) { if (pickVariant["position"].isValid()) {
transform.setTranslation(vec3FromVariant(pickVariant["position"])); transform.setTranslation(vec3FromVariant(pickVariant["position"]));
} }
@ -304,6 +326,9 @@ public:
shape["dimensions"] = vec3toVariant(transform.getScale()); shape["dimensions"] = vec3toVariant(transform.getScale());
collisionRegion["shape"] = shape; collisionRegion["shape"] = shape;
collisionRegion["loaded"] = loaded;
collisionRegion["threshold"] = threshold;
collisionRegion["position"] = vec3toVariant(transform.getTranslation()); collisionRegion["position"] = vec3toVariant(transform.getTranslation());
collisionRegion["orientation"] = quatToVariant(transform.getRotation()); collisionRegion["orientation"] = quatToVariant(transform.getRotation());
@ -312,13 +337,16 @@ public:
} }
operator bool() const override { 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())) || glm::any(glm::isnan(transform.getRotation())) ||
shapeInfo->getType() == SHAPE_TYPE_NONE); shapeInfo->getType() == SHAPE_TYPE_NONE);
} }
bool operator==(const CollisionRegion& other) const { 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.getRotation(), other.transform.getRotation())) &&
glm::all(glm::equal(transform.getScale(), other.transform.getScale())) && glm::all(glm::equal(transform.getScale(), other.transform.getScale())) &&
shapeInfo->getType() == other.shapeInfo->getType() && 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 // 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; QUrl modelURL;
// We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick // 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> shapeInfo = std::make_shared<ShapeInfo>(); std::shared_ptr<ShapeInfo> shapeInfo = std::make_shared<ShapeInfo>();
Transform transform; Transform transform;
float threshold;
}; };
namespace std { namespace std {

View file

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