SamGondelmanmerge with master2ca217e8 years agoHistory
1889 lines · 79.1 KB · C++
RawDownload
////  EntityScriptingInterface.cpp//  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// #include "EntityScriptingInterface.h" #include <glm/gtc/matrix_transform.hpp>#include <glm/gtx/transform.hpp> #include <QFutureWatcher>#include <QtConcurrent/QtConcurrentRun> #include <shared/QtHelpers.h>#include <VariantMapToScriptValue.h>#include <SharedUtil.h>#include <SpatialParentFinder.h>#include <AvatarHashMap.h> #include "EntityItemID.h"#include "EntitiesLogging.h"#include "EntityDynamicFactoryInterface.h"#include "EntityDynamicInterface.h"#include "EntitySimulation.h"#include "EntityTree.h"#include "LightEntityItem.h"#include "ModelEntityItem.h"#include "QVariantGLM.h"#include "SimulationOwner.h"#include "ZoneEntityItem.h"#include "WebEntityItem.h"#include <EntityScriptClient.h>#include <Profile.h>  EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :    _entityTree(NULL),    _bidOnSimulationOwnership(bidOnSimulationOwnership){    auto nodeList = DependencyManager::get<NodeList>();    connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged);    connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);    connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged);    connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged);    connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged);    connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged);     // If the user clicks somewhere where there is no entity at all, we will release focus    connect(this, &EntityScriptingInterface::mousePressOffEntity, [=]() {        setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);    });     auto& packetReceiver = nodeList->getPacketReceiver();    packetReceiver.registerListener(PacketType::EntityScriptCallMethod, this, "handleEntityScriptCallMethodPacket");} void EntityScriptingInterface::queueEntityMessage(PacketType packetType,                                                  EntityItemID entityID, const EntityItemProperties& properties) {    getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties);} void EntityScriptingInterface::resetActivityTracking() {    _activityTracking.addedEntityCount = 0;    _activityTracking.deletedEntityCount = 0;    _activityTracking.editedEntityCount = 0;} bool EntityScriptingInterface::canAdjustLocks() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->isAllowedEditor();} bool EntityScriptingInterface::canRez() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanRez();} bool EntityScriptingInterface::canRezTmp() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanRezTmp();} bool EntityScriptingInterface::canRezCertified() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanRezCertified();} bool EntityScriptingInterface::canRezTmpCertified() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanRezTmpCertified();} bool EntityScriptingInterface::canWriteAssets() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanWriteAssets();} bool EntityScriptingInterface::canReplaceContent() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanReplaceContent();} void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {    if (_entityTree) {        disconnect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);        disconnect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);        disconnect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);    }     _entityTree = elementTree;     if (_entityTree) {        connect(_entityTree.get(), &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);        connect(_entityTree.get(), &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);        connect(_entityTree.get(), &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);    }} EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperties& entitySideProperties,                                                        bool scalesWithParent) {    // In EntityTree code, properties.position and properties.rotation are relative to the parent.  In javascript,    // they are in world-space.  The local versions are put into localPosition and localRotation and position and    // rotation are converted from local to world space.    EntityItemProperties scriptSideProperties = entitySideProperties;    scriptSideProperties.setLocalPosition(entitySideProperties.getPosition());    scriptSideProperties.setLocalRotation(entitySideProperties.getRotation());    scriptSideProperties.setLocalVelocity(entitySideProperties.getLocalVelocity());    scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getLocalAngularVelocity());    scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions());     bool success;    glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition().toGlm(),                                                              entitySideProperties.getParentID(),                                                              entitySideProperties.getParentJointIndex(),                                                              scalesWithParent,                                                              success);    glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(),                                                              entitySideProperties.getParentID(),                                                              entitySideProperties.getParentJointIndex(),                                                              scalesWithParent,                                                              success);    glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(entitySideProperties.getVelocity().toGlm(),                                                                      entitySideProperties.getParentID(),                                                                      entitySideProperties.getParentJointIndex(),                                                                      scalesWithParent,                                                                      success);    glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(entitySideProperties.getAngularVelocity().toGlm(),                                                                                    entitySideProperties.getParentID(),                                                                                    entitySideProperties.getParentJointIndex(),                                                                                    scalesWithParent,                                                                                    success);    glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(entitySideProperties.getDimensions().toGlm(),                                                                          entitySideProperties.getParentID(),                                                                          entitySideProperties.getParentJointIndex(),                                                                          scalesWithParent,                                                                          success);      scriptSideProperties.setPosition(worldPosition);    scriptSideProperties.setRotation(worldRotation);    scriptSideProperties.setVelocity(worldVelocity);    scriptSideProperties.setAngularVelocity(worldAngularVelocity);    scriptSideProperties.setDimensions(worldDimensions);     return scriptSideProperties;}  EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties,                                                          bool scalesWithParent) {    // convert position and rotation properties from world-space to local, unless localPosition and localRotation    // are set.  If they are set, they overwrite position and rotation.    EntityItemProperties entitySideProperties = scriptSideProperties;    bool success;     // TODO -- handle velocity and angularVelocity     if (scriptSideProperties.localPositionChanged()) {        entitySideProperties.setPosition(scriptSideProperties.getLocalPosition());    } else if (scriptSideProperties.positionChanged()) {        glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition().toGlm(),                                                                  entitySideProperties.getParentID(),                                                                  entitySideProperties.getParentJointIndex(),                                                                  scalesWithParent, success);        entitySideProperties.setPosition(localPosition);    }     if (scriptSideProperties.localRotationChanged()) {        entitySideProperties.setRotation(scriptSideProperties.getLocalRotation());    } else if (scriptSideProperties.rotationChanged()) {        glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(),                                                                  entitySideProperties.getParentID(),                                                                  entitySideProperties.getParentJointIndex(),                                                                  scalesWithParent, success);        entitySideProperties.setRotation(localRotation);    }     if (scriptSideProperties.localVelocityChanged()) {        entitySideProperties.setVelocity(scriptSideProperties.getLocalVelocity());    } else if (scriptSideProperties.velocityChanged()) {        glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(entitySideProperties.getVelocity().toGlm(),                                                                          entitySideProperties.getParentID(),                                                                          entitySideProperties.getParentJointIndex(),                                                                          scalesWithParent, success);        entitySideProperties.setVelocity(localVelocity);    }     if (scriptSideProperties.localAngularVelocityChanged()) {        entitySideProperties.setAngularVelocity(scriptSideProperties.getLocalAngularVelocity());    } else if (scriptSideProperties.angularVelocityChanged()) {        glm::vec3 localAngularVelocity =            SpatiallyNestable::worldToLocalAngularVelocity(entitySideProperties.getAngularVelocity().toGlm(),                                                           entitySideProperties.getParentID(),                                                           entitySideProperties.getParentJointIndex(),                                                           scalesWithParent, success);        entitySideProperties.setAngularVelocity(localAngularVelocity);    }     if (scriptSideProperties.localDimensionsChanged()) {        entitySideProperties.setDimensions(scriptSideProperties.getLocalDimensions());    } else if (scriptSideProperties.dimensionsChanged()) {        glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(entitySideProperties.getDimensions().toGlm(),                                                                              entitySideProperties.getParentID(),                                                                              entitySideProperties.getParentJointIndex(),                                                                              scalesWithParent, success);        entitySideProperties.setDimensions(localDimensions);    }     return entitySideProperties;}  QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) {    PROFILE_RANGE(script_entities, __FUNCTION__);     _activityTracking.addedEntityCount++;     auto nodeList = DependencyManager::get<NodeList>();    auto sessionID = nodeList->getSessionUUID();     EntityItemProperties propertiesWithSimID = properties;    if (clientOnly) {        const QUuid myNodeID = sessionID;        propertiesWithSimID.setClientOnly(clientOnly);        propertiesWithSimID.setOwningAvatarID(myNodeID);    }     propertiesWithSimID.setLastEditedBy(sessionID);     bool scalesWithParent = propertiesWithSimID.getScalesWithParent();     propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);    propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());     EntityItemID id;    // If we have a local entity tree set, then also update it.    bool success = addLocalEntityCopy(propertiesWithSimID, id);     // queue the packet    if (success) {        queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);        return id;    } else {        return QUuid();    }} bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properties, EntityItemID& id, bool isClone) {    bool success = true;    id = EntityItemID(QUuid::createUuid());     if (_entityTree) {        _entityTree->withWriteLock([&] {            EntityItemPointer entity = _entityTree->addEntity(id, properties, isClone);            if (entity) {                if (properties.queryAACubeRelatedPropertyChanged()) {                    // due to parenting, the server may not know where something is in world-space, so include the bounding cube.                    bool success;                    AACube queryAACube = entity->getQueryAACube(success);                    if (success) {                        properties.setQueryAACube(queryAACube);                    }                }                 entity->setLastBroadcast(usecTimestampNow());                // since we're creating this object we will immediately volunteer to own its simulation                entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);                properties.setLastEdited(entity->getLastEdited());            } else {                qCDebug(entities) << "script failed to add new Entity to local Octree";                success = false;            }        });    }     return success;} QUuid EntityScriptingInterface::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) {    _activityTracking.addedEntityCount++;     EntityItemProperties properties;    properties.setType(EntityTypes::Model);    properties.setName(name);    properties.setModelURL(modelUrl);    properties.setShapeTypeFromString(shapeType);    properties.setDynamic(dynamic);    properties.setCollisionless(collisionless);    properties.setPosition(position);    properties.setGravity(gravity);    if (!textures.isEmpty()) {        properties.setTextures(textures);    }     auto nodeList = DependencyManager::get<NodeList>();    auto sessionID = nodeList->getSessionUUID();    properties.setLastEditedBy(sessionID);     return addEntity(properties);} QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) {    EntityItemID newEntityID;    EntityItemProperties properties = getEntityProperties(entityIDToClone);    bool cloneAvatarEntity = properties.getCloneAvatarEntity();    properties.convertToCloneProperties(entityIDToClone);     if (cloneAvatarEntity) {        return addEntity(properties, true);    } else {        // setLastEdited timestamp to 0 to ensure this entity gets updated with the properties         // from the server-created entity, don't change this unless you know what you are doing        properties.setLastEdited(0);        bool success = addLocalEntityCopy(properties, newEntityID, true);        if (success) {            getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID);            return newEntityID;        } else {            return QUuid();        }    }} EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {    EntityPropertyFlags noSpecificProperties;    return getEntityProperties(identity, noSpecificProperties);} EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {    PROFILE_RANGE(script_entities, __FUNCTION__);     bool scalesWithParent { false };    EntityItemProperties results;    if (_entityTree) {        _entityTree->withReadLock([&] {            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));            if (entity) {                scalesWithParent = entity->getScalesWithParent();                if (desiredProperties.getHasProperty(PROP_POSITION) ||                    desiredProperties.getHasProperty(PROP_ROTATION) ||                    desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||                    desiredProperties.getHasProperty(PROP_LOCAL_ROTATION) ||                    desiredProperties.getHasProperty(PROP_LOCAL_VELOCITY) ||                    desiredProperties.getHasProperty(PROP_LOCAL_ANGULAR_VELOCITY) ||                    desiredProperties.getHasProperty(PROP_LOCAL_DIMENSIONS)) {                    // if we are explicitly getting position or rotation, we need parent information to make sense of them.                    desiredProperties.setHasProperty(PROP_PARENT_ID);                    desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX);                }                 if (desiredProperties.isEmpty()) {                    // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation                    // don't end up in json saves, etc.  We still want them here, though.                    EncodeBitstreamParams params; // unknown                    desiredProperties = entity->getEntityProperties(params);                    desiredProperties.setHasProperty(PROP_LOCAL_POSITION);                    desiredProperties.setHasProperty(PROP_LOCAL_ROTATION);                    desiredProperties.setHasProperty(PROP_LOCAL_VELOCITY);                    desiredProperties.setHasProperty(PROP_LOCAL_ANGULAR_VELOCITY);                    desiredProperties.setHasProperty(PROP_LOCAL_DIMENSIONS);                }                 results = entity->getProperties(desiredProperties);            }        });    }     return convertPropertiesToScriptSemantics(results, scalesWithParent);} QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {    PROFILE_RANGE(script_entities, __FUNCTION__);     _activityTracking.editedEntityCount++;     auto nodeList = DependencyManager::get<NodeList>();    auto sessionID = nodeList->getSessionUUID();     EntityItemProperties properties = scriptSideProperties;    properties.setLastEditedBy(sessionID);     EntityItemID entityID(id);    if (!_entityTree) {        queueEntityMessage(PacketType::EntityEdit, entityID, properties);        return id;    }    // If we have a local entity tree set, then also update it.     bool updatedEntity = false;    _entityTree->withWriteLock([&] {        EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);        if (!entity) {            return;        }         if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) {            // don't edit other avatar's avatarEntities            return;        }         if (scriptSideProperties.parentRelatedPropertyChanged()) {            // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.            // If any of these changed, pull any missing properties from the entity.             if (!scriptSideProperties.parentIDChanged()) {                properties.setParentID(entity->getParentID());            }            if (!scriptSideProperties.parentJointIndexChanged()) {                properties.setParentJointIndex(entity->getParentJointIndex());            }            if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) {                properties.setPosition(entity->getWorldPosition());            }            if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) {                properties.setRotation(entity->getWorldOrientation());            }            if (!scriptSideProperties.localDimensionsChanged() && !scriptSideProperties.dimensionsChanged()) {                properties.setDimensions(entity->getScaledDimensions());            }        }        properties.setClientOnly(entity->getClientOnly());        properties.setOwningAvatarID(entity->getOwningAvatarID());        properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());        updatedEntity = _entityTree->updateEntity(entityID, properties);    });     // FIXME: We need to figure out a better way to handle this. Allowing these edits to go through potentially    // breaks entities that are parented.    //    // To handle cases where a script needs to edit an entity with a _known_ entity id but doesn't exist    // in the local entity tree, we need to allow those edits to go through to the server.    // if (!updatedEntity) {    //     return QUuid();    // }     bool entityFound { false };    _entityTree->withReadLock([&] {        EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);        if (entity) {            entityFound = true;            // make sure the properties has a type, so that the encode can know which properties to include            properties.setType(entity->getType());            bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();            bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges;            if (_bidOnSimulationOwnership && hasPhysicsChanges) {                auto nodeList = DependencyManager::get<NodeList>();                const QUuid myNodeID = nodeList->getSessionUUID();                 if (entity->getSimulatorID() == myNodeID) {                    // we think we already own the simulation, so make sure to send ALL TerseUpdate properties                    if (hasTerseUpdateChanges) {                        entity->getAllTerseUpdateProperties(properties);                    }                    // TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object                    // is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update                    // and instead let the physics simulation decide when to send a terse update.  This would remove                    // the "slide-no-rotate" glitch (and typical double-update) that we see during the "poke rolling                    // balls" test.  However, even if we solve this problem we still need to provide a "slerp the visible                    // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's                    // simulation.                     if (entity->getSimulationPriority() < SCRIPT_POKE_SIMULATION_PRIORITY) {                        // we re-assert our simulation ownership at a higher priority                        properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);                    }                } else {                    // we make a bid for simulation ownership                    properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);                    entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);                }            }            if (properties.queryAACubeRelatedPropertyChanged()) {

Lines 1–500 of 1889.