// // 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 CalculateEntityLoadingPriority getEntityLoadingPriorityOperator() { return _calculateEntityLoadingPriorityFunc; } 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 preUpdate(); 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 clearDomainAndNonOwnedEntities() override; virtual void clear() override; /// reloads the entity scripts, calling unload and preload void reloadEntityScripts(); void fadeOutRenderable(const EntityRendererPointer& renderable); // event handles which may generate entity related events QUuid 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 deleteEntity(const EntityItemID& id) const; void onEntityChanged(const EntityItemID& id); // Access the workload Space workload::SpacePointer getWorkloadSpace() const { return _space; } EntityEditPacketSender* getPacketSender(); static void setAddMaterialToEntityOperator(std::function addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; } static void setRemoveMaterialFromEntityOperator(std::function removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; } static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName); static bool removeMaterialFromEntity(const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName); static void setAddMaterialToAvatarOperator(std::function addMaterialToAvatarOperator) { _addMaterialToAvatarOperator = addMaterialToAvatarOperator; } static void setRemoveMaterialFromAvatarOperator(std::function removeMaterialFromAvatarOperator) { _removeMaterialFromAvatarOperator = removeMaterialFromAvatarOperator; } static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName); static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName); size_t getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; } size_t getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; } 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 = std::make_shared(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 resetPersistentEntitiesScriptEngine(); void resetNonPersistentEntitiesScriptEngine(); void setupEntityScriptEngineSignals(const ScriptEnginePointer& scriptEngine); void findBestZoneAndMaybeContainingEntities(QSet& entitiesContainingAvatar); bool applyLayeredZones(); void stopDomainAndNonOwnedEntities(); void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; QScriptValueList createEntityArgs(const EntityItemID& entityID); void checkEnterLeaveEntities(); void leaveDomainAndNonOwnedEntities(); void leaveAllEntities(); void forceRecheckEntities(); glm::vec3 _avatarPosition { 0.0f }; bool _forceRecheckEntities { true }; QSet _currentEntitiesInside; bool _wantScripts; ScriptEnginePointer _nonPersistentEntitiesScriptEngine; // used for domain + non-owned avatar entities, cleared on domain switch ScriptEnginePointer _persistentEntitiesScriptEngine; // used for local + owned avatar entities, persists on domain switch, cleared on reload content 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) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {} // We need to sort on volume AND id so that different clients sort zones with identical volumes the same way bool operator<(const LayeredZone& r) const { return volume < r.volume || (volume == r.volume && id < r.id); } bool operator==(const LayeredZone& r) const { return zone.lock() && zone.lock() == r.zone.lock(); } bool operator!=(const LayeredZone& r) const { return !(*this == r); } bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); } std::weak_ptr zone; QUuid id; float volume; }; class LayeredZones : public std::vector { public: bool clearDomainAndNonOwnedZones(); void sort() { std::sort(begin(), end(), std::less()); } bool equals(const LayeredZones& other) const; bool update(std::shared_ptr zone, const glm::vec3& position, EntityTreeRenderer* entityTreeRenderer); void appendRenderIDs(render::ItemIDs& list, EntityTreeRenderer* entityTreeRenderer) const; std::pair getZoneInteractionProperties() const; }; LayeredZones _layeredZones; uint64_t _lastZoneCheck { 0 }; const uint64_t ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const float ZONE_CHECK_DISTANCE = 0.001f; float _avgRenderableUpdateCost { 0.0f }; ReadWriteLockable _changedEntitiesGuard; std::unordered_set _changedEntities; size_t _prevNumEntityUpdates { 0 }; size_t _prevTotalNeededEntityUpdates { 0 }; std::unordered_set _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; static std::function _addMaterialToEntityOperator; static std::function _removeMaterialFromEntityOperator; static std::function _addMaterialToAvatarOperator; static std::function _removeMaterialFromAvatarOperator; }; #endif // hifi_EntityTreeRenderer_h