413 lines · 14.8 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 <VariantMapToScriptValue.h> #include "EntityScriptingInterface.h"#include "EntityTree.h"#include "LightEntityItem.h"#include "ModelEntityItem.h"  EntityScriptingInterface::EntityScriptingInterface() :    _nextCreatorTokenID(0),    _entityTree(NULL){    auto nodeList = DependencyManager::get<NodeList>();    connect(nodeList.data(), &NodeList::canAdjustLocksChanged, this, &EntityScriptingInterface::canAdjustLocksChanged);    connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);} void EntityScriptingInterface::queueEntityMessage(PacketType packetType,        EntityItemID entityID, const EntityItemProperties& properties) {    getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);} bool EntityScriptingInterface::canAdjustLocks() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanAdjustLocks();} bool EntityScriptingInterface::canRez() {    auto nodeList = DependencyManager::get<NodeList>();    return nodeList->getThisNodeCanRez();} void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {    if (_entityTree) {        disconnect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);        disconnect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);        disconnect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);        disconnect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);    }     _entityTree = modelTree;     if (_entityTree) {        connect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);        connect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);        connect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);        connect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);    }}   void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) {    auto nodeList = DependencyManager::get<NodeList>();    const QUuid myNodeID = nodeList->getSessionUUID();     // if this entity has non-zero physics/simulation related values, claim simulation ownership    if (propertiesWithSimID.velocityChanged() ||        propertiesWithSimID.rotationChanged() ||        propertiesWithSimID.containsPositionChange()) {        propertiesWithSimID.setSimulatorID(myNodeID);        entity->setSimulatorID(myNodeID);    } else if (entity->getSimulatorID() == myNodeID) {        propertiesWithSimID.setSimulatorID(QUuid()); // give up simulation ownership        entity->setSimulatorID(QUuid());    }}   EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {     // The application will keep track of creatorTokenID    uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID();     EntityItemProperties propertiesWithSimID = properties;     EntityItemID id(NEW_ENTITY, creatorTokenID, false );     // If we have a local entity tree set, then also update it.    if (_entityTree) {        _entityTree->lockForWrite();        EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID);        // This Node is creating a new object.  If it's in motion, set this Node as the simulator.        setSimId(propertiesWithSimID, entity);        _entityTree->unlock();    }     // queue the packet    queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID);     return id;} EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {    EntityItemID actualID = entityID;     if (!entityID.isKnownID) {        actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);        if (actualID == UNKNOWN_ENTITY_ID) {            return entityID; // bailing early        }                // found it!        entityID.id = actualID.id;        entityID.isKnownID = true;    }    return entityID;} EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID entityID) {    EntityItemProperties results;    EntityItemID identity = identifyEntity(entityID);    if (_entityTree) {        _entityTree->lockForRead();        EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));                if (entity) {            results = entity->getProperties();             // TODO: improve sitting points and naturalDimensions in the future,             //       for now we've included the old sitting points model behavior for entity types that are models            //        we've also added this hack for setting natural dimensions of models            if (entity->getType() == EntityTypes::Model) {                const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);                if (geometry) {                    results.setSittingPoints(geometry->sittingPoints);                    Extents meshExtents = geometry->getUnscaledMeshExtents();                    results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);                }            }         } else {            results.setIsUnknownID();        }        _entityTree->unlock();    }        return results;} EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) {    EntityItemID actualID = entityID;    // if the entity is unknown, attempt to look it up    if (!entityID.isKnownID) {        actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);        if (actualID.id != UNKNOWN_ENTITY_ID) {            entityID.id = actualID.id;            entityID.isKnownID = true;        }    }     EntityItemProperties propertiesWithSimID = properties;     // If we have a local entity tree set, then also update it. We can do this even if we don't know    // the actual id, because we can edit out local entities just with creatorTokenID    if (_entityTree) {        _entityTree->lockForWrite();        _entityTree->updateEntity(entityID, propertiesWithSimID, canAdjustLocks());        _entityTree->unlock();    }     // if at this point, we know the id, send the update to the entity server    if (entityID.isKnownID) {        // make sure the properties has a type, so that the encode can know which properties to include        if (propertiesWithSimID.getType() == EntityTypes::Unknown) {            EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);            if (entity) {                propertiesWithSimID.setType(entity->getType());                setSimId(propertiesWithSimID, entity);            }        }         queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, propertiesWithSimID);    }        return entityID;} void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {     EntityItemID actualID = entityID;        // if the entity is unknown, attempt to look it up    if (!entityID.isKnownID) {        actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);        if (actualID.id != UNKNOWN_ENTITY_ID) {            entityID.id = actualID.id;            entityID.isKnownID = true;        }    }     bool shouldDelete = true;     // If we have a local entity tree set, then also update it.    if (_entityTree) {        _entityTree->lockForWrite();         EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(actualID));        if (entity) {            if (entity->getLocked()) {                shouldDelete = false;            } else {                _entityTree->deleteEntity(entityID);            }        }         _entityTree->unlock();    }     // if at this point, we know the id, and we should still delete the entity, send the update to the entity server    if (shouldDelete && entityID.isKnownID) {        getEntityPacketSender()->queueEraseEntityMessage(entityID);    }} EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {    EntityItemID result(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false);    if (_entityTree) {        _entityTree->lockForRead();        const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);        _entityTree->unlock();        if (closestEntity) {            result = closestEntity->getEntityItemID();        }    }    return result;}  void EntityScriptingInterface::dumpTree() const {    if (_entityTree) {        _entityTree->lockForRead();        _entityTree->dumpTree();        _entityTree->unlock();    }} QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {    QVector<EntityItemID> result;    if (_entityTree) {        _entityTree->lockForRead();        QVector<const EntityItem*> entities;        _entityTree->findEntities(center, radius, entities);        _entityTree->unlock();                foreach (const EntityItem* entity, entities) {            result << entity->getEntityItemID();        }    }    return result;} QVector<EntityItemID> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {    QVector<EntityItemID> result;    if (_entityTree) {        _entityTree->lockForRead();        AABox box(corner, dimensions);        QVector<EntityItem*> entities;        _entityTree->findEntities(box, entities);        _entityTree->unlock();                foreach (const EntityItem* entity, entities) {            result << entity->getEntityItemID();        }    }    return result;} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) {    return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) {    return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,                                                                                     Octree::lockType lockType,                                                                                     bool precisionPicking) {      RayToEntityIntersectionResult result;    if (_entityTree) {        OctreeElement* element;        EntityItem* intersectedEntity = NULL;        result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,                                                                 (void**)&intersectedEntity, lockType, &result.accurate,                                                                 precisionPicking);        if (result.intersects && intersectedEntity) {            result.entityID = intersectedEntity->getEntityItemID();            result.properties = intersectedEntity->getProperties();            result.intersection = ray.origin + (ray.direction * result.distance);        }    }    return result;} void EntityScriptingInterface::setLightsArePickable(bool value) {    LightEntityItem::setLightsArePickable(value);} bool EntityScriptingInterface::getLightsArePickable() const {    return LightEntityItem::getLightsArePickable();} void EntityScriptingInterface::setSendPhysicsUpdates(bool value) {    EntityItem::setSendPhysicsUpdates(value);} bool EntityScriptingInterface::getSendPhysicsUpdates() const {    return EntityItem::getSendPhysicsUpdates();}  RayToEntityIntersectionResult::RayToEntityIntersectionResult() :     intersects(false),     accurate(true), // assume it's accurate    entityID(),    properties(),    distance(0),    face(),    entity(NULL){ } QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {    QScriptValue obj = engine->newObject();    obj.setProperty("intersects", value.intersects);    obj.setProperty("accurate", value.accurate);    QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID);    obj.setProperty("entityID", entityItemValue);     QScriptValue propertiesValue = EntityItemPropertiesToScriptValue(engine, value.properties);    obj.setProperty("properties", propertiesValue);     obj.setProperty("distance", value.distance);     QString faceName = "";        // handle BoxFace    switch (value.face) {        case MIN_X_FACE:            faceName = "MIN_X_FACE";            break;        case MAX_X_FACE:            faceName = "MAX_X_FACE";            break;        case MIN_Y_FACE:            faceName = "MIN_Y_FACE";            break;        case MAX_Y_FACE:            faceName = "MAX_Y_FACE";            break;        case MIN_Z_FACE:            faceName = "MIN_Z_FACE";            break;        case MAX_Z_FACE:            faceName = "MAX_Z_FACE";            break;        case UNKNOWN_FACE:            faceName = "UNKNOWN_FACE";            break;    }    obj.setProperty("face", faceName);     QScriptValue intersection = vec3toScriptValue(engine, value.intersection);    obj.setProperty("intersection", intersection);    return obj;} void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) {    value.intersects = object.property("intersects").toVariant().toBool();    value.accurate = object.property("accurate").toVariant().toBool();    QScriptValue entityIDValue = object.property("entityID");    if (entityIDValue.isValid()) {        EntityItemIDfromScriptValue(entityIDValue, value.entityID);    }    QScriptValue entityPropertiesValue = object.property("properties");    if (entityPropertiesValue.isValid()) {        EntityItemPropertiesFromScriptValue(entityPropertiesValue, value.properties);    }    value.distance = object.property("distance").toVariant().toFloat();     QString faceName = object.property("face").toVariant().toString();    if (faceName == "MIN_X_FACE") {        value.face = MIN_X_FACE;    } else if (faceName == "MAX_X_FACE") {        value.face = MAX_X_FACE;    } else if (faceName == "MIN_Y_FACE") {        value.face = MIN_Y_FACE;    } else if (faceName == "MAX_Y_FACE") {        value.face = MAX_Y_FACE;    } else if (faceName == "MIN_Z_FACE") {        value.face = MIN_Z_FACE;    } else {        value.face = MAX_Z_FACE;    };    QScriptValue intersection = object.property("intersection");    if (intersection.isValid()) {        vec3FromScriptValue(intersection, value.intersection);    }}