Merge pull request #5103 from samcake/punk

TEAM TEACHING : Repair the Overlay 3D rendering and introduce the concept of layer in the scene
This commit is contained in:
Brad Hefta-Gaub 2015-06-11 10:17:38 -07:00
commit a1ee339765
8 changed files with 115 additions and 106 deletions

View file

@ -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", {

View file

@ -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);
}

View file

@ -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();

View file

@ -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;

View file

@ -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) {

View file

@ -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:
};

View file

@ -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);
}
}

View file

@ -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;
};