From 85f24f0951c6ef3e34549036b7dd09cc355f7073 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 2 Dec 2023 15:59:52 -0800 Subject: [PATCH] entity tags --- libraries/entities/src/EntityItem.cpp | 15 ++++ libraries/entities/src/EntityItem.h | 5 ++ .../entities/src/EntityItemProperties.cpp | 27 +++++++ libraries/entities/src/EntityItemProperties.h | 4 ++ .../entities/src/EntityItemPropertiesMacros.h | 7 ++ libraries/entities/src/EntityPropertyFlags.h | 1 + .../entities/src/EntityScriptingInterface.cpp | 11 +++ .../entities/src/EntityScriptingInterface.h | 19 +++++ libraries/entities/src/EntityTree.cpp | 36 ++++++++++ libraries/entities/src/EntityTree.h | 1 + libraries/entities/src/EntityTreeElement.cpp | 71 +++++++++++++++++++ libraries/entities/src/EntityTreeElement.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 1 + libraries/octree/src/OctreePacketData.cpp | 35 +++++++++ libraries/octree/src/OctreePacketData.h | 4 ++ .../script-engine/src/ScriptValueUtils.cpp | 30 ++++++++ .../script-engine/src/ScriptValueUtils.h | 3 + 17 files changed, 271 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b63f0d91ab..ad4345ba9f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -104,6 +104,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_IGNORE_PICK_INTERSECTION; requestedProperties += PROP_RENDER_WITH_ZONES; requestedProperties += PROP_BILLBOARD_MODE; + requestedProperties += PROP_TAGS; requestedProperties += _grabProperties.getEntityProperties(params); // Physics @@ -301,6 +302,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, getIgnorePickIntersection()); APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, getRenderWithZones()); APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_TAGS, getTags()); withReadLock([&] { _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -874,6 +876,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); READ_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); READ_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY(PROP_TAGS, QSet, setTags); withWriteLock([&] { int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, @@ -1358,6 +1361,7 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignorePickIntersection, getIgnorePickIntersection); COPY_ENTITY_PROPERTY_TO_PROPERTIES(renderWithZones, getRenderWithZones); COPY_ENTITY_PROPERTY_TO_PROPERTIES(billboardMode, getBillboardMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(tags, getTags); withReadLock([&] { _grabProperties.getProperties(properties); }); @@ -1495,6 +1499,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignorePickIntersection, setIgnorePickIntersection); SET_ENTITY_PROPERTY_FROM_PROPERTIES(renderWithZones, setRenderWithZones); SET_ENTITY_PROPERTY_FROM_PROPERTIES(billboardMode, setBillboardMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(tags, setTags); withWriteLock([&] { bool grabPropertiesChanged = _grabProperties.setProperties(properties); somethingChanged |= grabPropertiesChanged; @@ -3553,3 +3558,13 @@ void EntityItem::setBillboardMode(BillboardMode value) { _billboardMode = value; }); } + +void EntityItem::setTags(const QSet& tags) { + withWriteLock([&] { + _tags = tags; + }); +} + +QSet EntityItem::getTags() const { + return resultWithReadLock>([&] { return _tags; }); +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ec84b0ccb2..01a2a7c5bf 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -559,6 +559,9 @@ public: BillboardMode getBillboardMode() const; virtual bool getRotateForPicking() const { return false; } + void setTags(const QSet& tags); + QSet getTags() const; + signals: void spaceUpdate(std::pair data); @@ -740,6 +743,8 @@ protected: bool _cullWithParent { false }; + QSet _tags; + mutable bool _needsRenderUpdate { false }; }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f543169401..205f0ab5f2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -424,6 +424,21 @@ void EntityItemProperties::setEntityHostTypeFromString(const QString& entityHost } } +QVector EntityItemProperties::getTagsAsVector() const { + QVector tags; + for (const QString& tag : _tags) { + tags.push_back(tag); + } + return tags; +} + +void EntityItemProperties::setTagsFromVector(const QVector& tags) { + _tags.clear(); + for (const QString& tag : tags) { + _tags.insert(tag); + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -454,6 +469,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); CHECK_PROPERTY_CHANGE(PROP_RENDER_WITH_ZONES, renderWithZones); CHECK_PROPERTY_CHANGE(PROP_BILLBOARD_MODE, billboardMode); + CHECK_PROPERTY_CHANGE(PROP_TAGS, tags); changedProperties += _grab.getChangedProperties(); // Physics @@ -822,6 +838,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * one of the zones in this list. * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation * property to control which axis is facing you. + * @property {string[]} tags=[] - A set of tags describing this entity. * * @property {Entities.Grab} grab - The entity's grab-related properties. * @@ -1615,6 +1632,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IGNORE_PICK_INTERSECTION, ignorePickIntersection); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RENDER_WITH_ZONES, renderWithZones); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TAGS, tags, getTagsAsVector()); _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); // Physics @@ -2031,6 +2049,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE(ignorePickIntersection, bool, setIgnorePickIntersection); COPY_PROPERTY_FROM_QSCRIPTVALUE(renderWithZones, qVectorQUuid, setRenderWithZones); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(billboardMode, BillboardMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(tags, qVectorQString, setTagsFromVector, getTagsAsVector); _grab.copyFromScriptValue(object, namesSet, _defaultSettings); // Physics @@ -2317,6 +2336,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(ignorePickIntersection); COPY_PROPERTY_IF_CHANGED(renderWithZones); COPY_PROPERTY_IF_CHANGED(billboardMode); + COPY_PROPERTY_IF_CHANGED(tags); _grab.merge(other._grab); // Physics @@ -2605,6 +2625,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool); ADD_PROPERTY_TO_MAP(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector); ADD_PROPERTY_TO_MAP(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode); + ADD_PROPERTY_TO_MAP(PROP_TAGS, Tags, tags, QSet); { // Grab ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); @@ -3087,6 +3108,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, properties.getIgnorePickIntersection()); APPEND_ENTITY_PROPERTY(PROP_RENDER_WITH_ZONES, properties.getRenderWithZones()); APPEND_ENTITY_PROPERTY(PROP_BILLBOARD_MODE, (uint32_t)properties.getBillboardMode()); + APPEND_ENTITY_PROPERTY(PROP_TAGS, properties.getTags()); _staticGrab.setProperties(properties); _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -3567,6 +3589,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_PICK_INTERSECTION, bool, setIgnorePickIntersection); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RENDER_WITH_ZONES, QVector, setRenderWithZones); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BILLBOARD_MODE, BillboardMode, setBillboardMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TAGS, QSet, setTags); properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); // Physics @@ -3959,6 +3982,7 @@ void EntityItemProperties::markAllChanged() { _ignorePickIntersectionChanged = true; _renderWithZonesChanged = true; _billboardModeChanged = true; + _tagsChanged = true; _grab.markAllChanged(); // Physics @@ -4358,6 +4382,9 @@ QList EntityItemProperties::listChangedProperties() { if (billboardModeChanged()) { out += "billboardMode"; } + if (tagsChanged()) { + out += "tags"; + } getGrab().listChangedProperties(out); // Physics diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 283d14c4cc..74cd9c4f15 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -203,6 +203,7 @@ public: DEFINE_PROPERTY(PROP_IGNORE_PICK_INTERSECTION, IgnorePickIntersection, ignorePickIntersection, bool, false); DEFINE_PROPERTY_REF(PROP_RENDER_WITH_ZONES, RenderWithZones, renderWithZones, QVector, QVector()); DEFINE_PROPERTY_REF_ENUM(PROP_BILLBOARD_MODE, BillboardMode, billboardMode, BillboardMode, BillboardMode::NONE); + DEFINE_PROPERTY_REF(PROP_TAGS, Tags, tags, QSet, QSet()); DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); // Physics @@ -498,6 +499,9 @@ protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); + QVector getTagsAsVector() const; + void setTagsFromVector(const QVector& tags); + private: QUuid _id; bool _idSet; diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index d6ee64ba9a..3b6e424663 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -125,6 +125,7 @@ inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorQUuidToScriptValue(e, v); } +inline ScriptValue convertScriptValue(ScriptEngine* e, const QVector& v) { return qVectorQStringToScriptValue(e, v); } inline ScriptValue convertScriptValue(ScriptEngine* e, const QRect& v) { return qRectToScriptValue(e, v); } @@ -223,6 +224,7 @@ typedef QVector qVectorQuat; typedef QVector qVectorBool; typedef QVector qVectorFloat; typedef QVector qVectorQUuid; +typedef QVector qVectorQString; inline float float_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } inline quint64 quint64_convertFromScriptValue(const ScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline quint32 quint32_convertFromScriptValue(const ScriptValue& v, bool& isValid) { @@ -305,6 +307,11 @@ inline qVectorQUuid qVectorQUuid_convertFromScriptValue(const ScriptValue& v, bo return qVectorQUuidFromScriptValue(v); } +inline qVectorQString qVectorQString_convertFromScriptValue(const ScriptValue& v, bool& isValid) { + isValid = true; + return qVectorQStringFromScriptValue(v); +} + inline glm::quat quat_convertFromScriptValue(const ScriptValue& v, bool& isValid) { isValid = false; /// assume it can't be converted ScriptValue x = v.property("x"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index e0b5a04094..555ac0a2de 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -46,6 +46,7 @@ enum EntityPropertyList { PROP_IGNORE_PICK_INTERSECTION, PROP_RENDER_WITH_ZONES, PROP_BILLBOARD_MODE, + PROP_TAGS, // Grab PROP_GRAB_GRABBABLE, PROP_GRAB_KINEMATIC, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2c36b7ddbc..4acf9b8d1b 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1457,6 +1457,17 @@ QVector EntityScriptingInterface::findEntitiesByName(const QString entity return result; } +QVector EntityScriptingInterface::findEntitiesByTags(const QVector entityTags, const glm::vec3& center, float radius, bool caseSensitiveSearch) const { + QVector result; + if (_entityTree) { + _entityTree->withReadLock([&] { + unsigned int searchFilter = PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); + _entityTree->evalEntitiesInSphereWithTags(center, radius, entityTags, caseSensitiveSearch, PickFilter(searchFilter), result); + }); + } + return result; +} + RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const ScriptValue& entityIdsToInclude, const ScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) const { PROFILE_RANGE(script_entities, __FUNCTION__); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 198cac005c..c677bdf0a1 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -789,6 +789,25 @@ public slots: Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch = false) const; + /*@jsdoc + * Finds all domain and avatar entities with particular tags that intersect a sphere. + *

