// // EntityScriptingInterface.h // libraries/entities/src // // Created by Brad Hefta-Gaub on 12/6/13. // Copyright 2013 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 // // TODO: How will we handle collision callbacks with Entities // #ifndef hifi_EntityScriptingInterface_h #define hifi_EntityScriptingInterface_h #include #include #include #include #include #include #include #include #include #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" #include "PolyLineEntityItem.h" #include "EntityTree.h" #include "EntityEditPacketSender.h" #include "EntitiesScriptEngineProvider.h" #include "EntityItemProperties.h" #include "BaseScriptEngine.h" class EntityTree; class MeshProxy; // helper factory to compose standardized, async metadata queries for "magic" Entity properties // like .script and .serverScripts. This is used for automated testing of core scripting features // as well as to provide early adopters a self-discoverable, consistent way to diagnose common // problems with their own Entity scripts. class EntityPropertyMetadataRequest { public: EntityPropertyMetadataRequest(BaseScriptEngine* engine) : _engine(engine) {}; bool script(EntityItemID entityID, QScriptValue handler); bool serverScripts(EntityItemID entityID, QScriptValue handler); private: QPointer _engine; }; /**jsdoc * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection} or * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}. * @typedef {object} Entities.RayToEntityIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected an entity, otherwise * false. * @property {boolean} accurate - Is always true. * @property {Uuid} entityID - The ID if the entity intersected, if any, otherwise null. * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. * @property {Vec3} intersection - The intersection point. * @property {Vec3} surfaceNormal - The surface normal of the entity at the intersection point. * @property {BoxFace} face - The face of the entity's axis-aligned box that the ray intersects. * @property {object} extraInfo - Extra information depending on the entity intersected. Currently, only Model * entities provide extra information, and the information provided depends on the precisionPicking parameter * value that the search function was called with. */ // "accurate" is currently always true because the ray intersection is always performed with an Octree::Lock. class RayToEntityIntersectionResult { public: RayToEntityIntersectionResult(); bool intersects; bool accurate; QUuid entityID; float distance; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; QVariantMap extraInfo; }; Q_DECLARE_METATYPE(RayToEntityIntersectionResult) QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& results); void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results); /**jsdoc * The Entities API provides facilities to create and interact with entities. Entities are 2D and 3D objects that are visible * to everyone and typically are persisted to the domain. For Interface scripts, the entities available are those that * Interface has displayed and so knows about. * * @namespace Entities * * @hifi-interface * @hifi-client-entity * @hifi-server-entity * @hifi-assignment-client * * @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus. * If no entity has keyboard focus, get returns null; set to null or {@link Uuid|Uuid.NULL} to * clear keyboard focus. */ /// 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; public: EntityScriptingInterface(bool bidOnSimulationOwnership); class ActivityTracking { public: int addedEntityCount { 0 }; int deletedEntityCount { 0 }; int editedEntityCount { 0 }; }; EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } virtual NodeType_t getServerNodeType() const override { return NodeType::EntityServer; } virtual OctreeEditPacketSender* createPacketSender() override { return new EntityEditPacketSender(); } void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(QSharedPointer engine); void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } public slots: /**jsdoc * Check whether or not you can change the locked property of entities. Locked entities have their * locked property set to true and cannot be edited or deleted. Whether or not you can change * entities' locked properties is configured in the domain server's permissions. * @function Entities.canAdjustLocks * @returns {boolean} true if the client can change the locked property of entities, * otherwise false. * @example Set an entity's locked property to true if you can. * if (Entities.canAdjustLocks()) { * Entities.editEntity(entityID, { locked: true }); * } else { * Window.alert("You do not have the permissions to set an entity locked!"); * } */ Q_INVOKABLE bool canAdjustLocks(); /**jsdoc * Check whether or not you can rez (create) new entities in the domain. * @function Entities.canRez * @returns {boolean} true if the domain server will allow the script to rez (create) new entities, * otherwise false. */ Q_INVOKABLE bool canRez(); /**jsdoc * Check whether or not you can rez (create) new temporary entities in the domain. Temporary entities are entities with a * finite lifetime property value set. * @function Entities.canRezTmp * @returns {boolean} true if the domain server will allow the script to rez (create) new temporary * entities, otherwise false. */ Q_INVOKABLE bool canRezTmp(); /**jsdoc * Check whether or not you can rez (create) new certified entities in the domain. Certified entities are entities that have * PoP certificates. * @function Entities.canRezCertified * @returns {boolean} true if the domain server will allow the script to rez (create) new certified * entities, otherwise false. */ Q_INVOKABLE bool canRezCertified(); /**jsdoc * Check whether or not you can rez (create) new temporary certified entities in the domain. Temporary entities are entities * with a finite lifetime property value set. Certified entities are entities that have PoP certificates. * @function Entities.canRezTmpCertified * @returns {boolean} true if the domain server will allow the script to rez (create) new temporary * certified entities, otherwise false. */ Q_INVOKABLE bool canRezTmpCertified(); /**jsdoc * Check whether or not you can make changes to the asset server's assets. * @function Entities.canWriteAssets * @returns {boolean} true if the domain server will allow the script to make changes to the asset server's * assets, otherwise false. */ Q_INVOKABLE bool canWriteAssets(); /**jsdoc * Check whether or not you can replace the domain's content set. * @function Entities.canReplaceContent * @returns {boolean} true if the domain server will allow the script to replace the domain's content set, * otherwise false. */ Q_INVOKABLE bool canReplaceContent(); /**jsdoc * Add a new entity with specified properties. * @function Entities.addEntity * @param {Entities.EntityProperties} properties - The properties of the entity to create. * @param {boolean} [clientOnly=false] - If true, or if clientOnly is set true in * the properties, the entity is created as an avatar entity; otherwise it is created on the server. An avatar entity * follows you to each domain you visit, rendering at the same world coordinates unless it's parented to your avatar. * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. * @example Create a box entity in front of your avatar. * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 0.5, y: 0.5, z: 0.5 } * }); * print("Entity created: " + entityID); */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine /// Deliberately not adding jsdoc, only used internally. Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& textures, const QString& shapeType, bool dynamic, bool collisionless, const glm::vec3& position, const glm::vec3& gravity); /**jsdoc * Get the properties of an entity. * @function Entities.getEntityProperties * @param {Uuid} entityID - The ID of the entity to get the properties of. * @param {string[]} [desiredProperties=[]] - Array of the names of the properties to get. If the array is empty, * all properties are returned. * @returns {Entities.EntityProperties} The properties of the entity if the entity can be found, otherwise an empty object. * @example Report the color of a new box entity. * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 0.5, y: 0.5, z: 0.5 } * }); * var properties = Entities.getEntityProperties(entityID, ["color"]); * print("Entity color: " + JSON.stringify(properties.color)); */ Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties); /**jsdoc * Update an entity with specified properties. * @function Entities.editEntity * @param {Uuid} entityID - The ID of the entity to edit. * @param {Entities.EntityProperties} properties - The properties to update the entity with. * @returns {Uuid} The ID of the entity if the edit was successful, otherwise null. * @example Change the color of an entity. * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 0.5, y: 0.5, z: 0.5 } * }); * var properties = Entities.getEntityProperties(entityID, ["color"]); * print("Entity color: " + JSON.stringify(properties.color)); * * Entities.editEntity(entityID, { * color: { red: 255, green: 0, blue: 0 } * }); * properties = Entities.getEntityProperties(entityID, ["color"]); * print("Entity color: " + JSON.stringify(properties.color)); */ Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties); /**jsdoc * Delete an entity. * @function Entities.deleteEntity * @param {Uuid} entityID - The ID of the entity to delete. * @example Delete an entity a few seconds after creating it. * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 0.5, y: 0.5, z: 0.5 } * }); * * Script.setTimeout(function () { * Entities.deleteEntity(entityID); * }, 3000); */ Q_INVOKABLE void deleteEntity(QUuid entityID); /**jsdoc * Call a method in a client entity script from a client script or client entity script, or call a method in a server * entity script from a server entity script. The entity script method must be exposed as a property in the target client * entity script. Additionally, if calling a server entity script, the server entity script must include the method's name * in an exposed property called remotelyCallable that is an array of method names that can be called. * @function Entities.callEntityMethod * @param {Uuid} entityID - The ID of the entity to call the method in. * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc * Call a method in a server entity script from a client script or client entity script. The entity script method must be * exposed as a property in the target server entity script. Additionally, the target server entity script must include the * method's name in an exposed property called remotelyCallable that is an array of method names that can be * called. * @function Entities.callEntityServerMethod * @param {Uuid} entityID - The ID of the entity to call the method in. * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ Q_INVOKABLE void callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc * Call a method in a specific user's client entity script from a server entity script. The entity script method must be * exposed as a property in the target client entity script. * @function Entities.callEntityClientMethod * @param {Uuid} clientSessionID - The session ID of the user to call the method in. * @param {Uuid} entityID - The ID of the entity to call the method in. * @param {string} method - The name of the method to call. * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ 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. * @function Entities.findClosestEntity * @param {Vec3} center - The point about which to search. * @param {number} radius - The radius within which to search. * @returns {Uuid} The ID of the entity that is closest to the center and within the radius if * there is one, otherwise null. * @example Find the closest entity within 10m of your avatar. * var entityID = Entities.findClosestEntity(MyAvatar.position, 10); * print("Closest entity: " + entityID); */ /// this function will not find any models in script engine contexts which don't have access to models 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. * @function Entities.findEntities * @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 that were found that intersect the search sphere. The array is empty if no * entities could be found. * @example Report how many entities are within 10m of your avatar. * var entityIDs = Entities.findEntities(MyAvatar.position, 10); * print("Number of entities within 10m: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector 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 * 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 * could be found. */ /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; /**jsdoc * Find all entities whose axis-aligned boxes intersect a search frustum. * @function Entities.findEntitiesInFrustum * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, * projection, and centerRadius properties must be specified. * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities * could be found. * @example Report the number of entities in view. * var entityIDs = Entities.findEntitiesInFrustum(Camera.frustum); * print("Number of entities in view: " + entityIDs.length); */ /// this function will not find any models in script engine contexts which don't have access to entities Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; /**jsdoc * Find all 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 * no entities could be found. * @example Report the number of Model entities within 10m of your avatar. * var entityIDs = Entities.findEntitiesByType("Model", MyAvatar.position, 10); * print("Number of Model entities within 10m: " + entityIDs.length); */ /// this function will not find any entities in script engine contexts which don't have access to entities Q_INVOKABLE QVector 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. * @function Entities.findEntitiesByName * @param {string} entityName - The name 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] - If true then the search is case-sensitive. * @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 name, "Light-Target". * var entityIDs = Entities.findEntitiesByName("Light-Target", MyAvatar.position, 10, false); * print("Number of entities with the name "Light-Target": " + entityIDs.length); */ Q_INVOKABLE QVector findEntitiesByName(const QString entityName, const glm::vec3& center, float radius, bool caseSensitiveSearch = false ) const; /**jsdoc * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
* @function Entities.findRayIntersection * @param {PickRay} pickRay - The PickRay to use for finding entities. * @param {boolean} [precisionPicking=false] - If true and the intersected entity is a Model * entity, the result's extraInfo 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. * @param {boolean} [visibleOnly=false] - If true then only entities that are * {@link Entities.EntityProperties|visible} are searched. * @param {boolean} [collideableOnly=false] - If true then only entities that are not * {@link Entities.EntityProperties|collisionless} are searched. * @returns {Entities.RayToEntityIntersectionResult} The result of the search for the first intersected entity. * @example Find the entity directly in front of your avatar. * var pickRay = { * origin: MyAvatar.position, * direction: Quat.getFront(MyAvatar.orientation) * }; * * var intersection = Entities.findRayIntersection(pickRay, true); * if (intersection.intersects) { * print("Entity in front of avatar: " + intersection.entityID); * } else { * print("No entity in front of avatar."); * } */ /// If the scripting context has visible entities, this will determine a ray intersection, the results /// 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& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly); /**jsdoc * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
* 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 true and the intersected entity is a Model * entity, the result's extraInfo 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()); /**jsdoc * Reloads an entity's server entity script such that the latest version re-downloaded. * @function Entities.reloadServerScripts * @param {Uuid} entityID - The ID of the entity to reload the server entity script of. * @returns {boolean} true if the reload request was successfully sent to the server, otherwise * false. */ Q_INVOKABLE bool reloadServerScripts(QUuid entityID); /**jsdoc * Gets the status of server entity script attached to an entity * @function Entities.getServerScriptStatus * @param {Uuid} entityID - The ID of the entity to get the server entity script status for. * @param {Entities~getServerScriptStatusCallback} callback - The function to call upon completion. * @returns {boolean} true always. */ /**jsdoc * Called when {@link Entities.getServerScriptStatus} is complete. * @callback Entities~getServerScriptStatusCallback * @param {boolean} success - true if the server entity script status could be obtained, otherwise * false. * @param {boolean} isRunning - true if there is a server entity script running, otherwise false. * @param {string} status - "running" if there is a server entity script running, otherwise an error string. * @param {string} errorInfo - "" if there is a server entity script running, otherwise it may contain extra * information on the error. */ Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); /**jsdoc * Get metadata for certain entity properties such as script and serverScripts. * @function Entities.queryPropertyMetadata * @param {Uuid} entityID - The ID of the entity to get the metadata for. * @param {string} property - The property name to get the metadata for. * @param {Entities~queryPropertyMetadataCallback} callback - The function to call upon completion. * @returns {boolean} true if the request for metadata was successfully sent to the server, otherwise * false. * @throws Throws an error if property is not handled yet or callback is not a function. */ /**jsdoc * Get metadata for certain entity properties such as script and serverScripts. * @function Entities.queryPropertyMetadata * @param {Uuid} entityID - The ID of the entity to get the metadata for. * @param {string} property - The property name to get the metadata for. * @param {object} scope - The "this" context that the callback will be executed within. * @param {Entities~queryPropertyMetadataCallback} callback - The function to call upon completion. * @returns {boolean} true if the request for metadata was successfully sent to the server, otherwise * false. * @throws Throws an error if property is not handled yet or callback is not a function. */ /**jsdoc * Called when {@link Entities.queryPropertyMetadata} is complete. * @callback Entities~queryPropertyMetadataCallback * @param {string} error - undefined if there was no error, otherwise an error message. * @param {object} result - The metadata for the requested entity property if there was no error, otherwise * undefined. */ Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); /**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. * @function Entities.setLightsArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Light} entities, otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE void setLightsArePickable(bool value); /**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. * @function Entities.getLightsArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Light} * entities, otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool getLightsArePickable() const; /**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. * @function Entities.setZonesArePickable * @param {boolean} value - Set true to make ray picks intersect the bounding box of * {@link Entities.EntityType|Zone} entities, otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE void setZonesArePickable(bool value); /**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. * @function Entities.getZonesArePickable * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Zone} * entities, otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool getZonesArePickable() const; /**jsdoc * Set whether or not {@link Entities.EntityType|Zone} entities' boundaries should be drawn. Currently not used. * @function Entities.setDrawZoneBoundaries * @param {boolean} value - Set to true if {@link Entities.EntityType|Zone} entities' boundaries should be * drawn, otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE void setDrawZoneBoundaries(bool value); /**jsdoc * Get whether or not {@link Entities.EntityType|Zone} entities' boundaries should be drawn. Currently not used. * @function Entities.getDrawZoneBoundaries * @returns {boolean} true if {@link Entities.EntityType|Zone} entities' boundaries should be drawn, * otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool getDrawZoneBoundaries() const; /**jsdoc * Set the values of all voxels in a spherical portion of a {@link Entities.EntityType|PolyVox} entity. * @function Entities.setVoxelSphere * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} center - The center of the sphere of voxels to set, in world coordinates. * @param {number} radius - The radius of the sphere of voxels to set, in world coordinates. * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. * @example Create a PolyVox sphere. * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); * var polyVox = Entities.addEntity({ * type: "PolyVox", * position: position, * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 32, y: 32, z: 32 }, * lifetime: 300 // Delete after 5 minutes. * }); * Entities.setVoxelSphere(polyVox, position, 0.9, 255); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); /**jsdoc * Set the values of all voxels in a capsule-shaped portion of a {@link Entities.EntityType|PolyVox} entity. * @function Entities.setVoxelCapsule * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} start - The center of the sphere of voxels to set, in world coordinates. * @param {Vec3} end - The center of the sphere of voxels to set, in world coordinates. * @param {number} radius - The radius of the capsule cylinder and spherical ends, in world coordinates. * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. * @example Create a PolyVox capsule shape. * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); * var polyVox = Entities.addEntity({ * type: "PolyVox", * position: position, * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 32, y: 32, z: 32 }, * lifetime: 300 // Delete after 5 minutes. * }); * var startPosition = Vec3.sum({ x: -0.5, y: 0, z: 0 }, position); * var endPosition = Vec3.sum({ x: 0.5, y: 0, z: 0 }, position); * Entities.setVoxelCapsule(polyVox, startPosition, endPosition, 0.5, 255); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxelCapsule(QUuid entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); /**jsdoc * Set the value of a particular voxels in a {@link Entities.EntityType|PolyVox} entity. * @function Entities.setVoxel * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} position - The position relative to the minimum axes values corner of the entity. The * position coordinates are rounded to the nearest integer to get the voxel coordinate. The minimum axes * corner voxel is { x: 0, y: 0, z: 0 }. * @param {number} value - If value % 256 == 0 then voxel is cleared, otherwise the voxel is set. * @example Create a cube PolyVox entity and clear the minimum axes corner voxel. * var entity = Entities.addEntity({ * type: "PolyVox", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 16, y: 16, z: 16 }, * lifetime: 300 // Delete after 5 minutes. * }); * Entities.setAllVoxels(entity, 1); * Entities.setVoxel(entity, { x: 0, y: 0, z: 0 }, 0); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); /**jsdoc * Set the values of all voxels in a {@link Entities.EntityType|PolyVox} entity. * @function Entities.setAllVoxels * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. * @example Create a PolyVox cube. * var entity = Entities.addEntity({ * type: "PolyVox", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 16, y: 16, z: 16 }, * lifetime: 300 // Delete after 5 minutes. * }); * Entities.setAllVoxels(entity, 1); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); /**jsdoc * Set the values of all voxels in a cubic portion of a {@link Entities.EntityType|PolyVox} entity. * @function Entities.setVoxelsInCuboid * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} lowPosition - The position of the minimum axes value corner of the cube of voxels to set, in voxel * coordinates. * @param {Vec3} cuboidSize - The size of the cube of voxels to set, in voxel coordinates. * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. * @example Create a PolyVox cube and clear the voxels in one corner. * var polyVox = Entities.addEntity({ * type: "PolyVox", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), * rotation: MyAvatar.orientation, * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 16, y: 16, z: 16 }, * lifetime: 300 // Delete after 5 minutes. * }); * Entities.setAllVoxels(polyVox, 1); * var cuboidPosition = { x: 12, y: 12, z: 12 }; * var cuboidSize = { x: 4, y: 4, z: 4 }; * Entities.setVoxelsInCuboid(polyVox, cuboidPosition, cuboidSize, 0); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); /**jsdoc * Convert voxel coordinates in a {@link Entities.EntityType|PolyVox} entity to world coordinates. Voxel coordinates are * relative to the minimum axes values corner of the entity with a scale of Vec3.ONE being the dimensions of * each voxel. * @function Entities.voxelCoordsToWorldCoords * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} voxelCoords - The voxel coordinates. May be fractional and outside the entity's bounding box. * @returns {Vec3} The world coordinates of the voxelCoords if the entityID is a * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. * @example Create a PolyVox cube with the 0,0,0 voxel replaced by a sphere. * // Cube PolyVox with 0,0,0 voxel missing. * var polyVox = Entities.addEntity({ * type: "PolyVox", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 16, y: 16, z: 16 }, * lifetime: 300 // Delete after 5 minutes. * }); * Entities.setAllVoxels(polyVox, 1); * Entities.setVoxel(polyVox, { x: 0, y: 0, z: 0 }, 0); * * // Red sphere in 0,0,0 corner position. * var cornerPosition = Entities.voxelCoordsToWorldCoords(polyVox, { x: 0, y: 0, z: 0 }); * var voxelDimensions = Vec3.multiply(2 / 16, Vec3.ONE); * var sphere = Entities.addEntity({ * type: "Sphere", * position: Vec3.sum(cornerPosition, Vec3.multiply(0.5, voxelDimensions)), * dimensions: voxelDimensions, * color: { red: 255, green: 0, blue: 0 }, * lifetime: 300 // Delete after 5 minutes. * }); */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords); /**jsdoc * Convert world coordinates to voxel coordinates in a {@link Entities.EntityType|PolyVox} entity. Voxel coordinates are * relative to the minimum axes values corner of the entity, with a scale of Vec3.ONE being the dimensions of * each voxel. * @function Entities.worldCoordsToVoxelCoords * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} worldCoords - The world coordinates. May be outside the entity's bounding box. * @returns {Vec3} The voxel coordinates of the worldCoords if the entityID is a * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. The value may be fractional. */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords); /**jsdoc * Convert voxel coordinates in a {@link Entities.EntityType|PolyVox} entity to local coordinates relative to the minimum * axes value corner of the entity, with the scale being the same as world coordinates. * @function Entities.voxelCoordsToLocalCoords * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} voxelCoords - The voxel coordinates. May be fractional and outside the entity's bounding box. * @returns {Vec3} The local coordinates of the voxelCoords if the entityID is a * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. * @example Get the world dimensions of a voxel in a PolyVox entity. * var polyVox = Entities.addEntity({ * type: "PolyVox", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), * dimensions: { x: 2, y: 2, z: 2 }, * voxelVolumeSize: { x: 16, y: 16, z: 16 }, * lifetime: 300 // Delete after 5 minutes. * }); * var voxelDimensions = Entities.voxelCoordsToLocalCoords(polyVox, Vec3.ONE); * print("Voxel dimensions: " + JSON.stringify(voxelDimensions)); */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords); /**jsdoc * Convert local coordinates to voxel coordinates in a {@link Entities.EntityType|PolyVox} entity. Local coordinates are * relative to the minimum axes value corner of the entity, with the scale being the same as world coordinates. * @function Entities.localCoordsToVoxelCoords * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. * @param {Vec3} localCoords - The local coordinates. May be outside the entity's bounding box. * @returns {Vec3} The voxel coordinates of the worldCoords if the entityID is a * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3(0)|Vec3.ZERO}. The value may be fractional. */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); /**jsdoc * Set the linePoints property of a {@link Entities.EntityType|Line} entity. * @function Entities.setAllPoints * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. * @param {Vec3[]} points - The array of points to set the entity's linePoints property to. * @returns {boolean} true if the entity's property was updated, otherwise false. The property * may fail to be updated if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, * one of the points is outside the entity's dimensions, or the number of points is greater than the maximum allowed. * @example Change the shape of a Line entity. * // Draw a horizontal line between two points. * var entity = Entities.addEntity({ * type: "Line", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 2, y: 2, z: 1 }, * linePoints: [ * { x: -1, y: 0, z: 0 }, * { x:1, y: -0, z: 0 } * ], * color: { red: 255, green: 0, blue: 0 }, * lifetime: 300 // Delete after 5 minutes. * }); * * // Change the line to be a "V". * Script.setTimeout(function () { * Entities.setAllPoints(entity, [ * { x: -1, y: 1, z: 0 }, * { x: 0, y: -1, z: 0 }, * { x: 1, y: 1, z: 0 }, * ]); * }, 2000); */ Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); /**jsdoc * Append a point to a {@link Entities.EntityType|Line} entity. * @function Entities.appendPoint * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. * @param {Vec3} point - The point to add to the line. The coordinates are relative to the entity's position. * @returns {boolean} true if the point was added to the line, otherwise false. The point may * fail to be added if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, the * point is outside the entity's dimensions, or the maximum number of points has been reached. * @example Append a point to a Line entity. * // Draw a line between two points. * var entity = Entities.addEntity({ * type: "Line", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), * rotation: MyAvatar.orientation, * dimensions: { x: 2, y: 2, z: 1 }, * linePoints: [ * { x: -1, y: 1, z: 0 }, * { x: 0, y: -1, z: 0 } * ], * color: { red: 255, green: 0, blue: 0 }, * lifetime: 300 // Delete after 5 minutes. * }); * * // Add a third point to create a "V". * Entities.appendPoint(entity, { x: 1, y: 1, z: 0 }); */ Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); /**jsdoc * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about — domain * and client-only — to the program log. * @function Entities.dumpTree */ Q_INVOKABLE void dumpTree() const; /**jsdoc * Add an action to an entity. An action is registered with the physics engine and is applied every physics simulation * step. Any entity may have more than one action associated with it, but only as many as will fit in an entity's * actionData property. * @function Entities.addAction * @param {Entities.ActionType} actionType - The type of action. * @param {Uuid} entityID - The ID of the entity to add the action to. * @param {Entities.ActionArguments} arguments - Configure the action. * @returns {Uuid} The ID of the action added if successfully added, otherwise null. * @example Constrain a cube to move along a vertical line. * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, * dynamic: true, * collisionless: false, * userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }", * lifetime: 300 // Delete after 5 minutes. * }); * * var actionID = Entities.addAction("slider", entityID, { * axis: { x: 0, y: 1, z: 0 }, * linearLow: 0, * linearHigh: 0.6 * }); */ Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments); /**jsdoc * Update an entity action. * @function Entities.updateAction * @param {Uuid} entityID - The ID of the entity with the action to update. * @param {Uuid} actionID - The ID of the action to update. * @param {Entities.ActionArguments} arguments - The arguments to update. * @returns {boolean} true if the update was successful, otherwise false. */ Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments); /**jsdoc * Delete an action from an entity. * @function Entities.deleteAction * @param {Uuid} entityID - The ID of entity to delete the action from. * @param {Uuid} actionID - The ID of the action to delete. * @returns {boolean} true if the update was successful, otherwise false. */ Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID); /**jsdoc * Get the IDs of the actions that are associated with an entity. * @function Entities.getActionIDs * @param {Uuid} entityID - The entity to get the action IDs for. * @returns {Uuid[]} An array of action IDs if any are found, otherwise an empty array. */ Q_INVOKABLE QVector getActionIDs(const QUuid& entityID); /**jsdoc * Get the arguments of an action. * @function Entities.getActionArguments * @param {Uuid} entityID - The ID of the entity with the action. * @param {Uuid} actionID - The ID of the action to get the arguments of. * @returns {Entities.ActionArguments} The arguments of the requested action if found, otherwise an empty object. */ Q_INVOKABLE QVariantMap getActionArguments(const QUuid& entityID, const QUuid& actionID); /**jsdoc * Get the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and * orientation. * @function Entities.getAbsoluteJointTranslationInObjectFrame * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @returns {Vec3} The translation of the joint relative to the entity's position and orientation if the entity is a * {@link Entities.EntityType|Model} entity, the entity is loaded, and the joint index is valid; otherwise * {@link Vec3(0)|Vec3.ZERO}. */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex); /**jsdoc * Get the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and * orientation. * @function Entities.getAbsoluteJointRotationInObjectFrame * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @returns {Quat} The rotation of the joint relative to the entity's orientation if the entity is a * {@link Entities.EntityType|Model} entity, the entity is loaded, and the joint index is valid; otherwise * {@link Quat(0)|Quat.IDENTITY}. * @example Compare the local and absolute rotations of an avatar model's left hand joint. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var index = Entities.getJointIndex(entityID, "LeftHand"); * var localRotation = Entities.getLocalJointRotation(entityID, index); * var absoluteRotation = Entities.getAbsoluteJointRotationInObjectFrame(entityID, index); * print("Left hand local rotation: " + JSON.stringify(Quat.safeEulerAngles(localRotation))); * print("Left hand absolute rotation: " + JSON.stringify(Quat.safeEulerAngles(absoluteRotation))); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::quat getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex); /**jsdoc * Set the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and * orientation. * @function Entities.setAbsoluteJointTranslationInObjectFrame * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @param {Vec3} translation - The translation to set the joint to relative to the entity's position and orientation. * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the joint index is valid, and the translation is different to the joint's current translation; otherwise * false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex, glm::vec3 translation); /**jsdoc * Set the rotation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and * orientation. * @function Entities.setAbsoluteJointRotationInObjectFrame * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @param {Quat} rotation - The rotation to set the joint to relative to the entity's orientation. * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the joint index is valid, and the rotation is different to the joint's current rotation; otherwise false. * @example Raise an avatar model's left palm. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var index = Entities.getJointIndex(entityID, "LeftHand"); * var absoluteRotation = Entities.getAbsoluteJointRotationInObjectFrame(entityID, index); * absoluteRotation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, 90), absoluteRotation); * var success = Entities.setAbsoluteJointRotationInObjectFrame(entityID, index, absoluteRotation); * print("Success: " + success); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex, glm::quat rotation); /**jsdoc * Get the local translation of a joint in a {@link Entities.EntityType|Model} entity. * @function Entities.getLocalJointTranslation * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @returns {Vec3} The local translation of the joint if the entity is a {@link Entities.EntityType|Model} entity, the * entity is loaded, and the joint index is valid; otherwise {@link Vec3(0)|Vec3.ZERO}. */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 getLocalJointTranslation(const QUuid& entityID, int jointIndex); /**jsdoc * Get the local rotation of a joint in a {@link Entities.EntityType|Model} entity. * @function Entities.getLocalJointRotation * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @returns {Quat} The local rotation of the joint if the entity is a {@link Entities.EntityType|Model} entity, the entity * is loaded, and the joint index is valid; otherwise {@link Quat(0)|Quat.IDENTITY}. * @example Report the local rotation of an avatar model's head joint. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var index = Entities.getJointIndex(entityID, "Head"); * var rotation = Entities.getLocalJointRotation(entityID, index); * print("Head local rotation: " + JSON.stringify(Quat.safeEulerAngles(rotation))); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::quat getLocalJointRotation(const QUuid& entityID, int jointIndex); /**jsdoc * Set the local translation of a joint in a {@link Entities.EntityType|Model} entity. * @function Entities.setLocalJointTranslation * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @param {Vec3} translation - The local translation to set the joint to. * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the joint index is valid, and the translation is different to the joint's current translation; otherwise * false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec3 translation); /**jsdoc * Set the local rotation of a joint in a {@link Entities.EntityType|Model} entity. * @function Entities.setLocalJointRotation * @param {Uuid} entityID - The ID of the entity. * @param {number} jointIndex - The integer index of the joint. * @param {Quat} rotation - The local rotation to set the joint to. * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the joint index is valid, and the rotation is different to the joint's current rotation; otherwise false. * @example Make an avatar model turn its head left. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var index = Entities.getJointIndex(entityID, "Head"); * var rotation = Quat.fromPitchYawRollDegrees(0, 60, 0); * var success = Entities.setLocalJointRotation(entityID, index, rotation); * print("Success: " + success); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::quat rotation); /**jsdoc * Set the local translations of joints in a {@link Entities.EntityType|Model} entity. * @function Entities.setLocalJointTranslations * @param {Uuid} entityID - The ID of the entity. * @param {Vec3[]} translations - The local translations to set the joints to. * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the model has joints, and at least one of the translations is different to the model's current translations; * otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector& translations); /**jsdoc * Set the local rotations of joints in a {@link Entities.EntityType|Model} entity. * @function Entities.setLocalJointRotations * @param {Uuid} entityID - The ID of the entity. * @param {Quat[]} rotations - The local rotations to set the joints to. * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the model has joints, and at least one of the rotations is different to the model's current rotations; otherwise * false. * @example Raise both palms of an avatar model. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * * // Get all the joint rotations. * var jointNames = Entities.getJointNames(entityID); * var jointRotations = []; * for (var i = 0, length = jointNames.length; i < length; i++) { * var index = Entities.getJointIndex(entityID, jointNames[i]); * jointRotations.push(Entities.getLocalJointRotation(entityID, index)); * } * * // Raise both palms. * var index = jointNames.indexOf("LeftHand"); * jointRotations[index] = Quat.multiply(Quat.fromPitchYawRollDegrees(-90, 0, 0), jointRotations[index]); * index = jointNames.indexOf("RightHand"); * jointRotations[index] = Quat.multiply(Quat.fromPitchYawRollDegrees(-90, 0, 0), jointRotations[index]); * * // Update all the joint rotations. * var success = Entities.setLocalJointRotations(entityID, jointRotations); * print("Success: " + success); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointRotations(const QUuid& entityID, const QVector& rotations); /**jsdoc * Set the local rotations and translations of joints in a {@link Entities.EntityType|Model} entity. This is the same as * calling both {@link Entities.setLocalJointRotations|setLocalJointRotations} and * {@link Entities.setLocalJointTranslations|setLocalJointTranslations} at the same time. * @function Entities.setLocalJointsData * @param {Uuid} entityID - The ID of the entity. * @param {Quat[]} rotations - The local rotations to set the joints to. * @param {Vec3[]} translations - The local translations to set the joints to. * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, * the model has joints, and at least one of the rotations or translations is different to the model's current values; * otherwise false. */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointsData(const QUuid& entityID, const QVector& rotations, const QVector& translations); /**jsdoc * Get the index of a named joint in a {@link Entities.EntityType|Model} entity. * @function Entities.getJointIndex * @param {Uuid} entityID - The ID of the entity. * @param {string} name - The name of the joint. * @returns {number} The integer index of the joint if the entity is a {@link Entities.EntityType|Model} entity, the entity * is loaded, and the joint is present; otherwise -1. The joint indexes are in order per * {@link Entities.getJointNames|getJointNames}. * @example Report the index of a model's head joint. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var index = Entities.getJointIndex(entityID, "Head"); * print("Head joint index: " + index); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name); /**jsdoc * Get the names of all the joints in a {@link Entities.EntityType|Model} entity. * @function Entities.getJointNames * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Model} entity. * @returns {string[]} The names of all the joints in the entity if it is a {@link Entities.EntityType|Model} entity and * is loaded, otherwise an empty array. The joint names are in order per {@link Entities.getJointIndex|getJointIndex}. * @example Report a model's joint names. * entityID = Entities.addEntity({ * type: "Model", * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * rotation: MyAvatar.orientation, * lifetime: 300 // Delete after 5 minutes. * }); * * Script.setTimeout(function () { * // Joint data aren't available until after the model has loaded. * var jointNames = Entities.getJointNames(entityID); * print("Joint names: " + JSON.stringify(jointNames)); * }, 2000); */ // FIXME move to a renderable entity interface Q_INVOKABLE QStringList getJointNames(const QUuid& entityID); /**jsdoc * Get the IDs of entities, overlays, and avatars that are directly parented to an entity. To get all descendants of an * entity, recurse on the IDs returned by the function. * @function Entities.getChildrenIDs * @param {Uuid} parentID - The ID of the entity to get the children IDs of. * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID * entity. Does not include children's children, etc. The array is empty if no children can be found or * parentID cannot be found. * @example Report the children of an entity. * function createEntity(description, position, parent) { * var entity = Entities.addEntity({ * type: "Sphere", * position: position, * dimensions: Vec3.HALF, * parentID: parent, * lifetime: 300 // Delete after 5 minutes. * }); * print(description + ": " + entity); * return entity; * } * * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); * var root = createEntity("Root", position, Uuid.NULL); * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); * var grandChild = createEntity("Grandchild", Vec3.sum(position, { x: 0, y: -2, z: 0 }), child); * * var children = Entities.getChildrenIDs(root); * print("Children of root: " + JSON.stringify(children)); // Only the child entity. */ Q_INVOKABLE QVector getChildrenIDs(const QUuid& parentID); /**jsdoc * Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model's joint. * @function Entities.getChildrenIDsOfJoint * @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of. * @param {number} jointIndex - Integer number of the model joint to get the children IDs of. * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID * entity, overlay, or avatar at the jointIndex joint. Does not include children's children, etc. The * array is empty if no children can be found or parentID cannot be found. * @example Report the children of your avatar's right hand. * function createEntity(description, position, parent) { * var entity = Entities.addEntity({ * type: "Sphere", * position: position, * dimensions: Vec3.HALF, * parentID: parent, * lifetime: 300 // Delete after 5 minutes. * }); * print(description + ": " + entity); * return entity; * } * * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); * var root = createEntity("Root", position, Uuid.NULL); * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); * * Entities.editEntity(root, { * parentID: MyAvatar.sessionUUID, * parentJointIndex: MyAvatar.getJointIndex("RightHand") * }); * * var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, MyAvatar.getJointIndex("RightHand")); * print("Children of hand: " + JSON.stringify(children)); // Only the root entity. */ Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); /**jsdoc * Check whether an entity or overlay has an entity as an ancestor (parent, parent's parent, etc.). * @function Entities.isChildOfParent * @param {Uuid} childID - The ID of the child entity or overlay to test for being a child, grandchild, etc. * @param {Uuid} parentID - The ID of the parent entity to test for being a parent, grandparent, etc. * @returns {boolean} true if the childID entity or overlay has the parentID entity * as a parent or grandparent etc., otherwise false. * @example Check that a grandchild entity is a child of its grandparent. * function createEntity(description, position, parent) { * var entity = Entities.addEntity({ * type: "Sphere", * position: position, * dimensions: Vec3.HALF, * parentID: parent, * lifetime: 300 // Delete after 5 minutes. * }); * print(description + ": " + entity); * return entity; * } * * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); * var root = createEntity("Root", position, Uuid.NULL); * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); * var grandChild = createEntity("Grandchild", Vec3.sum(position, { x: 0, y: -2, z: 0 }), child); * * print("grandChild has root as parent: " + Entities.isChildOfParent(grandChild, root)); // true */ Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID); /**jsdoc * Get the type — entity, overlay, or avatar — of an in-world item. * @function Entities.getNestableType * @param {Uuid} entityID - The ID of the item to get the type of. * @returns {string} The type of the item: "entity" if the item is an entity, "overlay" if the * the item is an overlay, "avatar" if the item is an avatar; otherwise "unknown" if the item * cannot be found. * @example Print some nestable types. * var entity = Entities.addEntity({ * type: "Sphere", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 1, z: -2 })), * lifetime: 300 // Delete after 5 minutes. * }); * * print(Entities.getNestableType(entity)); // "entity" * print(Entities.getNestableType(Uuid.generate())); // "unknown" */ Q_INVOKABLE QString getNestableType(QUuid entityID); /**jsdoc * Get the ID of the {@link Entities.EntityType|Web} entity that has keyboard focus. * @function Entities.getKeyboardFocusEntity * @returns {Uuid} The ID of the {@link Entities.EntityType|Web} entity that has focus, if any, otherwise null. */ Q_INVOKABLE QUuid getKeyboardFocusEntity() const; /**jsdoc * Set the {@link Entities.EntityType|Web} entity that has keyboard focus. * @function Entities.setKeyboardFocusEntity * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity to set keyboard focus to. Use * null or {@link Uuid|Uuid.NULL} to unset keyboard focus from an entity. */ Q_INVOKABLE void setKeyboardFocusEntity(const EntityItemID& id); /**jsdoc * Emit a {@link Entities.mousePressOnEntity|mousePressOnEntity} event. * @function Entities.sendMousePressOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendMousePressOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.mouseMoveOnEntity|mouseMoveOnEntity} event. * @function Entities.sendMouseMoveOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendMouseMoveOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.mouseReleaseOnEntity|mouseReleaseOnEntity} event. * @function Entities.sendMouseReleaseOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendMouseReleaseOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.clickDownOnEntity|clickDownOnEntity} event. * @function Entities.sendClickDownOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendClickDownOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.holdingClickOnEntity|holdingClickOnEntity} event. * @function Entities.sendHoldingClickOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendHoldingClickOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.clickReleaseOnEntity|clickReleaseOnEntity} event. * @function Entities.sendClickReleaseOnEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendClickReleaseOnEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.hoverEnterEntity|hoverEnterEntity} event. * @function Entities.sendHoverEnterEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendHoverEnterEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.hoverOverEntity|hoverOverEntity} event. * @function Entities.sendHoverOverEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendHoverOverEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Emit a {@link Entities.hoverLeaveEntity|hoverLeaveEntity} event. * @function Entities.sendHoverLeaveEntity * @param {Uuid} entityID - The ID of the entity to emit the event for. * @param {PointerEvent} event - The event details. */ Q_INVOKABLE void sendHoverLeaveEntity(const EntityItemID& id, const PointerEvent& event); /**jsdoc * Check whether an entity wants hand controller pointer events. For example, a {@link Entities.EntityType|Web} entity does * but a {@link Entities.EntityType|Shape} entity doesn't. * @function Entities.wantsHandControllerPointerEvents * @param {Uuid} entityID - The ID of the entity. * @returns {boolean} true if the entity can be found and it wants hand controller pointer events, otherwise * false. */ Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id); /**jsdoc * Send a script event over a {@link Entities.EntityType|Web} entity's EventBridge to the Web page's scripts. * @function Entities.emitScriptEvent * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity. * @param {string} message - The message to send. * @todo This function is currently not implemented. */ Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message); /**jsdoc * Check whether an axis-aligned box and a capsule intersect. * @function Entities.AABoxIntersectsCapsule * @param {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. * @param {Vec3} dimensions - The dimensions of the AA box. * @param {Vec3} start - One end of the capsule. * @param {Vec3} end - The other end of the capsule. * @param {number} radius - The radiues of the capsule. * @returns {boolean} true if the AA box and capsule intersect, otherwise false. */ Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, const glm::vec3& start, const glm::vec3& end, float radius); /**jsdoc * Get the meshes in a {@link Entities.EntityType|Model} or {@link Entities.EntityType|PolyVox} entity. * @function Entities.getMeshes * @param {Uuid} entityID - The ID of the Model or PolyVox entity to get the meshes of. * @param {Entities~getMeshesCallback} callback - The function to call upon completion. * @deprecated Use the {@link Graphics} API instead. */ /**jsdoc * Called when {@link Entities.getMeshes} is complete. * @callback Entities~getMeshesCallback * @param {MeshProxy[]} meshes - If success< is true, a {@link MeshProxy} per mesh in the * Model or PolyVox entity; otherwise undefined. * @param {boolean} success - true if the {@link Entities.getMeshes} call was successful, false * otherwise. The call may be unsuccessful if the requested entity could not be found. * @deprecated Use the {@link Graphics} API instead. */ // FIXME move to a renderable entity interface Q_INVOKABLE void getMeshes(QUuid entityID, QScriptValue callback); /**jsdoc * Get the object to world transform, excluding scale, of an entity. * @function Entities.getEntityTransform * @param {Uuid} entityID - The ID of the entity. * @returns {Mat4} The entity's object to world transform excluding scale (i.e., translation and rotation, with scale of 1) * if the entity can be found, otherwise a transform with zero translation and rotation and a scale of 1. * @example Position and rotation in an entity's world transform. * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 1, z: -2 })); * var orientation = MyAvatar.orientation; * print("Position: " + JSON.stringify(position)); * print("Orientation: " + JSON.stringify(orientation)); * * var entityID = Entities.addEntity({ * type: "Sphere", * position: position, * rotation: orientation, * dimensions: Vec3.HALF, * lifetime: 300 // Delete after 5 minutes. * }); * * var transform = Entities.getEntityTransform(entityID); * print("Transform: " + JSON.stringify(transform)); * print("Translation: " + JSON.stringify(Mat4.extractTranslation(transform))); // Same as position. * print("Rotation: " + JSON.stringify(Mat4.extractRotation(transform))); // Same as orientation. * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); /**jsdoc * Get the object to parent transform, excluding scale, of an entity. * @function Entities.getEntityLocalTransform * @param {Uuid} entityID - The ID of the entity. * @returns {Mat4} The entity's object to parent transform excluding scale (i.e., translation and rotation, with scale of * 1) if the entity can be found, otherwise a transform with zero translation and rotation and a scale of 1. * @example Position and rotation in an entity's local transform. * function createEntity(position, rotation, parent) { * var entity = Entities.addEntity({ * type: "Box", * position: position, * rotation: rotation, * dimensions: Vec3.HALF, * parentID: parent, * lifetime: 300 // Delete after 5 minutes. * }); * return entity; * } * * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); * * var parent = createEntity(position, MyAvatar.orientation, Uuid.NULL); * * var childTranslation = { x: 0, y: -1.5, z: 0 }; * var childRotation = Quat.fromPitchYawRollDegrees(0, 45, 0); * var child = createEntity(Vec3.sum(position, childTranslation), Quat.multiply(childRotation, MyAvatar.orientation), parent); * * var transform = Entities.getEntityLocalTransform(child); * print("Transform: " + JSON.stringify(transform)); * print("Translation: " + JSON.stringify(Mat4.extractTranslation(transform))); // childTranslation * print("Rotation: " + JSON.stringify(Quat.safeEulerAngles(Mat4.extractRotation(transform)))); // childRotation * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); /**jsdoc * Get the static certificate for an entity. The static certificate contains static properties of the item which cannot * be altered. * @function Entities.getStaticCertificateJSON * @param {Uuid} entityID - The ID of the entity to get the static certificate for. * @returns {string} The entity's static certificate as a JSON string if the entity can be found, otherwise an empty string. */ Q_INVOKABLE QString getStaticCertificateJSON(const QUuid& entityID); /**jsdoc * Verify the entity's proof of provenance, i.e., that the entity's certificateID property was produced by * High Fidelity signing the entity's static certificate JSON. * @function Entities.verifyStaticCertificateProperties * @param {Uuid} entityID - The ID of the entity to verify. * @returns {boolean} true if the entity can be found an its certificateID property is present * and its value matches the entity's static certificate JSON; otherwise false. */ Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID); signals: /**jsdoc * Triggered on the client that is the physics simulation owner during the collision of two entities. Note: Isn't triggered * for a collision with an avatar. * @function Entities.collisionWithEntity * @param {Uuid} idA - The ID of one entity in the collision. For an entity script, this is the ID of the entity containing * the script. * @param {Uuid} idB - The ID of the other entity in the collision. * @param {Collision} collision - The details of the collision. * @returns {Signal} * @example Change the color of an entity when it collides with another entity. * var entityScript = (function () { * function randomInteger(min, max) { * return Math.floor(Math.random() * (max - min + 1)) + min; * } * * this.collisionWithEntity = function (myID, otherID, collision) { * Entities.editEntity(myID, { * color: { * red: randomInteger(128, 255), * green: randomInteger(128, 255), * blue: randomInteger(128, 255) * } * }); * }; * }); * * var entityID = Entities.addEntity({ * type: "Box", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, * color: { red: 128, green: 128, blue: 128 }, * gravity: { x: 0, y: -9.8, z: 0 }, * velocity: { x: 0, y: 0.1, z: 0 }, // Kick off physics. * dynamic: true, * collisionless: false, // So that collision events are generated. * script: "(" + entityScript + ")", // Could host the script on a Web server instead. * lifetime: 300 // Delete after 5 minutes. * }); */ void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); /**jsdoc * Triggered when your ability to change the locked property of entities changes. * @function Entities.canAdjustLocksChanged * @param {boolean} canAdjustLocks - true if the script can change the locked property of an * entity, otherwise false. * @returns {Signal} * @example Report when your ability to change locks changes. * function onCanAdjustLocksChanged(canAdjustLocks) { * print("You can adjust entity locks: " + canAdjustLocks); * } * Entities.canAdjustLocksChanged.connect(onCanAdjustLocksChanged); */ void canAdjustLocksChanged(bool canAdjustLocks); /**jsdoc * Triggered when your ability to rez (create) entities changes. * @function Entities.canRezChanged * @param {boolean} canRez - true if the script can rez (create) entities, otherwise false. * @returns {Signal} */ void canRezChanged(bool canRez); /**jsdoc * Triggered when your ability to rez (create) temporary entities changes. Temporary entities are entities with a finite * lifetime property value set. * @function Entities.canRezTmpChanged * @param {boolean} canRezTmp - true if the script can rez (create) temporary entities, otherwise * false. * @returns {Signal} */ void canRezTmpChanged(bool canRezTmp); /**jsdoc * Triggered when your ability to rez (create) certified entities changes. Certified entities are entities that have PoP * certificates. * @function Entities.canRezCertifiedChanged * @param {boolean} canRezCertified - true if the script can rez (create) certified entities, otherwise * false. * @returns {Signal} */ void canRezCertifiedChanged(bool canRezCertified); /**jsdoc * Triggered when your ability to rez (create) temporary certified entities changes. Temporary entities are entities with a * finite lifetime property value set. Certified entities are entities that have PoP certificates. * @function Entities.canRezTmpCertifiedChanged * @param {boolean} canRezTmpCertified - true if the script can rez (create) temporary certified entities, * otherwise false. * @returns {Signal} */ void canRezTmpCertifiedChanged(bool canRezTmpCertified); /**jsdoc * Triggered when your ability to make changes to the asset server's assets changes. * @function Entities.canWriteAssetsChanged * @param {boolean} canWriteAssets - true if the script can change the ? property of an entity, * otherwise false. * @returns {Signal} */ void canWriteAssetsChanged(bool canWriteAssets); /**jsdoc * Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully * pressed while its laser is on an entity. * @function Entities.mousePressOnEntity * @param {Uuid} entityID - The ID of the entity that was pressed. * @param {PointerEvent} event - Details of the event. * @returns {Signal} * @example Report when an entity is clicked with the mouse or laser. * function onMousePressOnEntity(entityID, event) { * print("Clicked on entity: " + entityID); * } * * Entities.mousePressOnEntity.connect(onMousePressOnEntity); */ void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when a mouse button is double-clicked while the mouse cursor is on an entity. * @function Entities.mouseDoublePressOnEntity * @param {Uuid} entityID - The ID of the entity that was double-pressed. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void mouseDoublePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Repeatedly triggered while the mouse cursor or controller laser moves on an entity. * @function Entities.mouseMoveOnEntity * @param {Uuid} entityID - The ID of the entity that was moved on. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when a mouse button is released after clicking on an entity or the controller trigger is partly or fully * released after pressing on an entity, even if the mouse pointer or controller laser has moved off the entity. * @function Entities.mouseReleaseOnEntity * @param {Uuid} entityID - The ID of the entity that was originally pressed. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when a mouse button is clicked while the mouse cursor is not on an entity. * @function Entities.mousePressOffEntity * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void mousePressOffEntity(); /**jsdoc * Triggered when a mouse button is double-clicked while the mouse cursor is not on an entity. * @function Entities.mouseDoublePressOffEntity * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void mouseDoublePressOffEntity(); /**jsdoc * Triggered when a mouse button is clicked while the mouse cursor is on an entity. Note: Not triggered by controller. * @function Entities.clickDownOnEntity * @param {Uuid} entityID - The ID of the entity that was clicked. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Repeatedly triggered while a mouse button continues to be held after clicking an entity, even if the mouse cursor has * moved off the entity. Note: Not triggered by controller. * @function Entities.holdingClickOnEntity * @param {Uuid} entityID - The ID of the entity that was originally clicked. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when a mouse button is released after clicking on an entity, even if the mouse cursor has moved off the * entity. Note: Not triggered by controller. * @function Entities.clickReleaseOnEntity * @param {Uuid} entityID - The ID of the entity that was originally clicked. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void clickReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when the mouse cursor or controller laser starts hovering on an entity. * @function Entities.hoverEnterEntity * @param {Uuid} entityID - The ID of the entity that is being hovered. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void hoverEnterEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Repeatedly triggered when the mouse cursor or controller laser moves while hovering over an entity. * @function Entities.hoverOverEntity * @param {Uuid} entityID - The ID of the entity that is being hovered. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void hoverOverEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when the mouse cursor or controller laser stops hovering over an entity. * @function Entities.hoverLeaveEntity * @param {Uuid} entityID - The ID of the entity that was being hovered. * @param {PointerEvent} event - Details of the event. * @returns {Signal} */ void hoverLeaveEntity(const EntityItemID& entityItemID, const PointerEvent& event); /**jsdoc * Triggered when an avatar enters an entity. * @function Entities.enterEntity * @param {Uuid} entityID - The ID of the entity that the avatar entered. * @returns {Signal} * @example Change the color of an entity when an avatar enters or leaves. * var entityScript = (function () { * this.enterEntity = function (entityID) { * print("Enter entity"); * Entities.editEntity(entityID, { * color: { red: 255, green: 64, blue: 64 }, * }); * }; * this.leaveEntity = function (entityID) { * print("Leave entity"); * Entities.editEntity(entityID, { * color: { red: 128, green: 128, blue: 128 }, * }); * }; * }); * * var entityID = Entities.addEntity({ * type: "Sphere", * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), * dimensions: { x: 3, y: 3, z: 3 }, * color: { red: 128, green: 128, blue: 128 }, * collisionless: true, // So that avatar can walk through entity. * script: "(" + entityScript + ")", // Could host the script on a Web server instead. * lifetime: 300 // Delete after 5 minutes. * }); */ void enterEntity(const EntityItemID& entityItemID); /**jsdoc * Triggered when an avatar leaves an entity. * @function Entities.leaveEntity * @param {Uuid} entityID - The ID of the entity that the avatar left. * @returns {Signal} */ void leaveEntity(const EntityItemID& entityItemID); /**jsdoc * Triggered when an entity is deleted. * @function Entities.deletingEntity * @param {Uuid} entityID - The ID of the entity deleted. * @returns {Signal} * @example Report when an entity is deleted. * Entities.deletingEntity.connect(function (entityID) { * print("Deleted entity: " + entityID); * }); */ void deletingEntity(const EntityItemID& entityID); /**jsdoc * Triggered when an entity is added to Interface's local in-memory tree of entities it knows about. This may occur when * entities are loaded upon visiting a domain, when the user rotates their view so that more entities become visible, and * when a domain or client-only entity is added (e.g., by {@Entities.addEntity|addEntity}). * @function Entities.addingEntity * @param {Uuid} entityID - The ID of the entity added. * @returns {Signal} * @example Report when an entity is added. * Entities.addingEntity.connect(function (entityID) { * print("Added entity: " + entityID); * }); */ void addingEntity(const EntityItemID& entityID); /**jsdoc * Triggered when you disconnect from a domain, at which time Interface's local in-memory tree of entities it knows about * is cleared. * @function Entities.clearingEntities * @returns {Signal} * @example Report when Interfaces's entity tree is cleared. * Entities.clearingEntities.connect(function () { * print("Entities cleared"); * }); */ void clearingEntities(); /**jsdoc * Triggered in when a script in a {@link Entities.EntityType|Web} entity's Web page script sends an event over the * script's EventBridge. * @function Entities.webEventReceived * @param {Uuid} entityID - The ID of the entity that event was received from. * @param {string} message - The message received. * @returns {Signal} */ void webEventReceived(const EntityItemID& entityItemID, const QVariant& message); protected: void withEntitiesScriptEngine(std::function)> function) { std::lock_guard lock(_entitiesScriptEngineLock); function(_entitiesScriptEngine); }; private slots: void handleEntityScriptCallMethodPacket(QSharedPointer receivedMessage, SharedNodePointer senderNode); private: bool actionWorker(const QUuid& entityID, std::function actor); bool polyVoxWorker(QUuid entityID, std::function actor); bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID, EntityTypes::EntityType entityType = EntityTypes::Unknown); /// 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& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly = false, bool collidableOnly = false); EntityTreePointer _entityTree; std::recursive_mutex _entitiesScriptEngineLock; QSharedPointer _entitiesScriptEngine; bool _bidOnSimulationOwnership { false }; ActivityTracking _activityTracking; }; #endif // hifi_EntityScriptingInterface_h