Merge branch 'master' of github.com:highfidelity/hifi into feat/avatarTools/avatarPackager

This commit is contained in:
Thijs Wenker 2019-01-03 00:10:22 +01:00
commit cb91a4bc6d
55 changed files with 1540 additions and 780 deletions

View file

@ -276,6 +276,7 @@ void AssignmentClientMonitor::checkSpares() {
// Spawn or kill children, as needed. If --min or --max weren't specified, allow the child count
// to drift up or down as far as needed.
if (spareCount < 1 || totalCount < _minAssignmentClientForks) {
if (!_maxAssignmentClientForks || totalCount < _maxAssignmentClientForks) {
spawnChildClient();
@ -307,7 +308,7 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
AssignmentClientChildData* childData = nullptr;
if (!matchingNode) {
// The parent only expects to be talking with prorams running on this same machine.
// The parent only expects to be talking with programs running on this same machine.
if (senderSockAddr.getAddress() == QHostAddress::LocalHost ||
senderSockAddr.getAddress() == QHostAddress::LocalHostIPv6) {
@ -316,9 +317,9 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode(senderID, NodeType::Unassigned,
senderSockAddr, senderSockAddr);
auto childData = std::unique_ptr<AssignmentClientChildData>
auto newChildData = std::unique_ptr<AssignmentClientChildData>
{ new AssignmentClientChildData(Assignment::Type::AllTypes) };
matchingNode->setLinkedData(std::move(childData));
matchingNode->setLinkedData(std::move(newChildData));
} else {
// tell unknown assignment-client child to exit.
qDebug() << "Asking unknown child at" << senderSockAddr << "to exit.";
@ -329,9 +330,8 @@ void AssignmentClientMonitor::handleChildStatusPacket(QSharedPointer<ReceivedMes
return;
}
}
} else {
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
}
childData = dynamic_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
if (childData) {
// update our records about how to reach this child

View file

@ -1080,6 +1080,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
auto nodeList = DependencyManager::get<NodeList>();
nodeList->startThread();
nodeList->setFlagTimeForConnectionStep(true);
// move the AddressManager to the NodeList thread so that domain resets due to domain changes always occur
// before we tell MyAvatar to go to a new location in the new domain
@ -4914,7 +4915,7 @@ void Application::calibrateEyeTracker5Points() {
#endif
bool Application::exportEntities(const QString& filename,
const QVector<EntityItemID>& entityIDs,
const QVector<QUuid>& entityIDs,
const glm::vec3* givenOffset) {
QHash<EntityItemID, EntityItemPointer> entities;
@ -4989,16 +4990,12 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
glm::vec3 minCorner = center - vec3(scale);
float cubeSize = scale * 2;
AACube boundingCube(minCorner, cubeSize);
QVector<EntityItemPointer> entities;
QVector<EntityItemID> ids;
QVector<QUuid> entities;
auto entityTree = getEntities()->getTree();
entityTree->withReadLock([&] {
entityTree->findEntities(boundingCube, entities);
foreach(EntityItemPointer entity, entities) {
ids << entity->getEntityItemID();
}
entityTree->evalEntitiesInCube(boundingCube, PickFilter(), entities);
});
return exportEntities(filename, ids, &center);
return exportEntities(filename, entities, &center);
}
void Application::loadSettings() {

View file

@ -351,7 +351,7 @@ signals:
public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);
bool exportEntities(const QString& filename, const QVector<QUuid>& entityIDs, const glm::vec3* givenOffset = nullptr);
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& url, const bool isObservable = true, const qint64 callerId = -1);
void updateThreadPoolCount() const;

View file

@ -3187,17 +3187,15 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette
OctreeElementPointer element;
float distance;
BoxFace face;
const bool visibleOnly = false;
// This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable.
// What we really want is to use the collision hull!
// See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders
const bool collidableOnly = true;
const bool precisionPicking = true;
const auto lockType = Octree::Lock; // Should we refactor to take a lock just once?
bool* accurateResult = NULL;
// This isn't quite what we really want here. findRayIntersection always works on mesh, skipping entirely based on collidable.
// What we really want is to use the collision hull!
// See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders
QVariantMap extraInfo;
EntityItemID entityID = entityTree->findRayIntersection(startPointIn, directionIn, include, ignore, visibleOnly, collidableOnly, precisionPicking,
EntityItemID entityID = entityTree->evalRayIntersection(startPointIn, directionIn, include, ignore,
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)),
element, distance, face, normalOut, extraInfo, lockType, accurateResult);
if (entityID.isNull()) {
return false;

View file

@ -125,7 +125,7 @@ LaserPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID
StartEndRenderState(startID, endID), _pathID(pathID)
{
if (!_pathID.isNull()) {
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool();
_pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignorePickIntersection").value.toBool();
_lineWidth = qApp->getOverlays().getProperty(_pathID, "lineWidth").value.toFloat();
}
}
@ -142,7 +142,7 @@ void LaserPointer::RenderState::disable() {
if (!getPathID().isNull()) {
QVariantMap pathProps;
pathProps.insert("visible", false);
pathProps.insert("ignoreRayIntersection", true);
pathProps.insert("ignorePickIntersection", true);
qApp->getOverlays().editOverlay(getPathID(), pathProps);
}
}
@ -156,7 +156,7 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
pathProps.insert("start", vec3toVariant(origin));
pathProps.insert("end", endVariant);
pathProps.insert("visible", true);
pathProps.insert("ignoreRayIntersection", doesPathIgnoreRays());
pathProps.insert("ignorePickIntersection", doesPathIgnoreRays());
pathProps.insert("lineWidth", getLineWidth() * parentScale);
qApp->getOverlays().editOverlay(getPathID(), pathProps);
}

View file

@ -21,7 +21,7 @@ class LaserPointerScriptingInterface : public QObject, public Dependency {
SINGLETON_DEPENDENCY
/**jsdoc
* Synonym for {@link Pointers} as used for laser pointers.
* Synonym for {@link Pointers} as used for laser pointers. Deprecated.
*
* @namespace LaserPointers
*

View file

@ -9,6 +9,7 @@
#include "Application.h"
#include "EntityScriptingInterface.h"
#include "PickScriptingInterface.h"
#include "ui/overlays/Overlays.h"
#include "avatar/AvatarManager.h"
#include "scripting/HMDScriptingInterface.h"
@ -57,10 +58,15 @@ PickParabola ParabolaPick::getMathematicalPick() const {
PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
PickFilter searchFilter = getFilter();
if (DependencyManager::get<PickManager>()->getForceCoarsePicking()) {
searchFilter.setFlag(PickFilter::COARSE, true);
searchFilter.setFlag(PickFilter::PRECISE, false);
}
ParabolaToEntityIntersectionResult entityRes =
DependencyManager::get<EntityScriptingInterface>()->findParabolaIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
DependencyManager::get<EntityScriptingInterface>()->evalParabolaIntersectionVector(pick, searchFilter,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
if (entityRes.intersects) {
return std::make_shared<ParabolaPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
}
@ -70,7 +76,7 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick)
PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
ParabolaToOverlayIntersectionResult overlayRes =
qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());

View file

@ -253,12 +253,12 @@ StartEndRenderState::StartEndRenderState(const OverlayID& startID, const Overlay
_startID(startID), _endID(endID) {
if (!_startID.isNull()) {
_startDim = vec3FromVariant(qApp->getOverlays().getProperty(_startID, "dimensions").value);
_startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignoreRayIntersection").value.toBool();
_startIgnoreRays = qApp->getOverlays().getProperty(_startID, "ignorePickIntersection").value.toBool();
}
if (!_endID.isNull()) {
_endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value);
_endRot = quatFromVariant(qApp->getOverlays().getProperty(_endID, "rotation").value);
_endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool();
_endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignorePickIntersection").value.toBool();
}
}
@ -275,13 +275,13 @@ void StartEndRenderState::disable() {
if (!getStartID().isNull()) {
QVariantMap startProps;
startProps.insert("visible", false);
startProps.insert("ignoreRayIntersection", true);
startProps.insert("ignorePickIntersection", true);
qApp->getOverlays().editOverlay(getStartID(), startProps);
}
if (!getEndID().isNull()) {
QVariantMap endProps;
endProps.insert("visible", false);
endProps.insert("ignoreRayIntersection", true);
endProps.insert("ignorePickIntersection", true);
qApp->getOverlays().editOverlay(getEndID(), endProps);
}
_enabled = false;
@ -294,7 +294,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end,
startProps.insert("position", vec3toVariant(origin));
startProps.insert("visible", true);
startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale));
startProps.insert("ignoreRayIntersection", doesStartIgnoreRays());
startProps.insert("ignorePickIntersection", doesStartIgnoreRays());
qApp->getOverlays().editOverlay(getStartID(), startProps);
}
@ -346,7 +346,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end,
endProps.insert("position", vec3toVariant(position));
endProps.insert("rotation", quatToVariant(rotation));
endProps.insert("visible", true);
endProps.insert("ignoreRayIntersection", doesEndIgnoreRays());
endProps.insert("ignorePickIntersection", doesEndIgnoreRays());
qApp->getOverlays().editOverlay(getEndID(), endProps);
}
_enabled = true;

View file

@ -49,11 +49,17 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
}
}
PickFilter getPickFilter(unsigned int filter) {
// FIXME: Picks always intersect visible and collidable things right now
filter = filter | (PickScriptingInterface::PICK_INCLUDE_VISIBLE() | PickScriptingInterface::PICK_INCLUDE_COLLIDABLE());
return PickFilter(filter);
}
/**jsdoc
* A set of properties that can be passed to {@link Picks.createPick} to create a new Ray Pick.
* @typedef {object} Picks.RayPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
@ -73,7 +79,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
PickFilter filter = PickFilter();
if (propMap["filter"].isValid()) {
filter = PickFilter(propMap["filter"].toUInt());
filter = getPickFilter(propMap["filter"].toUInt());
}
float maxDistance = 0.0f;
@ -111,7 +117,7 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
* @typedef {object} Picks.StylusPickProperties
* @property {number} [hand=-1] An integer. 0 == left, 1 == right. Invalid otherwise.
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
*/
unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties) {
@ -132,7 +138,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties
PickFilter filter = PickFilter();
if (propMap["filter"].isValid()) {
filter = PickFilter(propMap["filter"].toUInt());
filter = getPickFilter(propMap["filter"].toUInt());
}
float maxDistance = 0.0f;
@ -153,7 +159,7 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties
* A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick.
* @typedef {object} Picks.ParabolaPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
@ -178,7 +184,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
PickFilter filter = PickFilter();
if (propMap["filter"].isValid()) {
filter = PickFilter(propMap["filter"].toUInt());
filter = getPickFilter(propMap["filter"].toUInt());
}
float maxDistance = 0.0f;
@ -250,7 +256,7 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
* @typedef {object} Picks.CollisionPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [filter=0] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {Shape} shape - The information about the collision region's size and shape. Dimensions are in world space, but will scale with the parent if defined.
* @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.
@ -273,7 +279,7 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert
PickFilter filter = PickFilter();
if (propMap["filter"].isValid()) {
filter = PickFilter(propMap["filter"].toUInt());
filter = getPickFilter(propMap["filter"].toUInt());
}
float maxDistance = 0.0f;

View file

@ -14,6 +14,7 @@
#include <DependencyManager.h>
#include <PhysicsEngine.h>
#include <Pick.h>
#include <PickFilter.h>
/**jsdoc
* The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways.
@ -23,41 +24,62 @@
* @hifi-interface
* @hifi-client-entity
*
* @property {number} PICK_NOTHING A filter flag. Don't intersect with anything. <em>Read-only.</em>
* @property {number} PICK_ENTITIES A filter flag. Include entities when intersecting. <em>Read-only.</em>
* @property {number} PICK_OVERLAYS A filter flag. Include overlays when intersecting. <em>Read-only.</em>
* @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. <em>Read-only.</em>
* @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>
* @property {number} PICK_COARSE A filter flag. Pick against coarse meshes, instead of exact meshes. <em>Read-only.</em>
* @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>
* @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting.
* <em>Read-only.</em>
* @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>
* @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags.
* <em>Read-only.</em>
* @property {number} PICK_ENTITIES A filter flag. Include domain and avatar entities when intersecting. <em>Read-only.</em>. Deprecated.
* @property {number} PICK_OVERLAYS A filter flag. Include local entities when intersecting. <em>Read-only.</em>. Deprecated.
*
* @property {number} PICK_DOMAIN_ENTITIES A filter flag. Include domain entities when intersecting. <em>Read-only.</em>.
* @property {number} PICK_AVATAR_ENTITIES A filter flag. Include avatar entities when intersecting. <em>Read-only.</em>.
* @property {number} PICK_LOCAL_ENTITIES A filter flag. Include local entities when intersecting. <em>Read-only.</em>.
* @property {number} PICK_AVATARS A filter flag. Include avatars when intersecting. <em>Read-only.</em>.
* @property {number} PICK_HUD A filter flag. Include the HUD sphere when intersecting in HMD mode. <em>Read-only.</em>.
*
* @property {number} PICK_INCLUDE_VISIBLE A filter flag. Include visible objects when intersecting. <em>Read-only.</em>.
* @property {number} PICK_INCLUDE_INVISIBLE A filter flag. Include invisible objects when intersecting. <em>Read-only.</em>.
*
* @property {number} PICK_INCLUDE_COLLIDABLE A filter flag. Include collidable objects when intersecting. <em>Read-only.</em>.
* @property {number} PICK_INCLUDE_NONCOLLIDABLE A filter flag. Include non-collidable objects when intersecting. <em>Read-only.</em>.
*
* @property {number} PICK_PRECISE A filter flag. Pick against exact meshes. <em>Read-only.</em>.
* @property {number} PICK_COARSE A filter flag. Pick against coarse meshes. <em>Read-only.</em>.
*
* @property {number} PICK_ALL_INTERSECTIONS <em>Read-only.</em>.
*
* @property {number} INTERSECTED_NONE An intersection type. Intersected nothing with the given filter flags. <em>Read-only.</em>
* @property {number} INTERSECTED_ENTITY An intersection type. Intersected an entity. <em>Read-only.</em>
* @property {number} INTERSECTED_OVERLAY An intersection type. Intersected an overlay. <em>Read-only.</em>
* @property {number} INTERSECTED_AVATAR An intersection type. Intersected an avatar. <em>Read-only.</em>
* @property {number} INTERSECTED_HUD An intersection type. Intersected the HUD sphere. <em>Read-only.</em>
* @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results. <em>Read-only.</em>
* @property {number} perFrameTimeBudget - The max number of usec to spend per frame updating Pick results.
*/
class PickScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT)
Q_PROPERTY(unsigned int PICK_DOMAIN_ENTITIES READ PICK_DOMAIN_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATAR_ENTITIES READ PICK_AVATAR_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_LOCAL_ENTITIES READ PICK_LOCAL_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT)
Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT)
Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_VISIBLE READ PICK_INCLUDE_VISIBLE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_COLLIDABLE READ PICK_INCLUDE_COLLIDABLE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT)
Q_PROPERTY(unsigned int PICK_PRECISE READ PICK_PRECISE CONSTANT)
Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT)
Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT)
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
SINGLETON_DEPENDENCY
public:
@ -72,11 +94,13 @@ public:
* Adds a new Pick.
* Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick.
* Picks created with this method always intersect at least visible and collidable things
* @function Picks.createPick
* @param {PickType} type A PickType that specifies the method of picking to use
* @param {Picks.RayPickProperties|Picks.StylusPickProperties|Picks.ParabolaPickProperties|Picks.CollisionPickProperties} properties A PickProperties object, containing all the properties for initializing this Pick
* @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid.
*/
// TODO: expand Pointers to be able to be fully configurable with PickFilters
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
/**jsdoc
@ -227,61 +251,80 @@ public:
*/
Q_INVOKABLE bool isMouse(unsigned int uid);
// FIXME: Move to other property definitions.
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
unsigned int getPerFrameTimeBudget() const;
void setPerFrameTimeBudget(unsigned int numUsecs);
public slots:
/**jsdoc
* @function Picks.PICK_NOTHING
* @returns {number}
*/
static constexpr unsigned int PICK_NOTHING() { return 0; }
/**jsdoc
* @function Picks.PICK_ENTITIES
* @returns {number}
*/
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); }
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); }
/**jsdoc
* @function Picks.PICK_OVERLAYS
* @returns {number}
*/
static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); }
static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); }
/**jsdoc
* @function Picks.PICK_DOMAIN_ENTITIES
* @returns {number}
*/
static constexpr unsigned int PICK_DOMAIN_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); }
/**jsdoc
* @function Picks.PICK_AVATAR_ENTITIES
* @returns {number}
*/
static constexpr unsigned int PICK_AVATAR_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); }
/**jsdoc
* @function Picks.PICK_LOCAL_ENTITIES
* @returns {number}
*/
static constexpr unsigned int PICK_LOCAL_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); }
/**jsdoc
* @function Picks.PICK_AVATARS
* @returns {number}
*/
static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_AVATARS); }
static constexpr unsigned int PICK_AVATARS() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); }
/**jsdoc
* @function Picks.PICK_HUD
* @returns {number}
*/
static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_HUD); }
static constexpr unsigned int PICK_HUD() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); }
/**jsdoc
* @function Picks.PICK_COARSE
* @function Picks.PICK_INCLUDE_VISIBLE
* @returns {number}
*/
static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_COARSE); }
static constexpr unsigned int PICK_INCLUDE_VISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); }
/**jsdoc
* @function Picks.PICK_INCLUDE_INVISIBLE
* @returns {number}
*/
static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_INVISIBLE); }
static constexpr unsigned int PICK_INCLUDE_INVISIBLE() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); }
/**jsdoc
* @function Picks.PICK_INCLUDE_COLLIDABLE
* @returns {number}
*/
static constexpr unsigned int PICK_INCLUDE_COLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); }
/**jsdoc
* @function Picks.PICK_INCLUDE_NONCOLLIDABLE
* @returns {number}
*/
static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_INCLUDE_NONCOLLIDABLE); }
static constexpr unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); }
/**jsdoc
* @function Picks.PICK_PRECISE
* @returns {number}
*/
static constexpr unsigned int PICK_PRECISE() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); }
/**jsdoc
* @function Picks.PICK_COARSE
* @returns {number}
*/
static constexpr unsigned int PICK_COARSE() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); }
/**jsdoc
* @function Picks.PICK_ALL_INTERSECTIONS

View file

@ -41,10 +41,12 @@ public:
* @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered,
* it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None".
*/
/**jsdoc
* Adds a new Pointer
* Different {@link PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example,
* with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer.
* Pointers created with this method always intersect at least visible and collidable things
* @function Pointers.createPointer
* @param {PickType} type A PickType that specifies the method of picking to use
* @param {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer <b>and</b> the {@link Picks.PickProperties} for the Pick that
@ -58,21 +60,21 @@ public:
* dimensions: {x:0.5, y:0.5, z:0.5},
* solid: true,
* color: {red:0, green:255, blue:0},
* ignoreRayIntersection: true
* ignorePickIntersection: true
* };
* var end2 = {
* type: "sphere",
* dimensions: {x:0.5, y:0.5, z:0.5},
* solid: true,
* color: {red:255, green:0, blue:0},
* ignoreRayIntersection: true
* ignorePickIntersection: true
* };
*
* var renderStates = [ {name: "test", end: end} ];
* var defaultRenderStates = [ {name: "test", distance: 10.0, end: end2} ];
* var pointer = Pointers.createPointer(PickType.Ray, {
* joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
* filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
* filter: Picks.PICK_LOCAL_ENTITIES | Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
* renderStates: renderStates,
* defaultRenderStates: defaultRenderStates,
* distanceScaleEnd: true,
@ -82,6 +84,7 @@ public:
* });
* Pointers.setRenderState(pointer, "test");
*/
// TODO: expand Pointers to be able to be fully configurable with PickFilters
Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties);
/**jsdoc

View file

@ -27,10 +27,15 @@ PickRay RayPick::getMathematicalPick() const {
}
PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
PickFilter searchFilter = getFilter();
if (DependencyManager::get<PickManager>()->getForceCoarsePicking()) {
searchFilter.setFlag(PickFilter::COARSE, true);
searchFilter.setFlag(PickFilter::PRECISE, false);
}
RayToEntityIntersectionResult entityRes =
DependencyManager::get<EntityScriptingInterface>()->findRayIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());
DependencyManager::get<EntityScriptingInterface>()->evalRayIntersectionVector(pick, searchFilter,
getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
if (entityRes.intersects) {
return std::make_shared<RayPickResult>(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo);
} else {
@ -39,7 +44,7 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
}
PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
RayToOverlayIntersectionResult overlayRes =
qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking,
getIncludeItemsAs<OverlayID>(), getIgnoreItemsAs<OverlayID>(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable());

View file

@ -19,14 +19,13 @@
#include "PickScriptingInterface.h"
/**jsdoc
* Synonym for {@link Picks} as used for ray picks.
* Synonym for {@link Picks} as used for ray picks. Deprecated.
*
* @namespace RayPick
*
* @hifi-interface
* @hifi-client-entity
*
* @property {number} PICK_NOTHING <em>Read-only.</em>
* @property {number} PICK_ENTITIES <em>Read-only.</em>
* @property {number} PICK_OVERLAYS <em>Read-only.</em>
* @property {number} PICK_AVATARS <em>Read-only.</em>
@ -44,7 +43,6 @@
class RayPickScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT)
@ -140,12 +138,6 @@ public:
public slots:
/**jsdoc
* @function RayPick.PICK_NOTHING
* @returns {number}
*/
static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); }
/**jsdoc
* @function RayPick.PICK_ENTITIES
* @returns {number}

View file

@ -61,7 +61,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) {
overlayProperties["loadPriority"] = 10.0f;
overlayProperties["solid"] = true;
overlayProperties["visible"] = false;
overlayProperties["ignoreRayIntersection"] = true;
overlayProperties["ignorePickIntersection"] = true;
overlayProperties["drawInFront"] = false;
return qApp->getOverlays().addOverlay("model", overlayProperties);

View file

@ -781,7 +781,7 @@ void Keyboard::loadKeyboardFile(const QString& keyboardFile) {
{ "isSolid", true },
{ "visible", false },
{ "grabbable", true },
{ "ignoreRayIntersection", false },
{ "ignorePickIntersection", false },
{ "dimensions", anchorObject["dimensions"].toVariant() },
{ "position", anchorObject["position"].toVariant() },
{ "orientation", anchorObject["rotation"].toVariant() }

View file

@ -85,7 +85,7 @@ void Grid3DOverlay::render(RenderArgs* args) {
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
_minorGridRowDivisions, _minorGridColDivisions, MINOR_GRID_EDGE,
_majorGridRowDivisions, _majorGridColDivisions, MAJOR_GRID_EDGE,
gridColor, _drawInFront, _geometryId);
gridColor, _geometryId);
}
}

View file

@ -498,20 +498,27 @@ void EntityTreeRenderer::handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUp
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
bool didUpdate = false;
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
QVector<EntityItemPointer> foundEntities;
QVector<QUuid> entityIDs;
// find the entities near us
// don't let someone else change our tree while we search
_tree->withReadLock([&] {
auto entityTree = std::static_pointer_cast<EntityTree>(_tree);
// FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster
std::static_pointer_cast<EntityTree>(_tree)->findEntities(_avatarPosition, radius, foundEntities);
entityTree->evalEntitiesInSphere(_avatarPosition, radius,
PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), entityIDs);
LayeredZones oldLayeredZones(std::move(_layeredZones));
_layeredZones.clear();
// create a list of entities that actually contain the avatar's position
for (auto& entity : foundEntities) {
for (auto& entityID : entityIDs) {
auto entity = entityTree->findEntityByID(entityID);
if (!entity) {
continue;
}
auto isZone = entity->getType() == EntityTypes::Zone;
auto hasScript = !entity->getScript().isEmpty();

View file

@ -26,6 +26,7 @@
#include "RenderableZoneEntityItem.h"
#include "RenderableMaterialEntityItem.h"
#include "RenderableImageEntityItem.h"
#include "RenderableGridEntityItem.h"
using namespace render;
@ -259,6 +260,10 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer,
result = make_renderer<PolyVoxEntityRenderer>(entity);
break;
case Type::Grid:
result = make_renderer<GridEntityRenderer>(entity);
break;
case Type::Light:
result = make_renderer<LightEntityRenderer>(entity);
break;

View file

@ -0,0 +1,148 @@
//
// Created by Sam Gondelman on 11/29/18
// 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 "RenderableGridEntityItem.h"
#include <DependencyManager.h>
#include <GeometryCache.h>
using namespace render;
using namespace render::entities;
GridEntityRenderer::GridEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
}
GridEntityRenderer::~GridEntityRenderer() {
auto geometryCache = DependencyManager::get<GeometryCache>();
if (geometryCache) {
geometryCache->releaseID(_geometryId);
}
}
bool GridEntityRenderer::isTransparent() const {
return Parent::isTransparent() || _alpha < 1.0f;
}
bool GridEntityRenderer::needsRenderUpdate() const {
return Parent::needsRenderUpdate();
}
bool GridEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
bool needsUpdate = resultWithReadLock<bool>([&] {
if (_color != entity->getColor()) {
return true;
}
if (_alpha != entity->getAlpha()) {
return true;
}
if (_followCamera != entity->getFollowCamera()) {
return true;
}
if (_majorGridEvery != entity->getMajorGridEvery()) {
return true;
}
if (_minorGridEvery != entity->getMinorGridEvery()) {
return true;
}
return false;
});
return needsUpdate;
}
void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
withWriteLock([&] {
_color = entity->getColor();
_alpha = entity->getAlpha();
_followCamera = entity->getFollowCamera();
_majorGridEvery = entity->getMajorGridEvery();
_minorGridEvery = entity->getMinorGridEvery();
});
void* key = (void*)this;
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
withWriteLock([&] {
_dimensions = entity->getScaledDimensions();
updateModelTransformAndBound();
_renderTransform = getModelTransform();
});
});
}
Item::Bound GridEntityRenderer::getBound() {
if (_followCamera) {
// This is a UI element that should always be in view, lie to the octree to avoid culling
const AABox DOMAIN_BOX = AABox(glm::vec3(-TREE_SCALE / 2), TREE_SCALE);
return DOMAIN_BOX;
}
return Parent::getBound();
}
ShapeKey GridEntityRenderer::getShapeKey() {
auto builder = render::ShapeKey::Builder().withOwnPipeline().withUnlit().withDepthBias();
if (isTransparent()) {
builder.withTranslucent();
}
return builder.build();
}
void GridEntityRenderer::doRender(RenderArgs* args) {
glm::u8vec3 color;
glm::vec3 dimensions;
Transform renderTransform;
withReadLock([&] {
color = _color;
dimensions = _dimensions;
renderTransform = _renderTransform;
});
if (!_visible) {
return;
}
auto batch = args->_batch;
Transform transform;
transform.setScale(dimensions);
transform.setRotation(renderTransform.getRotation());
if (_followCamera) {
// Get the camera position rounded to the nearest major grid line
// This grid is for UI and should lie on worldlines
glm::vec3 localCameraPosition = glm::inverse(transform.getRotation()) * (args->getViewFrustum().getPosition() - renderTransform.getTranslation());
localCameraPosition.z = 0;
localCameraPosition = (float)_majorGridEvery * glm::round(localCameraPosition / (float)_majorGridEvery);
transform.setTranslation(renderTransform.getTranslation() + transform.getRotation() * localCameraPosition);
} else {
transform.setTranslation(renderTransform.getTranslation());
}
batch->setModelTransform(transform);
auto minCorner = glm::vec2(-0.5f, -0.5f);
auto maxCorner = glm::vec2(0.5f, 0.5f);
float majorGridRowDivisions = dimensions.x / _majorGridEvery;
float majorGridColDivisions = dimensions.y / _majorGridEvery;
float minorGridRowDivisions = dimensions.x / _minorGridEvery;
float minorGridColDivisions = dimensions.y / _minorGridEvery;
glm::vec4 gridColor(toGlm(color), _alpha);
const float MINOR_GRID_EDGE = 0.0025f;
const float MAJOR_GRID_EDGE = 0.005f;
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
minorGridRowDivisions, minorGridColDivisions, MINOR_GRID_EDGE,
majorGridRowDivisions, majorGridColDivisions, MAJOR_GRID_EDGE,
gridColor, _geometryId);
}

View file

@ -0,0 +1,51 @@
//
// Created by Sam Gondelman on 11/29/18
// 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_RenderableGridEntityItem_h
#define hifi_RenderableGridEntityItem_h
#include "RenderableEntityItem.h"
#include <GridEntityItem.h>
namespace render { namespace entities {
class GridEntityRenderer : public TypedEntityRenderer<GridEntityItem> {
using Parent = TypedEntityRenderer<GridEntityItem>;
using Pointer = std::shared_ptr<GridEntityRenderer>;
public:
GridEntityRenderer(const EntityItemPointer& entity);
~GridEntityRenderer();
protected:
Item::Bound getBound() override;
ShapeKey getShapeKey() override;
bool isTransparent() const override;
private:
virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
glm::u8vec3 _color;
float _alpha;
bool _followCamera;
uint32_t _majorGridEvery;
float _minorGridEvery;
glm::vec3 _dimensions;
int _geometryId { 0 };
};
} }
#endif // hifi_RenderableGridEntityItem_h

View file

@ -13,44 +13,42 @@
#include <ImageEntityItem.h>
namespace render {
namespace entities {
namespace render { namespace entities {
class ImageEntityRenderer : public TypedEntityRenderer<ImageEntityItem> {
using Parent = TypedEntityRenderer<ImageEntityItem>;
using Pointer = std::shared_ptr<ImageEntityRenderer>;
public:
ImageEntityRenderer(const EntityItemPointer& entity);
~ImageEntityRenderer();
class ImageEntityRenderer : public TypedEntityRenderer<ImageEntityItem> {
using Parent = TypedEntityRenderer<ImageEntityItem>;
using Pointer = std::shared_ptr<ImageEntityRenderer>;
public:
ImageEntityRenderer(const EntityItemPointer& entity);
~ImageEntityRenderer();
protected:
ShapeKey getShapeKey() override;
protected:
ShapeKey getShapeKey() override;
bool isTransparent() const override;
bool isTransparent() const override;
private:
virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
private:
virtual bool needsRenderUpdate() const override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
virtual void doRender(RenderArgs* args) override;
QString _imageURL;
NetworkTexturePointer _texture;
bool _textureIsLoaded { false };
QString _imageURL;
NetworkTexturePointer _texture;
bool _textureIsLoaded { false };
bool _emissive;
bool _keepAspectRatio;
BillboardMode _billboardMode;
QRect _subImage;
bool _emissive;
bool _keepAspectRatio;
BillboardMode _billboardMode;
QRect _subImage;
glm::u8vec3 _color;
float _alpha;
glm::u8vec3 _color;
float _alpha;
glm::vec3 _dimensions;
glm::vec3 _dimensions;
int _geometryId { 0 };
};
int _geometryId { 0 };
};
}
}
} }
#endif // hifi_RenderableImageEntityItem_h

View file

@ -439,6 +439,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_KEEP_ASPECT_RATIO, keepAspectRatio);
CHECK_PROPERTY_CHANGE(PROP_SUB_IMAGE, subImage);
CHECK_PROPERTY_CHANGE(PROP_GRID_FOLLOW_CAMERA, followCamera);
CHECK_PROPERTY_CHANGE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
CHECK_PROPERTY_CHANGE(PROP_MINOR_GRID_EVERY, minorGridEvery);
// Certifiable Properties
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
CHECK_PROPERTY_CHANGE(PROP_ITEM_DESCRIPTION, itemDescription);
@ -714,6 +718,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @see {@link Entities.EntityProperties-Line|EntityProperties-Line}
* @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine}
* @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}
* @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid}
* @see {@link Entities.EntityProperties-Light|EntityProperties-Light}
* @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone}
* @see {@link Entities.EntityProperties-Material|EntityProperties-Material}
@ -1272,7 +1277,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* <code>"isFacingAvatar"</code> to <code>true</code> to set <code>billboardMode</code> to "full". Setting either to <code>false</code> sets the mode to "none"
* @property {Rect} subImage={ x: 0, y: 0, width: -1, height: -1 } - The portion of the image to display. If width or height are -1, defaults to
* the full image in that dimension.
* @property {Color} color=255,255,255 - The color of image.
* @property {Color} color=255,255,255 - The color of the image.
* @property {number} alpha=1 - The alpha of the image.
* @example <caption>Create a image entity.</caption>
* var image = Entities.addEntity({
@ -1285,6 +1290,30 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* });
*/
/**jsdoc
* The <code>"Grid"</code> {@link Entities.EntityType|EntityType} displays a grid on a 2D plane.
* It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
* @typedef {object} Entities.EntityProperties-Grid
* @property {Color} color=255,255,255 - The color of the grid.
* @property {number} alpha=1 - The alpha of the grid.
* @property {boolean} followCamera=true - If <code>true</code>, the grid is always visible even as the camera moves to another
* position.
* @property {number} majorGridEvery=5 - Integer number of <code>minorGridEvery</code> intervals at which to draw a thick grid
* line. Minimum value = <code>1</code>.
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
* <code>0.001</code>.
* @example <caption>Create a grid entity.</caption>
* var grid = Entities.addEntity({
* type: "Grid",
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })),
* dimensions: { x: 100.0, y: 100.0, z: 0.01 },
* followCamera: false,
* majorGridEvery: 4,
* minorGridEvery: 0.5,
* lifetime: 300 // Delete after 5 minutes.
* });
*/
QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime,
bool strictSemantics, EntityPsuedoPropertyFlags psueudoPropertyFlags) const {
@ -1577,6 +1606,16 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
}
}
// Grid only
if (_type == EntityTypes::Grid) {
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRID_FOLLOW_CAMERA, followCamera);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MINOR_GRID_EVERY, minorGridEvery);
}
/**jsdoc
* The axis-aligned bounding box of an entity.
* @typedef {object} Entities.BoundingBox
@ -1815,6 +1854,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(keepAspectRatio, bool, setKeepAspectRatio);
COPY_PROPERTY_FROM_QSCRIPTVALUE(subImage, QRect, setSubImage);
COPY_PROPERTY_FROM_QSCRIPTVALUE(followCamera, bool, setFollowCamera);
COPY_PROPERTY_FROM_QSCRIPTVALUE(majorGridEvery, uint32_t, setMajorGridEvery);
COPY_PROPERTY_FROM_QSCRIPTVALUE(minorGridEvery, float, setMinorGridEvery);
if (!honorReadOnly) {
// this is used by the json reader to set things that we don't want javascript to able to affect.
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(created, QDateTime, setCreated, [this]() {
@ -1999,6 +2042,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(keepAspectRatio);
COPY_PROPERTY_IF_CHANGED(subImage);
COPY_PROPERTY_IF_CHANGED(followCamera);
COPY_PROPERTY_IF_CHANGED(majorGridEvery);
COPY_PROPERTY_IF_CHANGED(minorGridEvery);
// Certifiable Properties
COPY_PROPERTY_IF_CHANGED(itemName);
COPY_PROPERTY_IF_CHANGED(itemDescription);
@ -2359,6 +2406,10 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool);
ADD_PROPERTY_TO_MAP(PROP_SUB_IMAGE, SubImage, subImage, QRect);
ADD_PROPERTY_TO_MAP(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool);
ADD_PROPERTY_TO_MAP(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float);
// FIXME - these are not yet handled
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
@ -2693,6 +2744,16 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
}
// Grid
if (properties.getType() == EntityTypes::Grid) {
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, properties.getFollowCamera());
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, properties.getMajorGridEvery());
APPEND_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, properties.getMinorGridEvery());
}
APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL());
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData());
@ -3101,6 +3162,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
}
// Grid
if (properties.getType() == EntityTypes::Grid) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MINOR_GRID_EVERY, float, setMinorGridEvery);
}
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData);
@ -3433,6 +3504,10 @@ void EntityItemProperties::markAllChanged() {
_emissiveChanged = true;
_keepAspectRatioChanged = true;
_subImageChanged = true;
_followCameraChanged = true;
_majorGridEveryChanged = true;
_minorGridEveryChanged = true;
}
// The minimum bounding box for the entity.
@ -3989,6 +4064,16 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "billboardMode";
}
if (followCameraChanged()) {
out += "followCamera";
}
if (majorGridEveryChanged()) {
out += "majorGridEvery";
}
if (minorGridEveryChanged()) {
out += "minorGridEvery";
}
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
getAmbientLight().listChangedProperties(out);

View file

@ -46,6 +46,7 @@
#include "BloomPropertyGroup.h"
#include "TextEntityItem.h"
#include "ZoneEntityItem.h"
#include "GridEntityItem.h"
#include "MaterialMappingMode.h"
#include "BillboardMode.h"
@ -80,6 +81,7 @@ class EntityItemProperties {
friend class LineEntityItem;
friend class PolyLineEntityItem;
friend class PolyVoxEntityItem;
friend class GridEntityItem;
friend class LightEntityItem;
friend class ZoneEntityItem;
friend class MaterialEntityItem;
@ -258,6 +260,10 @@ public:
DEFINE_PROPERTY_REF(PROP_KEEP_ASPECT_RATIO, KeepAspectRatio, keepAspectRatio, bool, true);
DEFINE_PROPERTY_REF(PROP_SUB_IMAGE, SubImage, subImage, QRect, QRect());
DEFINE_PROPERTY_REF(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool, true);
DEFINE_PROPERTY(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t, GridEntityItem::DEFAULT_MAJOR_GRID_EVERY);
DEFINE_PROPERTY(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float, GridEntityItem::DEFAULT_MINOR_GRID_EVERY);
// Certifiable Properties - related to Proof of Purchase certificates
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);

View file

@ -226,6 +226,7 @@ inline quint32 quint32_convertFromScriptValue(const QScriptValue& v, bool& isVal
}
inline quint16 quint16_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline uint32_t uint32_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); }
inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); }
inline uint8_t uint8_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return (uint8_t)(0xff & v.toVariant().toInt(&isValid)); }

View file

@ -318,6 +318,12 @@ enum EntityPropertyList {
PROP_IMAGE_URL = PROP_MODEL_URL,
PROP_KEEP_ASPECT_RATIO = PROP_ANIMATION_PLAYING,
// Aliases/Piggyback properties for Grid. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other.
PROP_GRID_FOLLOW_CAMERA = PROP_ANIMATION_PLAYING,
PROP_MAJOR_GRID_EVERY = PROP_ANIMATION_URL,
PROP_MINOR_GRID_EVERY = PROP_ANIMATION_FPS,
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
};

View file

@ -1085,13 +1085,10 @@ QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float
EntityItemID result;
if (_entityTree) {
EntityItemPointer closestEntity;
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->withReadLock([&] {
closestEntity = _entityTree->findClosestEntity(center, radius);
result = _entityTree->evalClosestEntity(center, radius, PickFilter(searchFilter));
});
if (closestEntity) {
result = closestEntity->getEntityItemID();
}
}
return result;
}
@ -1110,14 +1107,10 @@ QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, f
QVector<QUuid> result;
if (_entityTree) {
QVector<EntityItemPointer> entities;
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->withReadLock([&] {
_entityTree->findEntities(center, radius, entities);
_entityTree->evalEntitiesInSphere(center, radius, PickFilter(searchFilter), result);
});
foreach (EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
}
return result;
}
@ -1127,15 +1120,11 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
QVector<QUuid> result;
if (_entityTree) {
QVector<EntityItemPointer> entities;
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->withReadLock([&] {
AABox box(corner, dimensions);
_entityTree->findEntities(box, entities);
_entityTree->evalEntitiesInBox(box, PickFilter(searchFilter), result);
});
foreach (EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
}
return result;
}
@ -1170,14 +1159,10 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust
viewFrustum.calculate();
if (_entityTree) {
QVector<EntityItemPointer> entities;
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->withReadLock([&] {
_entityTree->findEntities(viewFrustum, entities);
_entityTree->evalEntitiesInFrustum(viewFrustum, PickFilter(searchFilter), result);
});
foreach(EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
}
}
@ -1189,86 +1174,64 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesByType(const QString entity
QVector<QUuid> result;
if (_entityTree) {
QVector<EntityItemPointer> entities;
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->withReadLock([&] {
_entityTree->findEntities(center, radius, entities);
_entityTree->evalEntitiesInSphereWithType(center, radius, type, PickFilter(searchFilter), result);
});
foreach(EntityItemPointer entity, entities) {
if (entity->getType() == type) {
result << entity->getEntityItemID().toString();
}
}
}
return result;
}
QVector<QUuid> EntityScriptingInterface::findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch) const {
QVector<QUuid> result;
if (_entityTree) {
QVector<EntityItemPointer> entities;
_entityTree->withReadLock([&] {
_entityTree->findEntities(center, radius, entities);
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
_entityTree->evalEntitiesInSphereWithName(center, radius, entityName, caseSensitiveSearch, PickFilter(searchFilter), result);
});
if (caseSensitiveSearch) {
foreach(EntityItemPointer entity, entities) {
if (entity->getName() == entityName) {
result << entity->getEntityItemID();
}
}
} else {
QString entityNameLowerCase = entityName.toLower();
foreach(EntityItemPointer entity, entities) {
QString entityItemLowerCase = entity->getName().toLower();
if (entityItemLowerCase == entityNameLowerCase) {
result << entity->getEntityItemID();
}
}
}
}
return result;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const {
PROFILE_RANGE(script_entities, __FUNCTION__);
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
return findRayIntersectionVector(ray, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly);
unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES);
if (!precisionPicking) {
searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COARSE);
}
if (visibleOnly) {
searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE);
}
if (collidableOnly) {
searchFilter = searchFilter | PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE);
}
return evalRayIntersectionWorker(ray, Octree::Lock, PickFilter(searchFilter), entitiesToInclude, entitiesToDiscard);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) {
PROFILE_RANGE(script_entities, __FUNCTION__);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly);
return evalRayIntersectionWorker(ray, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard);
}
// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed
// findRayIntersection() to be blocking because it never makes sense for a script to get back a non-answer
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
qWarning() << "Entities.findRayIntersectionBlocking() is obsolete, use Entities.findRayIntersection() instead.";
const QVector<EntityItemID>& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
const QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType, bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult EntityScriptingInterface::evalRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType, PickFilter searchFilter, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard) const {
RayToEntityIntersectionResult result;
if (_entityTree) {
OctreeElementPointer element;
result.entityID = _entityTree->findRayIntersection(ray.origin, ray.direction,
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
result.entityID = _entityTree->evalRayIntersection(ray.origin, ray.direction,
entityIdsToInclude, entityIdsToDiscard, searchFilter,
element, result.distance, result.face, result.surfaceNormal,
result.extraInfo, lockType, &result.accurate);
result.intersects = !result.entityID.isNull();
@ -1279,23 +1242,22 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
return result;
}
ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) {
PROFILE_RANGE(script_entities, __FUNCTION__);
return findParabolaIntersectionWorker(parabola, Octree::Lock, precisionPicking, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly);
return evalParabolaIntersectionWorker(parabola, Octree::Lock, searchFilter, entityIdsToInclude, entityIdsToDiscard);
}
ParabolaToEntityIntersectionResult EntityScriptingInterface::findParabolaIntersectionWorker(const PickParabola& parabola,
Octree::lockType lockType, bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
ParabolaToEntityIntersectionResult EntityScriptingInterface::evalParabolaIntersectionWorker(const PickParabola& parabola,
Octree::lockType lockType, PickFilter searchFilter, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard) const {
ParabolaToEntityIntersectionResult result;
if (_entityTree) {
OctreeElementPointer element;
result.entityID = _entityTree->findParabolaIntersection(parabola,
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
result.entityID = _entityTree->evalParabolaIntersection(parabola,
entityIdsToInclude, entityIdsToDiscard, searchFilter,
element, result.intersection, result.distance, result.parabolicDistance, result.face, result.surfaceNormal,
result.extraInfo, lockType, &result.accurate);
result.intersects = !result.entityID.isNull();

View file

@ -24,6 +24,7 @@
#include <OctreeScriptingInterface.h>
#include <RegisteredMetaTypes.h>
#include <PointerEvent.h>
#include <PickFilter.h>
#include "PolyVoxEntityItem.h"
#include "LineEntityItem.h"
@ -56,8 +57,7 @@ private:
};
/**jsdoc
* The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection} or
* {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}.
* The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection}.
* @typedef {object} Entities.RayToEntityIntersectionResult
* @property {boolean} intersects - <code>true</code> if the {@link PickRay} intersected an entity, otherwise
* <code>false</code>.
@ -119,7 +119,6 @@ public:
/// handles scripting of Entity commands from JS passed to assigned clients
class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency {
Q_OBJECT
Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity)
friend EntityPropertyMetadataRequest;
@ -144,10 +143,10 @@ public:
void resetActivityTracking();
ActivityTracking getActivityTracking() const { return _activityTracking; }
// TODO: expose to script?
ParabolaToEntityIntersectionResult findParabolaIntersectionVector(const PickParabola& parabola, bool precisionPicking,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly, bool collidableOnly);
RayToEntityIntersectionResult evalRayIntersectionVector(const PickRay& ray, PickFilter searchFilter,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
ParabolaToEntityIntersectionResult evalParabolaIntersectionVector(const PickParabola& parabola, PickFilter searchFilter,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
/**jsdoc
* Get the properties of multiple entities.
@ -394,9 +393,8 @@ public slots:
Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method,
const QStringList& params = QStringList());
/**jsdoc
* Find the entity with a position closest to a specified point and within a specified radius.
* Find the non-local entity with a position closest to a specified point and within a specified radius.
* @function Entities.findClosestEntity
* @param {Vec3} center - The point about which to search.
* @param {number} radius - The radius within which to search.
@ -410,7 +408,7 @@ public slots:
Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const;
/**jsdoc
* Find all entities that intersect a sphere defined by a center point and radius.
* Find all non-local entities that intersect a sphere defined by a center point and radius.
* @function Entities.findEntities
* @param {Vec3} center - The point about which to search.
* @param {number} radius - The radius within which to search.
@ -424,23 +422,23 @@ public slots:
Q_INVOKABLE QVector<QUuid> findEntities(const glm::vec3& center, float radius) const;
/**jsdoc
* Find all entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner
* Find all non-local entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner
* and dimensions.
* @function Entities.findEntitiesInBox
* @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values.
* @param {Vec3} dimensions - The dimensions of the search AA box.
* @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities
* @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities
* could be found.
*/
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<QUuid> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
/**jsdoc
* Find all entities whose axis-aligned boxes intersect a search frustum.
* Find all non-local entities whose axis-aligned boxes intersect a search frustum.
* @function Entities.findEntitiesInFrustum
* @param {ViewFrustum} frustum - The frustum to search in. The <code>position</code>, <code>orientation</code>,
* <code>projection</code>, and <code>centerRadius</code> properties must be specified.
* @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities
* @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities
* could be found.
* @example <caption>Report the number of entities in view.</caption>
* var entityIDs = Entities.findEntitiesInFrustum(Camera.frustum);
@ -450,12 +448,12 @@ public slots:
Q_INVOKABLE QVector<QUuid> findEntitiesInFrustum(QVariantMap frustum) const;
/**jsdoc
* Find all entities of a particular type that intersect a sphere defined by a center point and radius.
* Find all non-local entities of a particular type that intersect a sphere defined by a center point and radius.
* @function Entities.findEntitiesByType
* @param {Entities.EntityType} entityType - The type of entity to search for.
* @param {Vec3} center - The point about which to search.
* @param {number} radius - The radius within which to search.
* @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if
* @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if
* no entities could be found.
* @example <caption>Report the number of Model entities within 10m of your avatar.</caption>
* var entityIDs = Entities.findEntitiesByType("Model", MyAvatar.position, 10);
@ -465,7 +463,7 @@ public slots:
Q_INVOKABLE QVector<QUuid> findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const;
/**jsdoc
* Find all entities of a particular name that intersect a sphere defined by a center point and radius.
* Find all non-local entities with a particular name that intersect a sphere defined by a center point and radius.
* @function Entities.findEntitiesByName
* @param {string} entityName - The name of the entity to search for.
* @param {Vec3} center - The point about which to search.
@ -475,13 +473,13 @@ public slots:
* if no entities could be found.
* @example <caption>Report the number of entities with the name, "Light-Target".</caption>
* var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false);
* print("Number of entities with the name "Light-Target": " + entityIDs.length);
* print("Number of entities with the name Light-Target: " + entityIDs.length);
*/
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius,
bool caseSensitiveSearch = false ) const;
Q_INVOKABLE QVector<QUuid> findEntitiesByName(const QString entityName, const glm::vec3& center, float radius,
bool caseSensitiveSearch = false) const;
/**jsdoc
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
* Find the first non-local entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
* intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable}
* and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.<br />
* @function Entities.findRayIntersection
@ -512,33 +510,8 @@ public slots:
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
/// will be false.
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false,
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(),
bool visibleOnly = false, bool collidableOnly = false);
/// Same as above but with QVectors
RayToEntityIntersectionResult findRayIntersectionVector(const PickRay& ray, bool precisionPicking,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly, bool collidableOnly);
/**jsdoc
* Find the first entity intersected by a {@link PickRay}. <code>Light</code> and <code>Zone</code> entities are not
* intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable}
* and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.<br />
* This is a synonym for {@link Entities.findRayIntersection|findRayIntersection}.
* @function Entities.findRayIntersectionBlocking
* @param {PickRay} pickRay - The PickRay to use for finding entities.
* @param {boolean} [precisionPicking=false] - If <code>true</code> and the intersected entity is a <code>Model</code>
* entity, the result's <code>extraInfo</code> property includes more information than it otherwise would.
* @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities.
* @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search.
* @deprecated This function is deprecated and will soon be removed. Use
* {@link Entities.findRayIntersection|findRayIntersection} instead; it blocks and performs the same function.
*/
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
/// order to return an accurate result
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false,
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(),
bool visibleOnly = false, bool collidableOnly = false) const;
/**jsdoc
* Reloads an entity's server entity script such that the latest version re-downloaded.
@ -603,9 +576,7 @@ public slots:
/**jsdoc
* Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. By default, Light
* entities are not intersected. The setting lasts for the Interface session. Ray picks are done using
* {@link Entities.findRayIntersection|findRayIntersection} or
* {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick}
* APIs.
* {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
* @function Entities.setLightsArePickable
* @param {boolean} value - Set <code>true</code> to make ray picks intersect the bounding box of
* {@link Entities.EntityType|Light} entities, otherwise <code>false</code>.
@ -615,9 +586,7 @@ public slots:
/**jsdoc
* Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. Ray picks are
* done using {@link Entities.findRayIntersection|findRayIntersection} or
* {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick}
* APIs.
* done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
* @function Entities.getLightsArePickable
* @returns {boolean} <code>true</code> if ray picks intersect the bounding box of {@link Entities.EntityType|Light}
* entities, otherwise <code>false</code>.
@ -628,9 +597,7 @@ public slots:
/**jsdoc
* Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. By default, Light
* entities are not intersected. The setting lasts for the Interface session. Ray picks are done using
* {@link Entities.findRayIntersection|findRayIntersection} or
* {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick}
* APIs.
* {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
* @function Entities.setZonesArePickable
* @param {boolean} value - Set <code>true</code> to make ray picks intersect the bounding box of
* {@link Entities.EntityType|Zone} entities, otherwise <code>false</code>.
@ -640,9 +607,7 @@ public slots:
/**jsdoc
* Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. Ray picks are
* done using {@link Entities.findRayIntersection|findRayIntersection} or
* {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick}
* APIs.
* done using {@link Entities.findRayIntersection|findRayIntersection}, or the {@link Picks} API.
* @function Entities.getZonesArePickable
* @returns {boolean} <code>true</code> if ray picks intersect the bounding box of {@link Entities.EntityType|Zone}
* entities, otherwise <code>false</code>.
@ -1987,14 +1952,12 @@ private:
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly = false, bool collidableOnly = false);
RayToEntityIntersectionResult evalRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
PickFilter searchFilter, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) const;
/// actually does the work of finding the parabola intersection, can be called in locking mode or tryLock mode
ParabolaToEntityIntersectionResult findParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly = false, bool collidableOnly = false);
ParabolaToEntityIntersectionResult evalParabolaIntersectionWorker(const PickParabola& parabola, Octree::lockType lockType,
PickFilter searchFilter, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) const;
EntityTreePointer _entityTree;

View file

@ -43,50 +43,6 @@
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour
// combines the ray cast arguments into a single object
class RayArgs {
public:
// Inputs
glm::vec3 origin;
glm::vec3 direction;
glm::vec3 invDirection;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
bool visibleOnly;
bool collidableOnly;
bool precisionPicking;
// Outputs
OctreeElementPointer& element;
float& distance;
BoxFace& face;
glm::vec3& surfaceNormal;
QVariantMap& extraInfo;
EntityItemID entityID;
};
class ParabolaArgs {
public:
// Inputs
glm::vec3 origin;
glm::vec3 velocity;
glm::vec3 acceleration;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
bool visibleOnly;
bool collidableOnly;
bool precisionPicking;
// Outputs
OctreeElementPointer& element;
float& parabolicDistance;
BoxFace& face;
glm::vec3& surfaceNormal;
QVariantMap& extraInfo;
EntityItemID entityID;
};
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage)
{
@ -778,59 +734,32 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
}
}
class FindNearPointArgs {
class RayArgs {
public:
glm::vec3 position;
float targetRadius;
bool found;
EntityItemPointer closestEntity;
float closestEntityDistance;
// Inputs
glm::vec3 origin;
glm::vec3 direction;
glm::vec3 invDirection;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
PickFilter searchFilter;
// Outputs
OctreeElementPointer& element;
float& distance;
BoxFace& face;
glm::vec3& surfaceNormal;
QVariantMap& extraInfo;
EntityItemID entityID;
};
bool EntityTree::findNearPointOperation(const OctreeElementPointer& element, void* extraData) {
FindNearPointArgs* args = static_cast<FindNearPointArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
glm::vec3 penetration;
bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
// If this entityTreeElement contains the point, then search it...
if (sphereIntersection) {
EntityItemPointer thisClosestEntity = entityTreeElement->getClosestEntity(args->position);
// we may have gotten NULL back, meaning no entity was available
if (thisClosestEntity) {
glm::vec3 entityPosition = thisClosestEntity->getWorldPosition();
float distanceFromPointToEntity = glm::distance(entityPosition, args->position);
// If we're within our target radius
if (distanceFromPointToEntity <= args->targetRadius) {
// we are closer than anything else we've found
if (distanceFromPointToEntity < args->closestEntityDistance) {
args->closestEntity = thisClosestEntity;
args->closestEntityDistance = distanceFromPointToEntity;
args->found = true;
}
}
}
// we should be able to optimize this...
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of its children can contain the point, so stop searching
return false;
}
bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) {
bool evalRayIntersectionOp(const OctreeElementPointer& element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
bool keepSearching = true;
EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast<EntityTreeElement>(element);
EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction,
EntityItemID entityID = entityTreeElementPointer->evalRayIntersection(args->origin, args->direction,
args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude,
args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking);
args->entityIdsToDiscard, args->searchFilter, args->extraInfo);
if (!entityID.isNull()) {
args->entityID = entityID;
// We recurse OctreeElements in order, so if we hit something, we can stop immediately
@ -839,7 +768,7 @@ bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData)
return keepSearching;
}
float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) {
float evalRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast<EntityTreeElement>(element);
float distance = FLT_MAX;
@ -860,19 +789,18 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex
return distance;
}
EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
EntityItemID EntityTree::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
OctreeElementPointer& element, float& distance,
PickFilter searchFilter, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
Octree::lockType lockType, bool* accurateResult) {
RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard,
visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() };
searchFilter, element, distance, face, surfaceNormal, extraInfo, EntityItemID() };
distance = FLT_MAX;
bool requireLock = lockType == Octree::Lock;
bool lockResult = withReadLock([&]{
recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args);
recurseTreeWithOperationSorted(evalRayIntersectionOp, evalRayIntersectionSortingOp, &args);
}, requireLock);
if (accurateResult) {
@ -882,13 +810,32 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm:
return args.entityID;
}
bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) {
class ParabolaArgs {
public:
// Inputs
glm::vec3 origin;
glm::vec3 velocity;
glm::vec3 acceleration;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
PickFilter searchFilter;
// Outputs
OctreeElementPointer& element;
float& parabolicDistance;
BoxFace& face;
glm::vec3& surfaceNormal;
QVariantMap& extraInfo;
EntityItemID entityID;
};
bool evalParabolaIntersectionOp(const OctreeElementPointer& element, void* extraData) {
ParabolaArgs* args = static_cast<ParabolaArgs*>(extraData);
bool keepSearching = true;
EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast<EntityTreeElement>(element);
EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration,
EntityItemID entityID = entityTreeElementPointer->evalParabolaIntersection(args->origin, args->velocity, args->acceleration,
args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude,
args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking);
args->entityIdsToDiscard, args->searchFilter, args->extraInfo);
if (!entityID.isNull()) {
args->entityID = entityID;
// We recurse OctreeElements in order, so if we hit something, we can stop immediately
@ -897,7 +844,7 @@ bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extra
return keepSearching;
}
float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) {
float evalParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) {
ParabolaArgs* args = static_cast<ParabolaArgs*>(extraData);
EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast<EntityTreeElement>(element);
float distance = FLT_MAX;
@ -918,20 +865,20 @@ float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, voi
return distance;
}
EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola,
EntityItemID EntityTree::evalParabolaIntersection(const PickParabola& parabola,
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
PickFilter searchFilter,
OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance,
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
Octree::lockType lockType, bool* accurateResult) {
ParabolaArgs args = { parabola.origin, parabola.velocity, parabola.acceleration, entityIdsToInclude, entityIdsToDiscard,
visibleOnly, collidableOnly, precisionPicking, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() };
searchFilter, element, parabolicDistance, face, surfaceNormal, extraInfo, EntityItemID() };
parabolicDistance = FLT_MAX;
distance = FLT_MAX;
bool requireLock = lockType == Octree::Lock;
bool lockResult = withReadLock([&] {
recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args);
recurseTreeWithOperationSorted(evalParabolaIntersectionOp, evalParabolaIntersectionSortingOp, &args);
}, requireLock);
if (accurateResult) {
@ -946,33 +893,80 @@ EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola,
return args.entityID;
}
EntityItemPointer EntityTree::findClosestEntity(const glm::vec3& position, float targetRadius) {
FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX };
withReadLock([&] {
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findNearPointOperation, &args);
});
return args.closestEntity;
}
class FindAllNearPointArgs {
class FindClosestEntityArgs {
public:
// Inputs
glm::vec3 position;
float targetRadius;
QVector<EntityItemPointer> entities;
PickFilter searchFilter;
// Outputs
QUuid closestEntity;
float closestEntityDistance;
};
bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void* extraData) {
FindAllNearPointArgs* args = static_cast<FindAllNearPointArgs*>(extraData);
bool evalClosestEntityOperation(const OctreeElementPointer& element, void* extraData) {
FindClosestEntityArgs* args = static_cast<FindClosestEntityArgs*>(extraData);
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
glm::vec3 penetration;
bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
// If this entityTreeElement contains the point, then search it...
if (sphereIntersection) {
float closestDistanceSquared = FLT_MAX;
QUuid thisClosestEntity = entityTreeElement->evalClosetEntity(args->position, args->searchFilter, closestDistanceSquared);
// we may have gotten NULL back, meaning no entity was available
if (!thisClosestEntity.isNull()) {
float distanceFromPointToEntity = glm::sqrt(closestDistanceSquared);
// If we're within our target radius
if (distanceFromPointToEntity <= args->targetRadius) {
// we are closer than anything else we've found
if (distanceFromPointToEntity < args->closestEntityDistance) {
args->closestEntity = thisClosestEntity;
args->closestEntityDistance = distanceFromPointToEntity;
}
}
}
// we should be able to optimize this...
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of its children can contain the point, so stop searching
return false;
}
// NOTE: assumes caller has handled locking
QUuid EntityTree::evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter) {
FindClosestEntityArgs args = { position, targetRadius, searchFilter, QUuid(), FLT_MAX };
recurseTreeWithOperation(evalClosestEntityOperation, &args);
return args.closestEntity;
}
class FindEntitiesInSphereArgs {
public:
// Inputs
glm::vec3 position;
float targetRadius;
PickFilter searchFilter;
// Outputs
QVector<QUuid> entities;
};
bool evalInSphereOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInSphereArgs* args = static_cast<FindEntitiesInSphereArgs*>(extraData);
glm::vec3 penetration;
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
// If this element contains the point, then search it...
if (sphereIntersection) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(args->position, args->targetRadius, args->entities);
entityTreeElement->evalEntitiesInSphere(args->position, args->targetRadius, args->searchFilter, args->entities);
return true; // keep searching in case children have closer entities
}
@ -981,102 +975,166 @@ bool EntityTree::findInSphereOperation(const OctreeElementPointer& element, void
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const glm::vec3& center, float radius, QVector<EntityItemPointer>& foundEntities) {
FindAllNearPointArgs args = { center, radius, QVector<EntityItemPointer>() };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInSphereOperation, &args);
void EntityTree::evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInSphereArgs args = { center, radius, searchFilter, QVector<QUuid>() };
recurseTreeWithOperation(evalInSphereOperation, &args);
foundEntities.swap(args.entities);
}
// swap the two lists of entity pointers instead of copy
class FindEntitiesInSphereWithTypeArgs {
public:
// Inputs
glm::vec3 position;
float targetRadius;
EntityTypes::EntityType type;
PickFilter searchFilter;
// Outputs
QVector<QUuid> entities;
};
bool evalInSphereWithTypeOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInSphereWithTypeArgs* args = static_cast<FindEntitiesInSphereWithTypeArgs*>(extraData);
glm::vec3 penetration;
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
// If this element contains the point, then search it...
if (sphereIntersection) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->evalEntitiesInSphereWithType(args->position, args->targetRadius, args->type, args->searchFilter, args->entities);
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInSphereWithTypeArgs args = { center, radius, type, searchFilter, QVector<QUuid>() };
recurseTreeWithOperation(evalInSphereWithTypeOperation, &args);
foundEntities.swap(args.entities);
}
class FindEntitiesInSphereWithNameArgs {
public:
// Inputs
glm::vec3 position;
float targetRadius;
QString name;
bool caseSensitive;
PickFilter searchFilter;
// Outputs
QVector<QUuid> entities;
};
bool evalInSphereWithNameOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInSphereWithNameArgs* args = static_cast<FindEntitiesInSphereWithNameArgs*>(extraData);
glm::vec3 penetration;
bool sphereIntersection = element->getAACube().findSpherePenetration(args->position, args->targetRadius, penetration);
// If this element contains the point, then search it...
if (sphereIntersection) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->evalEntitiesInSphereWithName(args->position, args->targetRadius, args->name, args->caseSensitive, args->searchFilter, args->entities);
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInSphereWithNameArgs args = { center, radius, name, caseSensitive, searchFilter, QVector<QUuid>() };
recurseTreeWithOperation(evalInSphereWithNameOperation, &args);
foundEntities.swap(args.entities);
}
class FindEntitiesInCubeArgs {
public:
FindEntitiesInCubeArgs(const AACube& cube)
: _cube(cube), _foundEntities() {
}
// Inputs
AACube cube;
PickFilter searchFilter;
AACube _cube;
QVector<EntityItemPointer> _foundEntities;
// Outputs
QVector<QUuid> entities;
};
bool EntityTree::findInCubeOperation(const OctreeElementPointer& element, void* extraData) {
bool findInCubeOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInCubeArgs* args = static_cast<FindEntitiesInCubeArgs*>(extraData);
if (element->getAACube().touches(args->_cube)) {
if (element->getAACube().touches(args->cube)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(args->_cube, args->_foundEntities);
entityTreeElement->evalEntitiesInCube(args->cube, args->searchFilter, args->entities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities) {
FindEntitiesInCubeArgs args(cube);
// NOTE: This should use recursion, since this is a spatial operation
void EntityTree::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInCubeArgs args { cube, searchFilter, QVector<QUuid>() };
recurseTreeWithOperation(findInCubeOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args._foundEntities);
foundEntities.swap(args.entities);
}
class FindEntitiesInBoxArgs {
public:
FindEntitiesInBoxArgs(const AABox& box)
: _box(box), _foundEntities() {
}
// Inputs
AABox box;
PickFilter searchFilter;
AABox _box;
QVector<EntityItemPointer> _foundEntities;
// Outputs
QVector<QUuid> entities;
};
bool EntityTree::findInBoxOperation(const OctreeElementPointer& element, void* extraData) {
bool findInBoxOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
if (element->getAACube().touches(args->_box)) {
if (element->getAACube().touches(args->box)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(args->_box, args->_foundEntities);
entityTreeElement->evalEntitiesInBox(args->box, args->searchFilter, args->entities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities) {
FindEntitiesInBoxArgs args(box);
void EntityTree::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInBoxArgs args { box, searchFilter, QVector<QUuid>() };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInBoxOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args._foundEntities);
}
class FindInFrustumArgs {
public:
ViewFrustum frustum;
QVector<EntityItemPointer> entities;
};
bool EntityTree::findInFrustumOperation(const OctreeElementPointer& element, void* extraData) {
FindInFrustumArgs* args = static_cast<FindInFrustumArgs*>(extraData);
if (element->isInView(args->frustum)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(args->frustum, args->entities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities) {
FindInFrustumArgs args = { frustum, QVector<EntityItemPointer>() };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInFrustumOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args.entities);
}
class FindEntitiesInFrustumArgs {
public:
// Inputs
ViewFrustum frustum;
PickFilter searchFilter;
// Outputs
QVector<QUuid> entities;
};
bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData) {
FindEntitiesInFrustumArgs* args = static_cast<FindEntitiesInFrustumArgs*>(extraData);
if (element->isInView(args->frustum)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->evalEntitiesInFrustum(args->frustum, args->searchFilter, args->entities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(RecurseOctreeOperation& elementFilter,
QVector<EntityItemPointer>& foundEntities) {
recurseTreeWithOperation(elementFilter, nullptr);
void EntityTree::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector<QUuid>& foundEntities) {
FindEntitiesInFrustumArgs args = { frustum, searchFilter, QVector<QUuid>() };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInFrustumOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args.entities);
}
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) const {

View file

@ -45,7 +45,6 @@ public:
QHash<EntityItemID, EntityItemID>* map;
};
class EntityTree : public Octree, public SpatialParentTree {
Q_OBJECT
public:
@ -92,18 +91,16 @@ public:
virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override;
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override;
virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
OctreeElementPointer& element, float& distance,
PickFilter searchFilter, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
virtual EntityItemID findParabolaIntersection(const PickParabola& parabola,
virtual EntityItemID evalParabolaIntersection(const PickParabola& parabola,
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
OctreeElementPointer& element, glm::vec3& intersection, float& distance, float& parabolicDistance,
BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
PickFilter searchFilter, OctreeElementPointer& element, glm::vec3& intersection,
float& distance, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
virtual bool rootElementHasData() const override { return true; }
@ -128,44 +125,19 @@ public:
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true);
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = true);
/// \param position point of query in world-frame (meters)
/// \param targetRadius radius of query (meters)
EntityItemPointer findClosestEntity(const glm::vec3& position, float targetRadius);
EntityItemPointer findEntityByID(const QUuid& id) const;
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID) const;
virtual SpatiallyNestablePointer findByID(const QUuid& id) const override { return findEntityByID(id); }
EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID
/// finds all entities that touch a sphere
/// \param center the center of the sphere in world-frame (meters)
/// \param radius the radius of the sphere in world-frame (meters)
/// \param foundEntities[out] vector of EntityItemPointer
/// \remark Side effect: any initial contents in foundEntities will be lost
void findEntities(const glm::vec3& center, float radius, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that touch a cube
/// \param cube the query cube in world-frame (meters)
/// \param foundEntities[out] vector of non-EntityItemPointer
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that touch a box
/// \param box the query box in world-frame (meters)
/// \param foundEntities[out] vector of non-EntityItemPointer
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities);
/// finds all entities within a frustum
/// \parameter frustum the query frustum
/// \param foundEntities[out] vector of EntityItemPointer
void findEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that match scanOperator
/// \parameter scanOperator function that scans entities that match criteria
/// \parameter foundEntities[out] vector of EntityItemPointer
void findEntities(RecurseOctreeOperation& scanOperator, QVector<EntityItemPointer>& foundEntities);
QUuid evalClosestEntity(const glm::vec3& position, float targetRadius, PickFilter searchFilter);
void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector<QUuid>& foundEntities);
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
@ -322,11 +294,6 @@ protected:
void processRemovedEntities(const DeleteEntityOperator& theOperator);
bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties,
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
static bool findNearPointOperation(const OctreeElementPointer& element, void* extraData);
static bool findInSphereOperation(const OctreeElementPointer& element, void* extraData);
static bool findInCubeOperation(const OctreeElementPointer& element, void* extraData);
static bool findInBoxOperation(const OctreeElementPointer& element, void* extraData);
static bool findInFrustumOperation(const OctreeElementPointer& element, void* extraData);
static bool sendEntitiesOperation(const OctreeElementPointer& element, void* extraData);
static void bumpTimestamp(EntityItemProperties& properties);

View file

@ -139,10 +139,23 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
return false;
}
EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) {
bool checkFilterSettings(const EntityItemPointer& entity, PickFilter searchFilter) {
bool visible = entity->isVisible();
bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE);
if ((!searchFilter.doesPickVisible() && visible) || (!searchFilter.doesPickInvisible() && !visible) ||
(!searchFilter.doesPickCollidable() && collidable) || (!searchFilter.doesPickNonCollidable() && !collidable) ||
(!searchFilter.doesPickDomainEntities() && entity->isDomainEntity()) ||
(!searchFilter.doesPickAvatarEntities() && entity->isAvatarEntity()) ||
(!searchFilter.doesPickLocalEntities() && entity->isLocalEntity())) {
return false;
}
return true;
}
EntityItemID EntityTreeElement::evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
PickFilter searchFilter, QVariantMap& extraInfo) {
EntityItemID result;
BoxFace localFace { UNKNOWN_FACE };
@ -154,9 +167,8 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con
QVariantMap localExtraInfo;
float distanceToElementDetails = distance;
EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails,
localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly,
localExtraInfo, precisionPicking);
EntityItemID entityID = evalDetailedRayIntersection(origin, direction, element, distanceToElementDetails,
localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo);
if (!entityID.isNull() && distanceToElementDetails < distance) {
distance = distanceToElementDetails;
face = localFace;
@ -167,13 +179,12 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con
return result;
}
EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard,
bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) {
PickFilter searchFilter, QVariantMap& extraInfo) {
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
int entityNumber = 0;
EntityItemID entityID;
forEachEntity([&](EntityItemPointer entity) {
// use simple line-sphere for broadphase check
@ -187,11 +198,9 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
return;
}
// check RayPick filter settings
if ((visibleOnly && !entity->isVisible())
|| (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE))
|| (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID()))
|| (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
if (!checkFilterSettings(entity, searchFilter) ||
(entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) ||
(entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
return;
}
@ -222,7 +231,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
if (entity->supportsDetailedIntersection()) {
QVariantMap localExtraInfo;
if (entity->findDetailedRayIntersection(origin, direction, element, localDistance,
localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) {
localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
@ -244,7 +253,6 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori
}
}
}
entityNumber++;
});
return entityID;
}
@ -275,11 +283,10 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
return result;
}
EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
EntityItemID EntityTreeElement::evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
QVariantMap& extraInfo, bool precisionPicking) {
const QVector<EntityItemID>& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo) {
EntityItemID result;
BoxFace localFace;
@ -300,9 +307,8 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin
}
// Get the normal of the plane, the cross product of two vectors on the plane
glm::vec3 normal = glm::normalize(glm::cross(vectorOnPlane, acceleration));
EntityItemID entityID = findDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails,
localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly,
localExtraInfo, precisionPicking);
EntityItemID entityID = evalDetailedParabolaIntersection(origin, velocity, acceleration, normal, element, distanceToElementDetails,
localFace, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, searchFilter, localExtraInfo);
if (!entityID.isNull() && distanceToElementDetails < parabolicDistance) {
parabolicDistance = distanceToElementDetails;
face = localFace;
@ -313,13 +319,12 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin
return result;
}
EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
const glm::vec3& normal, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard,
bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) {
PickFilter searchFilter, QVariantMap& extraInfo) {
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
int entityNumber = 0;
EntityItemID entityID;
forEachEntity([&](EntityItemPointer entity) {
// use simple line-sphere for broadphase check
@ -338,11 +343,9 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3
return;
}
// check RayPick filter settings
if ((visibleOnly && !entity->isVisible())
|| (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE))
|| (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID()))
|| (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
if (!checkFilterSettings(entity, searchFilter) ||
(entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) ||
(entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID()))) {
return;
}
@ -374,7 +377,7 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3
if (entity->supportsDetailedIntersection()) {
QVariantMap localExtraInfo;
if (entity->findDetailedParabolaIntersection(origin, velocity, acceleration, element, localDistance,
localFace, localSurfaceNormal, localExtraInfo, precisionPicking)) {
localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) {
if (localDistance < parabolicDistance) {
parabolicDistance = localDistance;
face = localFace;
@ -396,55 +399,56 @@ EntityItemID EntityTreeElement::findDetailedParabolaIntersection(const glm::vec3
}
}
}
entityNumber++;
});
return entityID;
}
EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const {
EntityItemPointer closestEntity = NULL;
float closestEntityDistance = FLT_MAX;
withReadLock([&] {
foreach(EntityItemPointer entity, _entityItems) {
float distanceToEntity = glm::distance2(position, entity->getWorldPosition());
if (distanceToEntity < closestEntityDistance) {
closestEntity = entity;
}
QUuid EntityTreeElement::evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const {
QUuid closestEntity;
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
float distanceToEntity = glm::distance2(position, entity->getWorldPosition());
if (distanceToEntity < closestDistanceSquared) {
closestEntity = entity->getID();
closestDistanceSquared = distanceToEntity;
}
});
return closestEntity;
}
// TODO: change this to use better bounding shape for entity than sphere
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<EntityItemPointer>& foundEntities) const {
void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case
glm::vec3 penetration;
if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
if (success && entityBox.findSpherePenetration(position, radius, penetration)) {
glm::vec3 dimensions = entity->getRaycastDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to
// entities could presumably do actual hull testing if they wanted to
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular
// can we handle the ellipsoid case better? We only currently handle perfect spheres
// with centered registration points
if (entity->getShapeType() == SHAPE_TYPE_SPHERE &&
(dimensions.x == dimensions.y && dimensions.y == dimensions.z)) {
if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) {
// NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the
// maximum bounding sphere, which is actually larger than our actual radius
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(searchPosition, searchRadius,
entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity);
foundEntities.push_back(entity->getID());
}
}
} else {
@ -460,17 +464,134 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f));
if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) {
foundEntities.push_back(entity);
glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f));
if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) {
foundEntities.push_back(entity->getID());
}
}
}
});
}
void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities) {
void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter) || type != entity->getType()) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case
glm::vec3 penetration;
if (success && entityBox.findSpherePenetration(position, radius, penetration)) {
glm::vec3 dimensions = entity->getRaycastDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably do actual hull testing if they wanted to
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular
// can we handle the ellipsoid case better? We only currently handle perfect spheres
// with centered registration points
if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) {
// NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the
// maximum bounding sphere, which is actually larger than our actual radius
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity->getID());
}
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
// we're going to use the registration aware aa box in the entity frame
glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation());
glm::mat4 translation = glm::translate(entity->getWorldPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f));
if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) {
foundEntities.push_back(entity->getID());
}
}
}
});
}
void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
QString entityName = entity->getName();
if ((caseSensitive && name != entityName) || (!caseSensitive && name.toLower() != entityName.toLower())) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case
glm::vec3 penetration;
if (success && entityBox.findSpherePenetration(position, radius, penetration)) {
glm::vec3 dimensions = entity->getRaycastDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably do actual hull testing if they wanted to
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular
// can we handle the ellipsoid case better? We only currently handle perfect spheres
// with centered registration points
if (entity->getShapeType() == SHAPE_TYPE_SPHERE && (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) {
// NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the
// maximum bounding sphere, which is actually larger than our actual radius
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity->getID());
}
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
// we're going to use the registration aware aa box in the entity frame
glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation());
glm::mat4 translation = glm::translate(entity->getWorldPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(position, 1.0f));
if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, radius, penetration)) {
foundEntities.push_back(entity->getID());
}
}
}
});
}
void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better
@ -489,14 +610,18 @@ void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointe
//
// If the entities AABox touches the search cube then consider it to be found
if (!success || entityBox.touches(cube)) {
foundEntities.push_back(entity);
if (success && entityBox.touches(cube)) {
foundEntities.push_back(entity->getID());
}
});
}
void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities) {
void EntityTreeElement::evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better
@ -515,19 +640,24 @@ void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>
//
// If the entities AABox touches the search cube then consider it to be found
if (!success || entityBox.touches(box)) {
foundEntities.push_back(entity);
if (success && entityBox.touches(box)) {
foundEntities.push_back(entity->getID());
}
});
}
void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities) {
void EntityTreeElement::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector<QUuid>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
if (!checkFilterSettings(entity, searchFilter)) {
return;
}
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - See FIXMEs for similar methods above.
if (!success || frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox)) {
foundEntities.push_back(entity);
if (success && (frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox))) {
foundEntities.push_back(entity->getID());
}
});
}

View file

@ -19,7 +19,8 @@
#include "EntityEditPacketSender.h"
#include "EntityItem.h"
#include "EntityTree.h"
#include <PickFilter.h>
class EntityTree;
class EntityTreeElement;
@ -77,7 +78,6 @@ public:
EntityEditPacketSender* packetSender;
};
class EntityTreeElement : public OctreeElement, ReadWriteLockable {
friend class EntityTree; // to allow createElement to new us...
@ -135,28 +135,25 @@ public:
virtual bool deleteApproved() const override { return !hasEntities(); }
virtual bool canPickIntersect() const override { return hasEntities(); }
virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
virtual EntityItemID evalRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false);
virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
PickFilter searchFilter, QVariantMap& extraInfo);
virtual EntityItemID evalDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
QVariantMap& extraInfo, bool precisionPicking);
const QVector<EntityItemID>& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const override;
virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
virtual EntityItemID evalParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
QVariantMap& extraInfo, bool precisionPicking = false);
virtual EntityItemID findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
const QVector<EntityItemID>& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo);
virtual EntityItemID evalDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
const glm::vec3& normal, const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
QVariantMap& extraInfo, bool precisionPicking);
const QVector<EntityItemID>& entityIdsToDiscard, PickFilter searchFilter, QVariantMap& extraInfo);
template <typename F>
void forEachEntity(F f) const {
@ -175,28 +172,13 @@ public:
void addEntityItem(EntityItemPointer entity);
EntityItemPointer getClosestEntity(glm::vec3 position) const;
/// finds all entities that touch a sphere
/// \param position the center of the query sphere
/// \param radius the radius of the query sphere
/// \param entities[out] vector of const EntityItemPointer
void getEntities(const glm::vec3& position, float radius, QVector<EntityItemPointer>& foundEntities) const;
/// finds all entities that touch a box
/// \param box the query box
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that touch a box
/// \param box the query box
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities);
/// finds all entities that touch a frustum
/// \param frustum the query frustum
/// \param entities[out] vector of non-const EntityItemPointer
void getEntities(const ViewFrustum& frustum, QVector<EntityItemPointer>& foundEntities);
QUuid evalClosetEntity(const glm::vec3& position, PickFilter searchFilter, float& closestDistanceSquared) const;
void evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
void evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
void evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector<QUuid>& foundEntities) const;
/// finds all entities that match filter
/// \param filter function that adds matching entities to foundEntities

View file

@ -29,6 +29,7 @@
#include "LineEntityItem.h"
#include "PolyLineEntityItem.h"
#include "PolyVoxEntityItem.h"
#include "GridEntityItem.h"
#include "LightEntityItem.h"
#include "ZoneEntityItem.h"
#include "MaterialEntityItem.h"
@ -52,6 +53,7 @@ REGISTER_ENTITY_TYPE(ParticleEffect)
REGISTER_ENTITY_TYPE(Line)
REGISTER_ENTITY_TYPE(PolyLine)
REGISTER_ENTITY_TYPE(PolyVox)
REGISTER_ENTITY_TYPE(Grid)
REGISTER_ENTITY_TYPE(Light)
REGISTER_ENTITY_TYPE(Zone)
REGISTER_ENTITY_TYPE(Material)

View file

@ -76,6 +76,8 @@ public:
* <td>{@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine}</td></tr>
* <tr><td><code>"PolyVox"</code></td><td>A set of textured voxels.</td>
* <td>{@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}</td></tr>
* <tr><td><code>"Grid"</code></td><td>A grid of lines in a plane.</td>
* <td>{@link Entities.EntityProperties-Grid|EntityProperties-Grid}</td></tr>
* <tr><td><code>"Light"</code></td><td>A local lighting effect.</td>
* <td>{@link Entities.EntityProperties-Light|EntityProperties-Light}</td></tr>
* <tr><td><code>"Zone"</code></td><td>A volume of lighting effects and avatar permissions.</td>
@ -100,6 +102,7 @@ public:
Line,
PolyLine,
PolyVox,
Grid,
Light,
Zone,
Material,

View file

@ -0,0 +1,178 @@
//
// Created by Sam Gondelman on 11/29/18
// 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 "GridEntityItem.h"
#include "EntityItemProperties.h"
const uint32_t GridEntityItem::DEFAULT_MAJOR_GRID_EVERY = 5;
const float GridEntityItem::DEFAULT_MINOR_GRID_EVERY = 1.0f;
EntityItemPointer GridEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
Pointer entity(new GridEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
return entity;
}
// our non-pure virtual subclass for now...
GridEntityItem::GridEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
_type = EntityTypes::Grid;
}
void GridEntityItem::setUnscaledDimensions(const glm::vec3& value) {
const float GRID_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
// NOTE: Grid Entities always have a "depth" of 1cm.
EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, GRID_ENTITY_ITEM_FIXED_DEPTH));
}
EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(followCamera, getFollowCamera);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(majorGridEvery, getMajorGridEvery);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(minorGridEvery, getMinorGridEvery);
return properties;
}
bool GridEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(followCamera, setFollowCamera);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(majorGridEvery, setMajorGridEvery);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(minorGridEvery, setMinorGridEvery);
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - getLastEdited();
qCDebug(entities) << "GridEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
}
setLastEdited(properties.getLastEdited());
}
return somethingChanged;
}
int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) {
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
READ_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
READ_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
READ_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, float, setMinorGridEvery);
return bytesRead;
}
EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_COLOR;
requestedProperties += PROP_ALPHA;
requestedProperties += PROP_GRID_FOLLOW_CAMERA;
requestedProperties += PROP_MAJOR_GRID_EVERY;
requestedProperties += PROP_MINOR_GRID_EVERY;
return requestedProperties;
}
void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, getFollowCamera());
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, getMajorGridEvery());
APPEND_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, getMinorGridEvery());
}
void GridEntityItem::setColor(const glm::u8vec3& color) {
withWriteLock([&] {
_color = color;
});
}
glm::u8vec3 GridEntityItem::getColor() const {
return resultWithReadLock<glm::u8vec3>([&] {
return _color;
});
}
void GridEntityItem::setAlpha(float alpha) {
withWriteLock([&] {
_alpha = alpha;
});
}
float GridEntityItem::getAlpha() const {
return resultWithReadLock<float>([&] {
return _alpha;
});
}
void GridEntityItem::setFollowCamera(bool followCamera) {
withWriteLock([&] {
_followCamera = followCamera;
});
}
bool GridEntityItem::getFollowCamera() const {
return resultWithReadLock<bool>([&] {
return _followCamera;
});
}
void GridEntityItem::setMajorGridEvery(uint32_t majorGridEvery) {
withWriteLock([&] {
const uint32_t MAJOR_GRID_EVERY_MIN = 1;
_majorGridEvery = std::max(majorGridEvery, MAJOR_GRID_EVERY_MIN);
});
}
uint32_t GridEntityItem::getMajorGridEvery() const {
return resultWithReadLock<uint32_t>([&] {
return _majorGridEvery;
});
}
void GridEntityItem::setMinorGridEvery(float minorGridEvery) {
withWriteLock([&] {
const float MINOR_GRID_EVERY_MIN = 0.01f;
_minorGridEvery = std::max(minorGridEvery, MINOR_GRID_EVERY_MIN);
});
}
float GridEntityItem::getMinorGridEvery() const {
return resultWithReadLock<float>([&] {
return _minorGridEvery;
});
}

View file

@ -0,0 +1,71 @@
//
// Created by Sam Gondelman on 11/29/18
// 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_GridEntityItem_h
#define hifi_GridEntityItem_h
#include "EntityItem.h"
class GridEntityItem : public EntityItem {
using Pointer = std::shared_ptr<GridEntityItem>;
public:
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
GridEntityItem(const EntityItemID& entityItemID);
ALLOW_INSTANTIATION // This class can be instantiated
virtual void setUnscaledDimensions(const glm::vec3& value) override;
// methods for getting/setting all properties of an entity
EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
bool setProperties(const EntityItemProperties& properties) override;
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const override;
int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) override;
static const uint32_t DEFAULT_MAJOR_GRID_EVERY;
static const float DEFAULT_MINOR_GRID_EVERY;
void setColor(const glm::u8vec3& color);
glm::u8vec3 getColor() const;
void setAlpha(float alpha);
float getAlpha() const;
void setFollowCamera(bool followCamera);
bool getFollowCamera() const;
void setMajorGridEvery(uint32_t majorGridEvery);
uint32_t getMajorGridEvery() const;
void setMinorGridEvery(float minorGridEvery);
float getMinorGridEvery() const;
protected:
glm::u8vec3 _color;
float _alpha;
bool _followCamera { true };
uint32_t _majorGridEvery { DEFAULT_MAJOR_GRID_EVERY };
float _minorGridEvery { DEFAULT_MINOR_GRID_EVERY };
};
#endif // hifi_GridEntityItem_h

View file

@ -300,10 +300,10 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
} else {
HIFI_FCDEBUG(networking(), "Replicated packet of type" << headerType
<< "received from unknown upstream" << packet.getSenderSockAddr());
return false;
}
} else {
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
return true;
@ -319,7 +319,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
SharedNodePointer matchingNode = nodeWithLocalID(sourceLocalID);
sourceNode = matchingNode.data();
}
QUuid sourceID = sourceNode ? sourceNode->getUUID() : QUuid();
if (!sourceNode &&
@ -1261,6 +1261,10 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) {
}
void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) {
if (!_flagTimeForConnectionStep) {
// this is only true in interface
return;
}
if (connectionStep == ConnectionStep::LookupAddress) {
QWriteLocker writeLock(&_connectionTimeLock);

View file

@ -122,7 +122,7 @@ public:
bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
@ -204,9 +204,9 @@ public:
// This allows multiple threads (i.e. a thread pool) to share a lock
// without deadlocking when a dying node attempts to acquire a write lock
template<typename NestedNodeLambda>
void nestedEach(NestedNodeLambda functor,
int* lockWaitOut = nullptr,
int* nodeTransformOut = nullptr,
void nestedEach(NestedNodeLambda functor,
int* lockWaitOut = nullptr,
int* nodeTransformOut = nullptr,
int* functorOut = nullptr) {
auto start = usecTimestampNow();
{
@ -310,6 +310,9 @@ public:
void setAuthenticatePackets(bool useAuthentication) { _useAuthentication = useAuthentication; }
bool getAuthenticatePackets() const { return _useAuthentication; }
void setFlagTimeForConnectionStep(bool flag) { _flagTimeForConnectionStep = flag; }
bool isFlagTimeForConnectionStep() { return _flagTimeForConnectionStep; }
static void makeSTUNRequestPacket(char* stunRequestPacket);
#if (PR_BUILD || DEV_BUILD)
@ -440,6 +443,7 @@ private:
using LocalIDMapping = tbb::concurrent_unordered_map<Node::LocalID, SharedNodePointer>;
LocalIDMapping _localIDMap;
Node::LocalID _sessionLocalID { 0 };
bool _flagTimeForConnectionStep { false }; // only keep track in interface
};
#endif // hifi_LimitedNodeList_h

View file

@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::ImageEntities);
return static_cast<PacketVersion>(EntityVersion::GridEntities);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
case PacketType::AvatarIdentity:

View file

@ -248,7 +248,8 @@ enum class EntityVersion : PacketVersion {
MaterialRepeat,
EntityHostTypes,
CleanupProperties,
ImageEntities
ImageEntities,
GridEntities
};
enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -7,8 +7,6 @@
//
#include "Pick.h"
const PickFilter PickFilter::NOTHING;
int pickTypeMetaTypeId = qRegisterMetaType<PickQuery::PickType>("PickType");
PickQuery::PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled) :
@ -41,7 +39,8 @@ bool PickQuery::isEnabled() const {
void PickQuery::setPrecisionPicking(bool precisionPicking) {
withWriteLock([&] {
_filter.setFlag(PickFilter::PICK_COARSE, !precisionPicking);
_filter.setFlag(PickFilter::PRECISE, precisionPicking);
_filter.setFlag(PickFilter::COARSE, !precisionPicking);
});
}

View file

@ -18,6 +18,7 @@
#include <shared/ReadWriteLockable.h>
#include <TransformNode.h>
#include <PickFilter.h>
enum IntersectionType {
NONE = 0,
@ -27,84 +28,6 @@ enum IntersectionType {
HUD
};
class PickFilter {
public:
enum FlagBit {
PICK_ENTITIES = 0,
PICK_OVERLAYS,
PICK_AVATARS,
PICK_HUD,
PICK_COARSE, // if not set, does precise intersection, otherwise, doesn't
PICK_INCLUDE_INVISIBLE, // if not set, will not intersect invisible elements, otherwise, intersects both visible and invisible elements
PICK_INCLUDE_NONCOLLIDABLE, // if not set, will not intersect noncollidable elements, otherwise, intersects both collidable and noncollidable elements
// NOT YET IMPLEMENTED
PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections
NUM_FLAGS, // Not a valid flag
};
typedef std::bitset<NUM_FLAGS> Flags;
// The key is the Flags
Flags _flags;
PickFilter() {}
PickFilter(const Flags& flags) : _flags(flags) {}
bool operator== (const PickFilter& rhs) const { return _flags == rhs._flags; }
bool operator!= (const PickFilter& rhs) const { return _flags != rhs._flags; }
void setFlag(FlagBit flag, bool value) { _flags[flag] = value; }
bool doesPickNothing() const { return _flags == NOTHING._flags; }
bool doesPickEntities() const { return _flags[PICK_ENTITIES]; }
bool doesPickOverlays() const { return _flags[PICK_OVERLAYS]; }
bool doesPickAvatars() const { return _flags[PICK_AVATARS]; }
bool doesPickHUD() const { return _flags[PICK_HUD]; }
bool doesPickCoarse() const { return _flags[PICK_COARSE]; }
bool doesPickInvisible() const { return _flags[PICK_INCLUDE_INVISIBLE]; }
bool doesPickNonCollidable() const { return _flags[PICK_INCLUDE_NONCOLLIDABLE]; }
bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; }
// Helpers for RayPickManager
Flags getEntityFlags() const {
unsigned int toReturn = getBitMask(PICK_ENTITIES);
if (doesPickInvisible()) {
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
}
if (doesPickNonCollidable()) {
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
}
if (doesPickCoarse()) {
toReturn |= getBitMask(PICK_COARSE);
}
return Flags(toReturn);
}
Flags getOverlayFlags() const {
unsigned int toReturn = getBitMask(PICK_OVERLAYS);
if (doesPickInvisible()) {
toReturn |= getBitMask(PICK_INCLUDE_INVISIBLE);
}
if (doesPickNonCollidable()) {
toReturn |= getBitMask(PICK_INCLUDE_NONCOLLIDABLE);
}
if (doesPickCoarse()) {
toReturn |= getBitMask(PICK_COARSE);
}
return Flags(toReturn);
}
Flags getAvatarFlags() const { return Flags(getBitMask(PICK_AVATARS)); }
Flags getHUDFlags() const { return Flags(getBitMask(PICK_HUD)); }
static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; }
static const PickFilter NOTHING;
};
class PickResult {
public:
PickResult() {}

View file

@ -85,10 +85,10 @@ QVector4D PickCacheOptimizer<T>::update(std::unordered_map<uint32_t, std::shared
T mathematicalPick = pick->getMathematicalPick();
PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap());
if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
if (!pick->isEnabled() || pick->getMaxDistance() < 0.0f || !mathematicalPick) {
pick->setPickResult(res);
} else {
if (pick->getFilter().doesPickEntities()) {
if (pick->getFilter().doesPickDomainEntities() || pick->getFilter().doesPickAvatarEntities()) {
PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) {
PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick);
@ -99,7 +99,7 @@ QVector4D PickCacheOptimizer<T>::update(std::unordered_map<uint32_t, std::shared
}
}
if (pick->getFilter().doesPickOverlays()) {
if (pick->getFilter().doesPickLocalEntities()) {
PickCacheKey overlayKey = { pick->getFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() };
if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) {
PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick);

View file

@ -944,42 +944,44 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color)
}
void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
int majorRows, int majorCols, float majorEdge,
int minorRows, int minorCols, float minorEdge,
const glm::vec4& color, bool isLayered, int id) {
static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f);
static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f);
bool registered = (id != UNKNOWN_ID);
int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge,
const glm::vec4& color, int id) {
Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);
Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);
Vec2FloatPairPair key(majorKey, minorKey);
// Make the gridbuffer
if (registered && (!_registeredGridBuffers.contains(id) || _lastRegisteredGridBuffer[id] != key)) {
GridSchema gridSchema;
GridBuffer gridBuffer = std::make_shared<gpu::Buffer>(sizeof(GridSchema), (const gpu::Byte*) &gridSchema);
if (registered && _registeredGridBuffers.contains(id)) {
gridBuffer = _registeredGridBuffers[id];
GridBuffer gridBuffer;
if (id != UNKNOWN_ID) {
auto gridBufferIter = _registeredGridBuffers.find(id);
bool hadGridBuffer = gridBufferIter != _registeredGridBuffers.end();
if (hadGridBuffer) {
gridBuffer = gridBufferIter.value();
} else {
GridSchema gridSchema;
gridBuffer = std::make_shared<gpu::Buffer>(sizeof(GridSchema), (const gpu::Byte*)&gridSchema);
}
_registeredGridBuffers[id] = gridBuffer;
_lastRegisteredGridBuffer[id] = key;
if (!hadGridBuffer || _lastRegisteredGridBuffer[id] != key) {
_registeredGridBuffers[id] = gridBuffer;
_lastRegisteredGridBuffer[id] = key;
gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;
gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;
gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;
gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;
gridBuffer.edit<GridSchema>().edge = glm::vec4(glm::vec2(majorEdge),
// If rows or columns are not set, do not draw minor gridlines
glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));
gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;
gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;
gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;
gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;
gridBuffer.edit<GridSchema>().edge = glm::vec4(glm::vec2(majorEdge),
// If rows or columns are not set, do not draw minor gridlines
glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));
}
}
// Set the grid pipeline
useGridPipeline(batch, _registeredGridBuffers[id], isLayered);
useGridPipeline(batch, gridBuffer, color.a < 1.0f);
static const glm::vec2 MIN_TEX_COORD(0.0f, 0.0f);
static const glm::vec2 MAX_TEX_COORD(1.0f, 1.0f);
renderQuad(batch, minCorner, maxCorner, MIN_TEX_COORD, MAX_TEX_COORD, color, id);
}
@ -2115,26 +2117,40 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) {
}
}
void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) {
if (!_gridPipeline) {
auto program = gpu::Shader::createProgram(shader::render_utils::program::grid);
_gridSlot = 0;
auto stateLayered = std::make_shared<gpu::State>();
stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
PrepareStencil::testMask(*stateLayered);
_gridPipelineLayered = gpu::Pipeline::create(program, stateLayered);
auto state = std::make_shared<gpu::State>(stateLayered->getValues());
void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool transparent) {
if (!_gridPipelineOpaque || !_gridPipelineTransparent) {
const float DEPTH_BIAS = 0.001f;
state->setDepthBias(DEPTH_BIAS);
state->setDepthTest(true, false, gpu::LESS_EQUAL);
PrepareStencil::testMaskDrawShape(*state);
_gridPipeline = gpu::Pipeline::create(program, state);
// FIXME: need forward pipelines
{
auto program = gpu::Shader::createProgram(shader::render_utils::program::grid);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMaskDrawShape(*state);
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthBias(DEPTH_BIAS);
_gridPipelineOpaque = gpu::Pipeline::create(program, state);
}
{
auto program = gpu::Shader::createProgram(shader::render_utils::program::grid_translucent);
auto state = std::make_shared<gpu::State>();
state->setDepthTest(true, true, gpu::LESS_EQUAL);
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);
state->setCullMode(gpu::State::CULL_NONE);
state->setDepthBias(DEPTH_BIAS);
_gridPipelineTransparent = gpu::Pipeline::create(program, state);
}
}
gpu::PipelinePointer pipeline = isLayered ? _gridPipelineLayered : _gridPipeline;
batch.setPipeline(pipeline);
batch.setUniformBuffer(_gridSlot, gridBuffer);
batch.setPipeline(transparent ? _gridPipelineTransparent : _gridPipelineOpaque);
batch.setUniformBuffer(0, gridBuffer);
}

View file

@ -270,11 +270,7 @@ public:
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
int majorRows, int majorCols, float majorEdge,
int minorRows, int minorCols, float minorEdge,
const glm::vec4& color, bool isLayered, int id);
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
int rows, int cols, float edge, const glm::vec4& color, bool isLayered, int id) {
renderGrid(batch, minCorner, maxCorner, rows, cols, edge, 0, 0, 0.0f, color, isLayered, id);
}
const glm::vec4& color, int id);
void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id);
@ -414,9 +410,8 @@ private:
};
using GridBuffer = gpu::BufferView;
void useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered);
gpu::PipelinePointer _gridPipeline;
gpu::PipelinePointer _gridPipelineLayered;
int _gridSlot;
gpu::PipelinePointer _gridPipelineOpaque;
gpu::PipelinePointer _gridPipelineTransparent;
class BatchItemDetails {
public:

View file

@ -1,8 +1,6 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
// grid.frag
// fragment shader
//
// Created by Zach Pomerantz on 2/16/2016.
// Copyright 2016 High Fidelity, Inc.
@ -11,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include gpu/ShaderConstants.h@>
<@include gpu/Paint.slh@>
@ -21,26 +21,20 @@ struct Grid {
};
LAYOUT(binding=0) uniform gridBuffer {
Grid grid;
Grid grid;
};
Grid getGrid() { return grid; }
layout(location=GPU_ATTR_TEXCOORD0) in vec2 varTexCoord0;
layout(location=GPU_ATTR_COLOR) in vec4 varColor;
layout(location=0) out vec4 outFragColor;
void main(void) {
Grid grid = getGrid();
float alpha = mix(paintGridMajorMinor(varTexCoord0, grid.offset, grid.period, grid.edge),
paintGrid(varTexCoord0, grid.offset.xy, grid.period.xy, grid.edge.xy),
float(grid.edge.z == 0.0));
if (alpha == 0.0) {
if (alpha < 0.0001) {
discard;
}
outFragColor = vec4(varColor.xyz, varColor.w * alpha);
packDeferredFragmentUnlit(vec3(1.0, 0.0, 0.0), 1.0, varColor.xyz);
}

View file

@ -0,0 +1,41 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gondelman on 12/22/18
// 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 DeferredBufferWrite.slh@>
<@include gpu/ShaderConstants.h@>
<@include gpu/Paint.slh@>
struct Grid {
vec4 period;
vec4 offset;
vec4 edge;
};
LAYOUT(binding=0) uniform gridBuffer {
Grid grid;
};
layout(location=GPU_ATTR_TEXCOORD0) in vec2 varTexCoord0;
layout(location=GPU_ATTR_COLOR) in vec4 varColor;
void main(void) {
float alpha = mix(paintGridMajorMinor(varTexCoord0, grid.offset, grid.period, grid.edge),
paintGrid(varTexCoord0, grid.offset.xy, grid.period.xy, grid.edge.xy),
float(grid.edge.z == 0.0));
alpha *= varColor.w;
if (alpha < 0.0001) {
discard;
}
packDeferredFragmentTranslucent(vec3(1.0, 0.0, 0.0), alpha, varColor.xyz, DEFAULT_ROUGHNESS);
}

View file

@ -0,0 +1 @@
VERTEX standardTransformPNTC

View file

@ -0,0 +1,99 @@
//
// Created by Sam Gondelman on 12/7/18.
// 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_PickFilter_h
#define hifi_PickFilter_h
#include <bitset>
class PickFilter {
public:
enum FlagBit {
DOMAIN_ENTITIES = 0,
AVATAR_ENTITIES,
LOCAL_ENTITIES,
AVATARS,
HUD,
VISIBLE,
INVISIBLE,
COLLIDABLE,
NONCOLLIDABLE,
PRECISE,
COARSE,
// NOT YET IMPLEMENTED
PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections
NUM_FLAGS, // Not a valid flag
};
typedef std::bitset<NUM_FLAGS> Flags;
// The key is the Flags
Flags _flags;
PickFilter() {}
PickFilter(const Flags& flags) : _flags(flags) {}
bool operator==(const PickFilter& rhs) const { return _flags == rhs._flags; }
bool operator!=(const PickFilter& rhs) const { return _flags != rhs._flags; }
void setFlag(FlagBit flag, bool value) { _flags[flag] = value; }
// There are different groups of related flags. If none of the flags in a group are set, the search filter includes them all.
bool doesPickDomainEntities() const { return _flags[DOMAIN_ENTITIES] || !(_flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); }
bool doesPickAvatarEntities() const { return _flags[AVATAR_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS] || _flags[HUD]); }
bool doesPickLocalEntities() const { return _flags[LOCAL_ENTITIES] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[AVATARS] || _flags[HUD]); }
bool doesPickAvatars() const { return _flags[AVATARS] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[HUD]); }
bool doesPickHUD() const { return _flags[HUD] || !(_flags[DOMAIN_ENTITIES] || _flags[AVATAR_ENTITIES] || _flags[LOCAL_ENTITIES] || _flags[AVATARS]); }
bool doesPickVisible() const { return _flags[VISIBLE] || !_flags[INVISIBLE]; }
bool doesPickInvisible() const { return _flags[INVISIBLE] || !_flags[VISIBLE]; }
bool doesPickCollidable() const { return _flags[COLLIDABLE] || !_flags[NONCOLLIDABLE]; }
bool doesPickNonCollidable() const { return _flags[NONCOLLIDABLE] || !_flags[COLLIDABLE]; }
bool isPrecise() const { return _flags[PRECISE] || !_flags[COARSE]; }
bool isCoarse() const { return _flags[COARSE]; }
bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; }
// Helpers for RayPickManager
Flags getEntityFlags() const {
unsigned int toReturn = 0;
for (int i = DOMAIN_ENTITIES; i < LOCAL_ENTITIES; i++) {
if (_flags[i]) {
toReturn |= getBitMask(FlagBit(i));
}
}
for (int i = HUD + 1; i < NUM_FLAGS; i++) {
if (_flags[i]) {
toReturn |= getBitMask(FlagBit(i));
}
}
return Flags(toReturn);
}
Flags getOverlayFlags() const {
unsigned int toReturn = getBitMask(LOCAL_ENTITIES);
for (int i = HUD + 1; i < NUM_FLAGS; i++) {
if (_flags[i]) {
toReturn |= getBitMask(FlagBit(i));
}
}
return Flags(toReturn);
}
Flags getAvatarFlags() const { return Flags(getBitMask(AVATARS)); }
Flags getHUDFlags() const { return Flags(getBitMask(HUD)); }
static constexpr unsigned int getBitMask(FlagBit bit) { return 1 << bit; }
};
#endif // hifi_PickFilter_h

View file

@ -671,8 +671,6 @@ void OpenVrDisplayPlugin::postPreview() {
PoseData nextRender, nextSim;
nextRender.frameIndex = presentCount();
_hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
if (!_threadedSubmit) {
vr::VRCompositor()->WaitGetPoses(nextRender.vrPoses, vr::k_unMaxTrackedDeviceCount, nextSim.vrPoses,
vr::k_unMaxTrackedDeviceCount);
@ -692,7 +690,7 @@ void OpenVrDisplayPlugin::postPreview() {
}
bool OpenVrDisplayPlugin::isHmdMounted() const {
return _hmdActivityLevel == vr::k_EDeviceActivityLevel_UserInteraction;
return isHeadInHeadset();
}
void OpenVrDisplayPlugin::updatePresentPose() {

View file

@ -37,6 +37,7 @@ class OpenVrDisplayPlugin : public HmdDisplayPlugin {
public:
bool isSupported() const override;
const QString getName() const override;
bool getSupportsAutoSwitch() override final { return true; }
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override;
@ -78,7 +79,6 @@ protected:
private:
vr::IVRSystem* _system { nullptr };
std::atomic<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
std::atomic<uint32_t> _keyboardSupressionCount{ 0 };
vr::HmdMatrix34_t _lastGoodHMDPose;

View file

@ -37,6 +37,11 @@ static int refCount { 0 };
static Mutex mutex;
static vr::IVRSystem* activeHmd { nullptr };
static bool _openVrQuitRequested { false };
static bool _headInHeadset { false };
bool isHeadInHeadset() {
return _headInHeadset;
}
bool openVrQuitRequested() {
return _openVrQuitRequested;
@ -272,6 +277,15 @@ void handleOpenVrEvents() {
default:
break;
}
if (event.data.controller.button == vr::k_EButton_ProximitySensor) {
vr::VRControllerState_t controllerState = vr::VRControllerState_t();
if (activeHmd->GetControllerState(vr::k_unTrackedDeviceIndex_Hmd, &controllerState, sizeof(vr::VRControllerState_t))) {
ulong promitySensorFlag = (1UL << ((int)vr::k_EButton_ProximitySensor));
_headInHeadset = (controllerState.ulButtonPressed & promitySensorFlag) == promitySensorFlag;
}
}
#if DEV_BUILD
qDebug() << "OpenVR: Event " << activeHmd->GetEventTypeNameFromEnum((vr::EVREventType)event.eventType) << "(" << event.eventType << ")";
#endif

View file

@ -23,6 +23,7 @@ vr::IVRSystem* acquireOpenVrSystem();
void releaseOpenVrSystem();
void handleOpenVrEvents();
bool openVrQuitRequested();
bool isHeadInHeadset();
void enableOpenVrKeyboard(PluginContainer* container);
void disableOpenVrKeyboard();
bool isOpenVrKeyboardShown();

View file

@ -87,14 +87,6 @@ function makeEntity () {
.withRay(function(){
Entities.findRayIntersection(this.ray, false);
});
entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=true')
.withRay(function(){
Entities.findRayIntersectionBlocking(this.ray, true);
})
entityTests.addTestCase('findRayIntersectionBlocking, precisionPicking=false')
.withRay(function(){
Entities.findRayIntersectionBlocking(this.ray, false);
})
entityTests.addTestCase('no-op')
.run(function(){});

View file

@ -24,6 +24,19 @@ const DELTA_X_MOVE_COLUMNS_THRESHOLD = 2;
const DELTA_X_COLUMN_SWAP_POSITION = 5;
const CERTIFIED_PLACEHOLDER = "** Certified **";
function decimalMegabytes(number) {
return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : "";
}
function displayIfNonZero(number) {
return number ? number : "";
}
function getFilename(url) {
let urlParts = url.split('/');
return urlParts[urlParts.length - 1];
}
const COLUMNS = {
type: {
columnHeader: "Type",
@ -79,6 +92,7 @@ const COLUMNS = {
dropdownLabel: "Texture Size",
propertyID: "texturesSize",
initialWidth: 0.10,
format: decimalMegabytes
},
hasTransparent: {
columnHeader: "&#xe00b;",
@ -605,19 +619,6 @@ function loaded() {
}));
}
function decimalMegabytes(number) {
return number ? (number / BYTES_PER_MEGABYTE).toFixed(1) : "";
}
function displayIfNonZero(number) {
return number ? number : "";
}
function getFilename(url) {
let urlParts = url.split('/');
return urlParts[urlParts.length - 1];
}
function updateEntityData(entityData) {
entities = [];
entitiesByID = {};
@ -639,7 +640,7 @@ function loaded() {
certificateID: entity.certificateID,
verticesCount: displayIfNonZero(entity.verticesCount),
texturesCount: displayIfNonZero(entity.texturesCount),
texturesSize: decimalMegabytes(entity.texturesSize),
texturesSize: entity.texturesSize,
hasTransparent: entity.hasTransparent,
isBaked: entity.isBaked,
drawCalls: displayIfNonZero(entity.drawCalls),
@ -874,7 +875,11 @@ function loaded() {
if (column.data.glyph) {
elCell.innerHTML = itemData[column.data.propertyID] ? column.data.columnHeader : null;
} else {
elCell.innerHTML = itemData[column.data.propertyID];
let value = itemData[column.data.propertyID];
if (column.data.format) {
value = column.data.format(value);
}
elCell.innerHTML = value;
}
elCell.style = "min-width:" + column.widthPx + "px;" + "max-width:" + column.widthPx + "px;";
elCell.className = createColumnClassName(column.columnID);

View file

@ -137,7 +137,7 @@
direction: this.firingDirection
};
this.createGunFireEffect(this.barrelPoint)
var intersection = Entities.findRayIntersectionBlocking(pickRay, true);
var intersection = Entities.findRayIntersection(pickRay, true);
if (intersection.intersects) {
this.createEntityHitEffect(intersection.intersection);
if (Math.random() < this.playRichochetSoundChance) {