Note: Server entity scripts only find entities that have a server entity script + * running in them or a parent entity. You can apply a dummy script to entities that you want found in a search.

+ * @function Entities.findEntitiesByTags + * @param {string[]} entityTags - The tags of the entity to search for. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @param {boolean} [caseSensitive=false] - true if the search is case-sensitive, false if it is + * case-insensitive. + * @returns {Uuid[]} An array of entity IDs that have the specified name and intersect the search sphere. The array is + * empty if no entities could be found. + * @example Report the number of entities with the tag, "Light-Target". + * var entityIDs = Entities.findEntitiesByTags(["Light-Target"], MyAvatar.position, 10, false); + * print("Number of entities with the tag Light-Target: " + entityIDs.length); + */ + Q_INVOKABLE QVector findEntitiesByTags(const QVector entityTags, const glm::vec3& center, float radius, + bool caseSensitiveSearch = false) const; + /*@jsdoc * Finds the first avatar or domain entity intersected by a {@link PickRay}. Light and Zone * entities are not intersected unless they've been configured as pickable using diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fb7fbe6499..5f88d7191c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1110,6 +1110,42 @@ void EntityTree::evalEntitiesInSphereWithName(const glm::vec3& center, float rad foundEntities.swap(args.entities); } +class FindEntitiesInSphereWithTagsArgs { +public: + // Inputs + glm::vec3 position; + float targetRadius; + QVector tags; + bool caseSensitive; + PickFilter searchFilter; + + // Outputs + QVector entities; +}; + +bool evalInSphereWithTagsOperation(const OctreeElementPointer& element, void* extraData) { + FindEntitiesInSphereWithTagsArgs* args = static_cast(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(element); + entityTreeElement->evalEntitiesInSphereWithTags(args->position, args->targetRadius, args->tags, 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::evalEntitiesInSphereWithTags(const glm::vec3& center, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) { + FindEntitiesInSphereWithTagsArgs args = { center, radius, tags, caseSensitive, searchFilter, QVector() }; + recurseTreeWithOperation(evalInSphereWithTagsOperation, &args); + foundEntities.swap(args.entities); +} + class FindEntitiesInCubeArgs { public: // Inputs diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d5c3a74eb7..1161bec6e9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -138,6 +138,7 @@ public: void evalEntitiesInSphere(const glm::vec3& center, float radius, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInSphereWithType(const glm::vec3& center, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInSphereWithName(const glm::vec3& center, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); + void evalEntitiesInSphereWithTags(const glm::vec3& center, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities); void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index f330058a78..030c3082c2 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -602,6 +602,77 @@ void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position, }); } +void EntityTreeElement::evalEntitiesInSphereWithTags(const glm::vec3& position, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const { + forEachEntity([&](EntityItemPointer entity) { + if (!checkFilterSettings(entity, searchFilter)) { + return; + } + + QSet entityTags = entity->getTags(); + for (const QString& tag : tags) { + if (caseSensitive && !entityTags.contains(tag)) { + return; + } else { + const QString lowerTag = tag.toLower(); + bool found = false; + for (const QString& entityTag : entityTags) { + if (lowerTag == entityTag.toLower()) { + found = true; + } + } + if (!found) { + 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->getScaledDimensions(); + + // 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; + glm::vec3 center = entity->getCenterPosition(success); + + if (success && findSphereSpherePenetration(position, radius, center, entityTrueRadius, penetration)) { + 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 translation = glm::translate(entity->getWorldPosition()); + glm::mat4 rotation = glm::mat4_cast(entity->getWorldOrientation()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot(); + + 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& foundEntities) const { forEachEntity([&](EntityItemPointer entity) { if (!checkFilterSettings(entity, searchFilter)) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index dab56132c9..951481fcb9 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -177,6 +177,7 @@ public: void evalEntitiesInSphere(const glm::vec3& position, float radius, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInSphereWithType(const glm::vec3& position, float radius, EntityTypes::EntityType type, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInSphereWithName(const glm::vec3& position, float radius, const QString& name, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; + void evalEntitiesInSphereWithTags(const glm::vec3& position, float radius, const QVector& tags, bool caseSensitive, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInCube(const AACube& cube, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInBox(const AABox& box, PickFilter searchFilter, QVector& foundEntities) const; void evalEntitiesInFrustum(const ViewFrustum& frustum, PickFilter searchFilter, QVector& foundEntities) const; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3badfdf418..55e183d00b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -290,6 +290,7 @@ enum class EntityVersion : PacketVersion { UserAgent, AllBillboardMode, TextAlignment, + EntityTags, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 3966fcf86f..c13d58226b 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -496,6 +496,24 @@ bool OctreePacketData::appendValue(const QVector& value) { return success; } +bool OctreePacketData::appendValue(const QSet& value) { + QVector valueVector; + for (const QString& valueString : value) { + valueVector.push_back(valueString); + } + + uint16_t qVecSize = value.size(); + bool success = appendValue(qVecSize); + if (success) { + success = append((const unsigned char*)valueVector.constData(), qVecSize * sizeof(QString)); + if (success) { + _bytesOfValues += qVecSize * sizeof(QString); + _totalBytesOfValues += qVecSize * sizeof(QString); + } + } + return success; +} + bool OctreePacketData::appendValue(const glm::quat& value) { const size_t VALUES_PER_QUAT = 4; const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; @@ -802,6 +820,23 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QVecto return sizeof(uint16_t) + length * sizeof(QUuid); } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QSet& result) { + QVector resultVector; + + uint16_t length; + memcpy(&length, dataBytes, sizeof(uint16_t)); + dataBytes += sizeof(length); + resultVector.resize(length); + memcpy(resultVector.data(), dataBytes, length * sizeof(QString)); + + result.clear(); + for (const QString& resultString : resultVector) { + result.insert(resultString); + } + + return sizeof(uint16_t) + length * sizeof(QString); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 583f090942..745551eb22 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -190,6 +190,9 @@ public: /// appends a QVector of QUuids to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); + /// appends a QSet of QStrings to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const QSet& value); + /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::quat& value); @@ -290,6 +293,7 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, QSet& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result); static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QRect& result); diff --git a/libraries/script-engine/src/ScriptValueUtils.cpp b/libraries/script-engine/src/ScriptValueUtils.cpp index 5bada98f15..f5808080ba 100644 --- a/libraries/script-engine/src/ScriptValueUtils.cpp +++ b/libraries/script-engine/src/ScriptValueUtils.cpp @@ -862,6 +862,14 @@ bool quuidFromScriptValue(const ScriptValue& object, QUuid& uuid) { return true; } +ScriptValue qStringToScriptValue(ScriptEngine* engine, const QString& string) { + if (string.isNull()) { + return engine->nullValue(); + } + ScriptValue obj(engine->newValue(string)); + return obj; +} + /*@jsdoc * A 2D size value. * @typedef {object} Size @@ -1029,3 +1037,25 @@ QVector qVectorEntityItemIDFromScriptValue(const ScriptValue& arra } return newVector; } + +ScriptValue qVectorQStringToScriptValue(ScriptEngine* engine, const QVector& vector) { + ScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, qStringToScriptValue(engine, vector.at(i))); + } + return array; +} + +QVector qVectorQStringFromScriptValue(const ScriptValue& array) { + if (!array.isArray()) { + return QVector(); + } + QVector newVector; + int length = array.property("length").toInteger(); + newVector.reserve(length); + for (int i = 0; i < length; i++) { + QString string = array.property(i).toString(); + newVector << string; + } + return newVector; +} \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptValueUtils.h b/libraries/script-engine/src/ScriptValueUtils.h index 45fee61cc4..0a206d1aa5 100644 --- a/libraries/script-engine/src/ScriptValueUtils.h +++ b/libraries/script-engine/src/ScriptValueUtils.h @@ -224,6 +224,9 @@ ScriptValue qVectorQUuidToScriptValue(ScriptEngine* engine, const QVector bool qVectorQUuidFromScriptValue(const ScriptValue& array, QVector& vector); QVector qVectorQUuidFromScriptValue(const ScriptValue& array); +ScriptValue qVectorQStringToScriptValue(ScriptEngine* engine, const QVector& vector); +QVector qVectorQStringFromScriptValue(const ScriptValue& array); + class AACube; ScriptValue aaCubeToScriptValue(ScriptEngine* engine, const AACube& aaCube); bool aaCubeFromScriptValue(const ScriptValue& object, AACube& aaCube);