diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d12c9dae3c..d5e2b24f36 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -304,7 +304,8 @@ SelectionDisplay = (function () { visible: false, dashed: true, lineWidth: 2.0, - ignoreRayIntersection: true // this never ray intersects + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true }); var selectionBox = Overlays.addOverlay("cube", { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1a808bc15c..0264c6e3c0 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -123,6 +123,7 @@ protected: namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay); + template <> int payloadGetLayer(const Overlay::Pointer& overlay); template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args); } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index cf3262d05c..d5e4b34f6b 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -37,7 +37,7 @@ namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { if (overlay->is3D() && !static_cast(overlay.get())->getDrawOnHUD()) { if (static_cast(overlay.get())->getDrawInFront()) { - return ItemKey::Builder().withTypeShape().withNoDepthSort().build(); + return ItemKey::Builder().withTypeShape().withLayered().build(); } else { return ItemKey::Builder::opaqueShape(); } @@ -53,6 +53,17 @@ namespace render { return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f)); } } + template <> int payloadGetLayer(const Overlay::Pointer& overlay) { + // MAgic number while we are defining the layering mechanism: + const int LAYER_2D = 2; + const int LAYER_3D_FRONT = 1; + const int LAYER_3D = 0; + if (overlay->is3D()) { + return (static_cast(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D); + } else { + return LAYER_2D; + } + } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { glPushMatrix(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 777d9466a5..3fd7d05666 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); + _jobs.push_back(Job(DrawPostLayered())); _jobs.push_back(Job(ResetGLState())); } @@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex // render opaques auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; @@ -163,7 +164,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC // render transparents auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index bfc888ea8a..53964ac1db 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } } - void addClearStateCommands(gpu::Batch& batch) { batch._glDepthMask(true); batch._glDepthFunc(GL_LESS); @@ -446,6 +445,48 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi args->_context->syncCache(); } +template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawPostLayered"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() > 0)) { + inItems.emplace_back(id); + } + } + if (inItems.empty()) { + return; + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + + renderItems(sceneContext, renderContext, inItems); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + + // Force the context sync + args->_context->syncCache(); +} void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 1f260583f2..0052c314c0 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -87,6 +87,14 @@ public: }; template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class DrawPostLayered { +public: +}; +template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + + class ResetGLState { public: }; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 8615f7cf7a..1d2e54541b 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey } void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { - (*this)[ItemFilter::Builder::opaqueShape()]; - (*this)[ItemFilter::Builder::transparentShape()]; + (*this)[ItemFilter::Builder::opaqueShape().withoutLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withoutLayered()]; (*this)[ItemFilter::Builder::light()]; (*this)[ItemFilter::Builder::background()]; + (*this)[ItemFilter::Builder::opaqueShape().withLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } @@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) { } } -void Item::kill() { - _payload.reset(); - _key._flags.reset(); -} - void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); @@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { _items[(*updateID)].update((*updateFunctor)); } } - -void Scene::registerObserver(ObserverPointer& observer) { - // make sure it's a valid observer - if (observer && (observer->getScene() == nullptr)) { - // Then register the observer - _observers.push_back(observer); - - // And let it do what it wants to do - observer->registerScene(this); - } -} - -void Scene::unregisterObserver(ObserverPointer& observer) { - // make sure it's a valid observer currently registered - if (observer && (observer->getScene() == this)) { - // let it do what it wants to do - observer->unregisterScene(); - - // Then unregister the observer - auto it = std::find(_observers.begin(), _observers.end(), observer); - _observers.erase(it); - } -} - diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index b9481d367e..5ec9f0c951 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -36,7 +36,6 @@ public: enum FlagBit { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light - TYPE_BACKGROUND, // Item is a Background TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item @@ -44,7 +43,7 @@ public: INVISIBLE, // Visible or not? could be just here to cast shadow SHADOW_CASTER, // Item cast shadows PICKABLE, // Item can be picked/selected - NO_DEPTH_SORT, // Item should not be depth sorted + LAYERED, // Item belongs to one of the layers different from the default layer NUM_FLAGS, // Not a valid flag }; @@ -57,6 +56,7 @@ public: ItemKey(const Flags& flags) : _flags(flags) {} class Builder { + friend class ItemKey; Flags _flags{ 0 }; public: Builder() {} @@ -65,7 +65,6 @@ public: Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } @@ -73,14 +72,15 @@ public: Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withPickable() { _flags.set(PICKABLE); return (*this); } - Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); } + Builder& withLayered() { _flags.set(LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemKey opaqueShape() { return Builder().withTypeShape().build(); } - static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); } - static ItemKey light() { return Builder().withTypeLight().build(); } - static ItemKey background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemKey(const Builder& builder) : ItemKey(builder._flags) {} bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; } @@ -101,8 +101,7 @@ public: bool isPickable() const { return _flags[PICKABLE]; } - bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; } - bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; } + bool isLayered() const { return _flags[LAYERED]; } }; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { @@ -122,6 +121,7 @@ public: ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} class Builder { + friend class ItemFilter; ItemKey::Flags _value{ 0 }; ItemKey::Flags _mask{ 0 }; public: @@ -131,7 +131,6 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); } Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } @@ -153,16 +152,18 @@ public: Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - Builder& withDepthSort() { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } - Builder& withNotDepthSort() { _value.set(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } + Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); } - static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); } - static ItemFilter light() { return Builder().withTypeLight().build(); } - static ItemFilter background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} + // Item Filter operator testing if a key pass the filter bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } @@ -179,7 +180,7 @@ public: }; inline QDebug operator<<(QDebug debug, const ItemFilter& me) { - debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape()) + debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build()) << "]"; return debug; } @@ -214,35 +215,40 @@ public: public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound() const = 0; - virtual void render(RenderArgs* args) = 0; + virtual int getLayer() const = 0; - virtual void update(const UpdateFunctorPointer& functor) = 0; + virtual void render(RenderArgs* args) = 0; virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} protected: + friend class Item; + virtual void update(const UpdateFunctorPointer& functor) = 0; }; - - - typedef std::shared_ptr PayloadPointer; - - Item() {} ~Item() {} + // Main scene / item managment interface Reset/Update/Kill void resetPayload(const PayloadPointer& payload); - void kill(); + void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload + void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key // Check heuristic key const ItemKey& getKey() const { return _key; } // Payload Interface + + // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) const Bound getBound() const { return _payload->getBound(); } + + // Get the layer where the item belongs. 0 by default meaning NOT LAYERED + int getLayer() const { return _payload->getLayer(); } + + // Render call for the item void render(RenderArgs* args) { _payload->render(args); } - void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Shape Type Interface const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } @@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) { // of the Payload interface template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } +template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } // Shape type interface @@ -290,19 +297,25 @@ public: typedef std::shared_ptr DataPointer; typedef UpdateFunctor Updater; - virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + Payload(const DataPointer& data) : _data(data) {} // Payload general interface virtual const ItemKey getKey() const { return payloadGetKey(_data); } virtual const Item::Bound getBound() const { return payloadGetBound(_data); } + virtual int getLayer() const { return payloadGetLayer(_data); } + + virtual void render(RenderArgs* args) { payloadRender(_data, args); } // Shape Type interface virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey(_data); } - Payload(const DataPointer& data) : _data(data) {} protected: DataPointer _data; + + // Update mechanics + virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + friend class Item; }; // Let's show how to make a simple FooPayload example: @@ -358,6 +371,7 @@ public: typedef std::vector< ItemIDAndBounds > ItemIDsBounds; + // A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly class ItemBucketMap : public std::map { public: @@ -374,8 +388,6 @@ public: }; class Engine; -class Observer; - class PendingChanges { public: @@ -411,36 +423,6 @@ typedef std::queue PendingChangesQueue; // Items are notified accordingly on any update message happening class Scene { public: - - class Observer { - public: - Observer(Scene* scene) { - - } - ~Observer() {} - - const Scene* getScene() const { return _scene; } - Scene* editScene() { return _scene; } - - protected: - Scene* _scene = nullptr; - virtual void onRegisterScene() {} - virtual void onUnregisterScene() {} - - friend class Scene; - void registerScene(Scene* scene) { - _scene = scene; - onRegisterScene(); - } - - void unregisterScene() { - onUnregisterScene(); - _scene = 0; - } - }; - typedef std::shared_ptr< Observer > ObserverPointer; - typedef std::vector< ObserverPointer > Observers; - Scene(); ~Scene() {} @@ -450,11 +432,6 @@ public: /// Enqueue change batch to the scene void enqueuePendingChanges(const PendingChanges& pendingChanges); - /// Scene Observer listen to any change and get notified - void registerObserver(ObserverPointer& observer); - void unregisterObserver(ObserverPointer& observer); - - /// Access the main bucketmap of items const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } @@ -483,10 +460,6 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); - - // The scene context listening for any change to the database - Observers _observers; - friend class Engine; };