From 7685fe2229ff876fa0a3cfe296f45a38f1d1d704 Mon Sep 17 00:00:00 2001
From: Sam Gateau <sam@highfidelity.io>
Date: Thu, 11 Jun 2015 06:40:21 -0700
Subject: [PATCH] Clean up on the item interface and introduction of the
 Layered concept, fixing the highliting box of the edit tool

---
 examples/libraries/entitySelectionTool.js     |   3 +-
 interface/src/ui/overlays/Overlay.h           |   1 +
 interface/src/ui/overlays/OverlaysPayload.cpp |  13 +-
 .../render-utils/src/RenderDeferredTask.cpp   |   5 +-
 libraries/render/src/render/DrawTask.cpp      |  43 ++++++-
 libraries/render/src/render/DrawTask.h        |   8 ++
 libraries/render/src/render/Scene.cpp         |  35 +-----
 libraries/render/src/render/Scene.h           | 113 +++++++-----------
 8 files changed, 115 insertions(+), 106 deletions(-)

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<Base3DOverlay*>(overlay.get())->getDrawOnHUD()) {
             if (static_cast<Base3DOverlay*>(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<Base3DOverlay*>(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 8cb29609ba..5568312dfe 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<PayloadInterface> 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 <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
 template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
+template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
 template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
     
 // Shape type interface
@@ -290,19 +297,25 @@ public:
     typedef std::shared_ptr<T> DataPointer;
     typedef UpdateFunctor<T> Updater;
 
-    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(functor.get())->_func((*_data)); }
+    Payload(const DataPointer& data) : _data(data) {}
 
     // Payload general interface
     virtual const ItemKey getKey() const { return payloadGetKey<T>(_data); }
     virtual const Item::Bound getBound() const { return payloadGetBound<T>(_data); }
+    virtual int getLayer() const { return payloadGetLayer<T>(_data); }
+
+
     virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); } 
 
     // Shape Type interface
     virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); }
 
-    Payload(const DataPointer& data) : _data(data) {}
 protected:
     DataPointer _data;
+
+    // Update mechanics
+    virtual void update(const UpdateFunctorPointer& functor) { static_cast<Updater*>(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<ItemFilter, ItemIDSet, ItemFilter::Less> {
 public:
@@ -374,8 +388,6 @@ public:
 };
 
 class Engine;
-class Observer;
-
 
 class PendingChanges {
 public:
@@ -411,36 +423,6 @@ typedef std::queue<PendingChanges> 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;
 };