QUuid EntityScriptingInterface::editEntity(QUuid id, EntityItemProperties properties) {
    EntityItemID entityID(id);
    // If we have a local entity tree set, then also update it.
    if (!_entityTree) {
        queueEntityMessage(PacketType::EntityEdit, entityID, properties);
        return id;
    }

    bool updatedEntity = false;
    _entityTree->withWriteLock([&] {
        updatedEntity = _entityTree->updateEntity(entityID, properties);
    });


    if (!updatedEntity) {
        return QUuid();
    }

    _entityTree->withReadLock([&] {
        EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
        if (entity) {
            // 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 (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 a 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_EDIT_SIMULATION_PRIORITY) {
                        // we re-assert our simulation ownership at a higher priority
                        properties.setSimulationOwner(myNodeID,
                            glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
                    }
                } else {
                    // we make a bid for simulation ownership
                    properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
                    entity->flagForOwnership();
                }
            }
            entity->setLastBroadcast(usecTimestampNow());
        }
    });
    queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    return id;
}
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {

    EntityItemProperties propertiesWithSimID = properties;

    EntityItemID id = EntityItemID(QUuid::createUuid());

    // If we have a local entity tree set, then also update it.
    bool success = true;
    if (_entityTree) {
        _entityTree->withWriteLock([&] {
            EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
            if (entity) {
                // This Node is creating a new object.  If it's in motion, set this Node as the simulator.
                auto nodeList = DependencyManager::get<NodeList>();
                const QUuid myNodeID = nodeList->getSessionUUID();
                propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);

                // and make note of it now, so we can act on it right away.
                entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);

                entity->setLastBroadcast(usecTimestampNow());
            } else {
                qCDebug(entities) << "script failed to add new Entity to local Octree";
                success = false;
            }
        });
    }

    // queue the packet
    if (success) {
        queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
    }

    return id;
}
Exemplo n.º 3
0
bool EntityScriptingInterface::setAbsoluteJointTranslationsInObjectFrame(const QUuid& entityID,
                                                                         const QVector<glm::vec3>& translations) {
    if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
        auto now = usecTimestampNow();
        auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);

        bool result = false;
        for (int index = 0; index < translations.size(); index++) {
            result |= modelEntity->setAbsoluteJointTranslationInObjectFrame(index, translations[index]);
        }
        if (result) {
            EntityItemProperties properties;
            _entityTree->withWriteLock([&] {
                entity->setLastEdited(now);
                entity->setLastBroadcast(now);
                properties = entity->getProperties();
            });

            properties.setJointTranslationsDirty();
            properties.setLastEdited(now);
            queueEntityMessage(PacketType::EntityEdit, entityID, properties);
            return true;
        }
    }
    return false;
}
Exemplo n.º 4
0
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
    EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties);
    propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());

    auto dimensions = propertiesWithSimID.getDimensions();
    float volume = dimensions.x * dimensions.y * dimensions.z;
    auto density = propertiesWithSimID.getDensity();
    auto newVelocity = propertiesWithSimID.getVelocity().length();
    float cost = calculateCost(density * volume, 0, newVelocity);
    cost *= costMultiplier;

    if (cost > _currentAvatarEnergy) {
        return QUuid();
    }

    EntityItemID id = EntityItemID(QUuid::createUuid());

    // If we have a local entity tree set, then also update it.
    bool success = true;
    if (_entityTree) {
        _entityTree->withWriteLock([&] {
            EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
            if (entity) {
                if (propertiesWithSimID.parentRelatedPropertyChanged()) {
                    // 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) {
                        propertiesWithSimID.setQueryAACube(queryAACube);
                    }
                }

                if (_bidOnSimulationOwnership) {
                    // This Node is creating a new object.  If it's in motion, set this Node as the simulator.
                    auto nodeList = DependencyManager::get<NodeList>();
                    const QUuid myNodeID = nodeList->getSessionUUID();

                    // and make note of it now, so we can act on it right away.
                    propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
                    entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
                }

                entity->setLastBroadcast(usecTimestampNow());
            } else {
                qCDebug(entities) << "script failed to add new Entity to local Octree";
                success = false;
            }
        });
    }

    // queue the packet
    if (success) {
        emit debitEnergySource(cost);
        queueEntityMessage(PacketType::EntityAdd, id, propertiesWithSimID);
    }

    return id;
}
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;
        }
    }
    
    // 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, properties);
        _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 (properties.getType() == EntityTypes::Unknown) {
            EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
            if (entity) {
                EntityItemProperties tempProperties = properties;
                tempProperties.setType(entity->getType());
                queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, tempProperties);
                return entityID;
            }
        }
        
        // if the properties already includes the type, then use it as is
        queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
    }
    
    return entityID;
}
EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {

    // The application will keep track of creatorTokenID
    uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID();

    EntityItemID id(NEW_ENTITY, creatorTokenID, false );

    // If we have a local entity tree set, then also update it.
    if (_entityTree) {
        _entityTree->lockForWrite();
        _entityTree->addEntity(id, properties);
        _entityTree->unlock();
    }

    // queue the packet
    queueEntityMessage(PacketTypeEntityAddOrEdit, id, properties);

    return id;
}
bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
                                            std::function<bool(EntitySimulation*, EntityItemPointer)> actor) {
    if (!_entityTree) {
        return false;
    }

    EntityItemPointer entity;
    bool doTransmit = false;
    _entityTree->withWriteLock([&] {
        EntitySimulation* simulation = _entityTree->getSimulation();
        entity = _entityTree->findEntityByEntityItemID(entityID);
        if (!entity) {
            qDebug() << "actionWorker -- unknown entity" << entityID;
            return;
        }

        if (!simulation) {
            qDebug() << "actionWorker -- no simulation" << entityID;
            return;
        }

        doTransmit = actor(simulation, entity);
        if (doTransmit) {
            _entityTree->entityChanged(entity);
        }
    });

    // transmit the change
    if (doTransmit) {
        EntityItemProperties properties;
        _entityTree->withReadLock([&] {
            properties = entity->getProperties();
        });

        properties.setActionDataDirty();
        auto now = usecTimestampNow();
        properties.setLastEdited(now);
        queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    }

    return doTransmit;
}
Exemplo n.º 8
0
bool EntityScriptingInterface::setAbsoluteJointRotationInObjectFrame(const QUuid& entityID,
                                                                     int jointIndex, glm::quat rotation) {
    if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
        auto now = usecTimestampNow();
        auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
        bool result = modelEntity->setAbsoluteJointRotationInObjectFrame(jointIndex, rotation);
        if (result) {
            EntityItemProperties properties;
            _entityTree->withWriteLock([&] {
                properties = entity->getProperties();
                entity->setLastBroadcast(now);
            });

            properties.setJointRotationsDirty();
            properties.setLastEdited(now);
            queueEntityMessage(PacketType::EntityEdit, entityID, properties);
            return true;
        }
    }
    return false;
}
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor) {
    if (!_entityTree) {
        return false;
    }

    EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
    if (!entity) {
        qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
    }

    EntityTypes::EntityType entityType = entity->getType();

    if (entityType != EntityTypes::Line) {
        return false;
    }

    auto now = usecTimestampNow();

    auto lineEntity = std::static_pointer_cast<LineEntityItem>(entity);
    bool success;
    _entityTree->withWriteLock([&] {
        success = actor(*lineEntity);
        entity->setLastEdited(now);
        entity->setLastBroadcast(now);
    });

    EntityItemProperties properties;
    _entityTree->withReadLock([&] {
        properties = entity->getProperties();
    });

    properties.setLinePointsDirty();
    properties.setLastEdited(now);

    queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    return success;
}
Exemplo n.º 10
0
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
    EntityItemProperties properties = scriptSideProperties;

    auto dimensions = properties.getDimensions();
    float volume = dimensions.x * dimensions.y * dimensions.z;
    auto density = properties.getDensity();
    auto newVelocity = properties.getVelocity().length();
    float oldVelocity = { 0.0f };

    EntityItemID entityID(id);
    if (!_entityTree) {
        queueEntityMessage(PacketType::EntityEdit, entityID, properties);

        //if there is no local entity entity tree, no existing velocity, use 0.
        float cost = calculateCost(density * volume, oldVelocity, newVelocity);
        cost *= costMultiplier;

        if (cost > _currentAvatarEnergy) {
            return QUuid();
        } else {
            //debit the avatar energy and continue
            emit debitEnergySource(cost);
        }

        return id;
    }
    // If we have a local entity tree set, then also update it.

    bool updatedEntity = false;
    _entityTree->withWriteLock([&] {
        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.
            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
            if (!entity) {
                return;
            }
            //existing entity, retrieve old velocity for check down below
            oldVelocity = entity->getVelocity().length();

            if (!scriptSideProperties.parentIDChanged()) {
                properties.setParentID(entity->getParentID());
            }
            if (!scriptSideProperties.parentJointIndexChanged()) {
                properties.setParentJointIndex(entity->getParentJointIndex());
            }
            if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) {
                properties.setPosition(entity->getPosition());
            }
            if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) {
                properties.setRotation(entity->getOrientation());
            }
        }
        properties = convertLocationFromScriptSemantics(properties);

        float cost = calculateCost(density * volume, oldVelocity, newVelocity);
        cost *= costMultiplier;

        if (cost > _currentAvatarEnergy) {
            updatedEntity = false;
        } else {
            //debit the avatar energy and continue
            updatedEntity = _entityTree->updateEntity(entityID, properties);
            if (updatedEntity) {
                emit debitEnergySource(cost);
            }
        }
    });

    if (!updatedEntity) {
        return QUuid();
    }

    _entityTree->withReadLock([&] {
        EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
        if (entity) {
            // 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->pokeSimulationOwnership();
                }
            }
            if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
                properties.setQueryAACube(entity->getQueryAACube());
            }
            entity->setLastBroadcast(usecTimestampNow());

            // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
            // if they've changed.
            entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
                if (descendant->getNestableType() == NestableType::Entity) {
                    if (descendant->computePuffedQueryAACube()) {
                        EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
                        EntityItemProperties newQueryCubeProperties;
                        newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
                        newQueryCubeProperties.setLastEdited(properties.getLastEdited());
                        queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties);
                        entityDescendant->setLastBroadcast(usecTimestampNow());
                    }
                }
            });
        }
    });
    queueEntityMessage(PacketType::EntityEdit, entityID, properties);
    return id;
}