EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
    EntityItemProperties results;
    if (_entityTree) {
        _entityTree->withReadLock([&] {
            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
            if (entity) {
                results = entity->getProperties(desiredProperties);

                // 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);
                        results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
                    }
                }

            }
        });
    }

    return results;
}
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;
}
bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) {
    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 setPoints(entityID, [point](LineEntityItem& lineEntity) -> bool
        {
            return (LineEntityItem*)lineEntity.appendPoint(point);
        });
    }
    
    return false;
}
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
    EntityItemProperties results;
    if (_entityTree) {
        _entityTree->withReadLock([&] {
            EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
            if (entity) {
                if (desiredProperties.getHasProperty(PROP_POSITION) ||
                    desiredProperties.getHasProperty(PROP_ROTATION) ||
                    desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||
                    desiredProperties.getHasProperty(PROP_LOCAL_ROTATION)) {
                    // 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);
                 }

                results = entity->getProperties(desiredProperties);

                // 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);
                        results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
                    }
                }

            }
        });
    }

    return convertLocationToScriptSemantics(results);
}
EntityItemPointer EntityScriptingInterface::checkForTreeEntityAndTypeMatch(const QUuid& entityID,
                                                                           EntityTypes::EntityType entityType) {
    if (!_entityTree) {
        return EntityItemPointer();
    }
    
    EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
    if (!entity) {
        qDebug() << "EntityScriptingInterface::checkForTreeEntityAndTypeMatch - no entity with ID" << entityID;
        return entity;
    }
    
    if (entityType != EntityTypes::Unknown && entity->getType() != entityType) {
        return EntityItemPointer();
    }
    
    return entity;
}
glm::vec3 EntityScriptingInterface::localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords) {
    if (!_entityTree) {
        return glm::vec3(0.0f);
    }

    EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
    if (!entity) {
        qCDebug(entities) << "EntityScriptingInterface::localCoordsToVoxelCoords no entity with ID" << entityID;
        return glm::vec3(0.0f);
    }

    EntityTypes::EntityType entityType = entity->getType();
    if (entityType != EntityTypes::PolyVox) {
        return glm::vec3(0.0f);
    }

    auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
    return polyVoxEntity->localCoordsToVoxelCoords(localCoords);
}
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;
}
bool EntityScriptingInterface::setVoxels(QUuid entityID,
                                         std::function<bool(PolyVoxEntityItem&)> actor) {
    if (!_entityTree) {
        return false;
    }

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

    EntityTypes::EntityType entityType = entity->getType();
    if (entityType != EntityTypes::PolyVox) {
        return false;
    }

    auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(entity);
    bool result;
    _entityTree->withWriteLock([&] {
        result = actor(*polyVoxEntity);
    });
    return result;
}
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;
}
Beispiel #10
0
EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, const EntityItemPointer& entity, const ScenePointer& scene, Transaction& transaction) {
    EntityRenderer::Pointer result;
    if (!entity) {
        return result;
    }

    using Type = EntityTypes::EntityType_t;
    auto type = entity->getType();
    switch (type) {
        case Type::Light:
            result = make_renderer<LightEntityRenderer>(entity);
            break;

        case Type::Line:
            result = make_renderer<LineEntityRenderer>(entity);
            break;

        case Type::Model:
            result = make_renderer<ModelEntityRenderer>(entity);
            break;

        case Type::ParticleEffect:
            result = make_renderer<ParticleEffectEntityRenderer>(entity);
            break;

        case Type::PolyLine:
            result = make_renderer<PolyLineEntityRenderer>(entity);
            break;

        case Type::PolyVox:
            result = make_renderer<PolyVoxEntityRenderer>(entity);
            break;

        case Type::Shape:
        case Type::Box:
        case Type::Sphere:
            result = make_renderer<ShapeEntityRenderer>(entity);
            break;

        case Type::Text:
            result = make_renderer<TextEntityRenderer>(entity);
            break;

        case Type::Web:
            if (!nsightActive()) {
                result = make_renderer<WebEntityRenderer>(entity);
            }
            break;

        case Type::Zone:
            result = make_renderer<ZoneEntityRenderer>(entity);
            break;

        case Type::Material:
            result = make_renderer<MaterialEntityRenderer>(entity);
            break;

        default:
            break;
    }

    if (result) {
        result->addToScene(scene, transaction);
    }

    return result;
}