// // EntityTreeRenderer.h // interface/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 // #ifndef hifi_EntityTreeRenderer_h #define hifi_EntityTreeRenderer_h #include #include #include #include #include // for RayToEntityIntersectionResult #include #include #include #include #include #include #include class AbstractScriptingServicesInterface; class AbstractViewStateInterface; class Model; class ScriptEngine; class ZoneEntityItem; class EntityItem; namespace render { namespace entities { class EntityRenderer; using EntityRendererPointer = std::shared_ptr; using EntityRendererWeakPointer = std::weak_ptr; } } using EntityRenderer = render::entities::EntityRenderer; using EntityRendererPointer = render::entities::EntityRendererPointer; using EntityRendererWeakPointer = render::entities::EntityRendererWeakPointer; class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; using CalculateEntityLoadingPriority = std::function; // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeProcessor, public Dependency { Q_OBJECT public: static void setEntitiesShouldFadeFunction(std::function func) { _entitiesShouldFadeFunction = func; } static std::function getEntitiesShouldFadeFunction() { return _entitiesShouldFadeFunction; } EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices); virtual ~EntityTreeRenderer(); QSharedPointer getSharedFromThis() { return qSharedPointerCast(sharedFromThis()); } virtual char getMyNodeType() const override { return NodeType::EntityServer; } virtual PacketType getMyQueryMessageType() const override { return PacketType::EntityQuery; } virtual PacketType getExpectedPacketType() const override { return PacketType::EntityData; } // Returns the priority at which an entity should be loaded. Higher values indicate higher priority. static float getEntityLoadingPriority(const EntityItem& item) { return _calculateEntityLoadingPriorityFunc(item); } static void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { _calculateEntityLoadingPriorityFunc = fn; } void setMouseRayPickID(unsigned int rayPickID) { _mouseRayPickID = rayPickID; } unsigned int getMouseRayPickID() { return _mouseRayPickID; } void setMouseRayPickResultOperator(std::function getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; } void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; } void shutdown(); void update(bool simulate); EntityTreePointer getTree() { return std::static_pointer_cast(_tree); } void processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); virtual void init() override; /// clears the tree virtual void clearNonLocalEntities() override; virtual void clear() override; /// reloads the entity scripts, calling unload and preload void reloadEntityScripts(); // event handles which may generate entity related events std::pair mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseDoublePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); /// connect our signals to anEntityScriptingInterface for firing of events related clicking, /// hovering over, and entering entities void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface); // For Scene.shouldRenderEntities QList& getEntitiesLastInScene() { return _entityIDsLastInScene; } std::pair getZoneInteractionProperties(); bool wantsKeyboardFocus(const EntityItemID& id) const; QObject* getEventHandler(const EntityItemID& id); bool wantsHandControllerPointerEvents(const EntityItemID& id) const; void setProxyWindow(const EntityItemID& id, QWindow* proxyWindow); void setCollisionSound(const EntityItemID& id, const SharedSoundPointer& sound); EntityItemPointer getEntity(const EntityItemID& id); void onEntityChanged(const EntityItemID& id); // Access the workload Space workload::SpacePointer getWorkloadSpace() const { return _space; } EntityEditPacketSender* getPacketSender(); signals: void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); public slots: void addingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID); void entityScriptChanging(const EntityItemID& entityID, const bool reload); void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void updateEntityRenderStatus(bool shouldRenderEntities); void updateZone(const EntityItemID& id); // optional slots that can be wired to menu items void setDisplayModelBounds(bool value) { _displayModelBounds = value; } void setPrecisionPicking(bool value) { _setPrecisionPickingOperator(_mouseRayPickID, value); } EntityRendererPointer renderableForEntityId(const EntityItemID& id) const; render::ItemID renderableIdForEntityId(const EntityItemID& id) const; void handleSpaceUpdate(std::pair proxyUpdate); protected: virtual OctreePointer createTree() override { EntityTreePointer newTree = EntityTreePointer(new EntityTree(true)); newTree->createRootElement(); return newTree; } private: void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction); void updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction); EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); } render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); } void resetEntitiesScriptEngine(); bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); bool applyLayeredZones(); void stopNonLocalEntityScripts(); void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; QScriptValueList createEntityArgs(const EntityItemID& entityID); bool checkEnterLeaveEntities(); void leaveNonLocalEntities(); void leaveAllEntities(); void forceRecheckEntities(); glm::vec3 _avatarPosition { 0.0f }; QVector _currentEntitiesInside; bool _wantScripts; ScriptEnginePointer _entitiesScriptEngine; void playEntityCollisionSound(const EntityItemPointer& entity, const Collision& collision); bool _lastPointerEventValid; PointerEvent _lastPointerEvent; AbstractViewStateInterface* _viewState; AbstractScriptingServicesInterface* _scriptingServices; bool _displayModelBounds; bool _shuttingDown { false }; QMultiMap _waitingOnPreload; unsigned int _mouseRayPickID; std::function _getPrevRayPickResultOperator; std::function _setPrecisionPickingOperator; class LayeredZone { public: LayeredZone(std::shared_ptr zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {} LayeredZone(std::shared_ptr zone) : LayeredZone(zone, zone->getID(), zone->getVolumeEstimate()) {} bool operator<(const LayeredZone& r) const { return std::tie(volume, id) < std::tie(r.volume, r.id); } bool operator==(const LayeredZone& r) const { return id == r.id; } bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); } std::shared_ptr zone; QUuid id; float volume; }; class LayeredZones : public std::set { public: LayeredZones() {}; LayeredZones(LayeredZones&& other); // avoid accidental misconstruction LayeredZones(const LayeredZones&) = delete; LayeredZones& operator=(const LayeredZones&) = delete; LayeredZones& operator=(LayeredZones&&) = delete; void clear(); void clearNonLocalLayeredZones(); std::pair insert(const LayeredZone& layer); void update(std::shared_ptr zone); bool contains(const LayeredZones& other); std::shared_ptr getZone() { return empty() ? nullptr : begin()->zone; } private: std::map _map; iterator _skyboxLayer { end() }; }; LayeredZones _layeredZones; float _avgRenderableUpdateCost { 0.0f }; uint64_t _lastZoneCheck { 0 }; const uint64_t ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const float ZONE_CHECK_DISTANCE = 0.001f; ReadWriteLockable _changedEntitiesGuard; std::unordered_set _changedEntities; std::unordered_map _renderablesToUpdate; std::unordered_map _entitiesInScene; std::unordered_map _entitiesToAdd; // For Scene.shouldRenderEntities QList _entityIDsLastInScene; static int _entitiesScriptEngineCount; static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc; static std::function _entitiesShouldFadeFunction; mutable std::mutex _spaceLock; workload::SpacePointer _space{ new workload::Space() }; workload::Transaction::Updates _spaceUpdates; }; #endif // hifi_EntityTreeRenderer_h