mirror of
https://github.com/overte-org/overte.git
synced 2025-08-12 16:55:20 +02:00
Merge pull request #4996 from samcake/punk
TEAM TEACHING: Culling, LOD and Depth sort added to DrawSceneTask
This commit is contained in:
commit
c71519bfb1
6 changed files with 330 additions and 29 deletions
|
@ -3421,18 +3421,23 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("SceneProcessPendingChanges");
|
||||
_main3DScene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
_main3DScene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
_main3DScene->processPendingChangesQueue();
|
||||
_main3DScene->processPendingChangesQueue();
|
||||
}
|
||||
|
||||
// FOr now every frame pass the renderCOntext
|
||||
render::RenderContext renderContext;
|
||||
renderContext.args = renderArgs;
|
||||
_renderEngine->setRenderContext(renderContext);
|
||||
{
|
||||
PerformanceTimer perfTimer("EngineRun");
|
||||
render::RenderContext renderContext;
|
||||
renderContext.args = renderArgs;
|
||||
_renderEngine->setRenderContext(renderContext);
|
||||
|
||||
// Before the deferred pass, let's try to use the render engine
|
||||
_renderEngine->run();
|
||||
// Before the deferred pass, let's try to use the render engine
|
||||
_renderEngine->run();
|
||||
}
|
||||
|
||||
{
|
||||
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const RenderableEntityItemProxy::Pointer& payload) {
|
||||
if (payload && payload->entity) {
|
||||
if (payload->entity->getType() == EntityTypes::Light) {
|
||||
return ItemKey::Builder::light();
|
||||
}
|
||||
}
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,20 @@
|
|||
#include "gpu/Batch.h"
|
||||
#include "gpu/Context.h"
|
||||
|
||||
#include "ViewFrustum.h"
|
||||
#include "RenderArgs.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace render;
|
||||
|
||||
DrawSceneTask::DrawSceneTask() : Task() {
|
||||
|
||||
_jobs.push_back(Job(DrawOpaque()));
|
||||
_jobs.push_back(Job(DrawLight()));
|
||||
_jobs.push_back(Job(DrawTransparent()));
|
||||
}
|
||||
|
||||
DrawSceneTask::~DrawSceneTask() {
|
||||
}
|
||||
|
@ -29,25 +40,239 @@ void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderCon
|
|||
if (!sceneContext->_scene) {
|
||||
return;
|
||||
}
|
||||
auto& scene = sceneContext->_scene;
|
||||
|
||||
auto& itemBucketMap = scene->getMasterBucket();
|
||||
|
||||
// Is it possible that we render without a viewFrustum ?
|
||||
if (!(renderContext->args && renderContext->args->_viewFrustum)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto job : _jobs) {
|
||||
job.run(sceneContext, renderContext);
|
||||
}
|
||||
};
|
||||
|
||||
Job::~Job() {
|
||||
}
|
||||
|
||||
template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// render opaques
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape());
|
||||
|
||||
ItemIDs inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
inItems.push_back(id);
|
||||
}
|
||||
|
||||
ItemIDs culledItems;
|
||||
cullItems(sceneContext, renderContext, inItems, culledItems);
|
||||
|
||||
ItemIDs sortedItems;
|
||||
depthSortItems(sceneContext, renderContext, true, culledItems, sortedItems); // Sort Front to back opaque items!
|
||||
|
||||
renderItems(sceneContext, renderContext, sortedItems);
|
||||
}
|
||||
|
||||
|
||||
template <> void render::jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// render transparents
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape());
|
||||
|
||||
ItemIDs inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
inItems.push_back(id);
|
||||
}
|
||||
|
||||
ItemIDs culledItems;
|
||||
cullItems(sceneContext, renderContext, inItems, culledItems);
|
||||
|
||||
ItemIDs sortedItems;
|
||||
depthSortItems(sceneContext, renderContext, false, culledItems, sortedItems); // Sort Back to front transparent items!
|
||||
|
||||
renderItems(sceneContext, renderContext, sortedItems);
|
||||
}
|
||||
|
||||
template <> void render::jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
// render lights
|
||||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
|
||||
|
||||
|
||||
ItemIDs inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
inItems.push_back(id);
|
||||
}
|
||||
|
||||
ItemIDs culledItems;
|
||||
cullItems(sceneContext, renderContext, inItems, culledItems);
|
||||
renderItems(sceneContext, renderContext, culledItems);
|
||||
}
|
||||
|
||||
/*
|
||||
bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera) {
|
||||
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
|
||||
float octreeSizeScale = getOctreeSizeScale();
|
||||
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
float maxScale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
|
||||
|
||||
if (_shouldRenderTableNeedsRebuilding) {
|
||||
_shouldRenderTable.clear();
|
||||
|
||||
float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small
|
||||
float scale = maxScale;
|
||||
float visibleDistanceAtScale = visibleDistanceAtMaxScale;
|
||||
|
||||
while (scale > SMALLEST_SCALE_IN_TABLE) {
|
||||
scale /= 2.0f;
|
||||
visibleDistanceAtScale /= 2.0f;
|
||||
_shouldRenderTable[scale] = visibleDistanceAtScale;
|
||||
}
|
||||
_shouldRenderTableNeedsRebuilding = false;
|
||||
}
|
||||
|
||||
float closestScale = maxScale;
|
||||
float visibleDistanceAtClosestScale = visibleDistanceAtMaxScale;
|
||||
QMap<float, float>::const_iterator lowerBound = _shouldRenderTable.lowerBound(largestDimension);
|
||||
if (lowerBound != _shouldRenderTable.constEnd()) {
|
||||
closestScale = lowerBound.key();
|
||||
visibleDistanceAtClosestScale = lowerBound.value();
|
||||
}
|
||||
|
||||
if (closestScale < largestDimension) {
|
||||
visibleDistanceAtClosestScale *= 2.0f;
|
||||
}
|
||||
|
||||
return (distanceToCamera <= visibleDistanceAtClosestScale);
|
||||
}*/
|
||||
|
||||
void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outItems) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
auto& scene = sceneContext->_scene;
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// Culling / LOD
|
||||
for (auto id : inItems) {
|
||||
auto item = scene->getItem(id);
|
||||
auto bound = item.getBound();
|
||||
|
||||
if (bound.isNull()) {
|
||||
outItems.push_back(id); // One more Item to render
|
||||
args->_itemsRendered++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: some entity types (like lights) might want to be rendered even
|
||||
// when they are outside of the view frustum...
|
||||
|
||||
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
|
||||
|
||||
bool outOfView = args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE;
|
||||
if (!outOfView) {
|
||||
bool bigEnoughToRender = true; //_viewState->shouldRenderMesh(bound.getLargestDimension(), distance);
|
||||
|
||||
if (bigEnoughToRender) {
|
||||
outItems.push_back(id); // One more Item to render
|
||||
args->_itemsRendered++;
|
||||
} else {
|
||||
args->_itemsTooSmall++;
|
||||
}
|
||||
} else {
|
||||
args->_itemsOutOfView++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ItemBound {
|
||||
float _centerDepth = 0.0f;
|
||||
float _nearDepth = 0.0f;
|
||||
float _farDepth = 0.0f;
|
||||
ItemID _id = 0;
|
||||
|
||||
ItemBound() {}
|
||||
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {}
|
||||
};
|
||||
|
||||
struct FrontToBackSort {
|
||||
bool operator() (const ItemBound& left, const ItemBound& right) {
|
||||
return (left._centerDepth < right._centerDepth);
|
||||
}
|
||||
};
|
||||
|
||||
struct BackToFrontSort {
|
||||
bool operator() (const ItemBound& left, const ItemBound& right) {
|
||||
return (left._centerDepth > right._centerDepth);
|
||||
}
|
||||
};
|
||||
|
||||
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outItems) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
|
||||
auto& scene = sceneContext->_scene;
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// Allocate and simply copy
|
||||
outItems.reserve(inItems.size());
|
||||
|
||||
|
||||
// Make a local dataset of the center distance and closest point distance
|
||||
std::vector<ItemBound> itemBounds;
|
||||
itemBounds.reserve(outItems.size());
|
||||
|
||||
for (auto id : inItems) {
|
||||
auto item = scene->getItem(id);
|
||||
auto bound = item.getBound();
|
||||
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
|
||||
|
||||
itemBounds.emplace_back(ItemBound(distance, distance, distance, id));
|
||||
}
|
||||
|
||||
// sort against Z
|
||||
if (frontToBack) {
|
||||
FrontToBackSort frontToBackSort;
|
||||
std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort);
|
||||
} else {
|
||||
BackToFrontSort backToFrontSort;
|
||||
std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort);
|
||||
}
|
||||
|
||||
// FInally once sorted result to a list of itemID
|
||||
for (auto& itemBound : itemBounds) {
|
||||
outItems.emplace_back(itemBound._id);
|
||||
}
|
||||
}
|
||||
|
||||
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems) {
|
||||
auto& scene = sceneContext->_scene;
|
||||
RenderArgs* args = renderContext->args;
|
||||
gpu::Batch theBatch;
|
||||
|
||||
args->_batch = &theBatch;
|
||||
|
||||
// render opaques
|
||||
auto filter = ItemFilter::Builder::opaqueShape();
|
||||
auto& opaqueShapeItems = itemBucketMap.at(filter);
|
||||
|
||||
for (auto id : opaqueShapeItems) {
|
||||
// render
|
||||
for (auto id : inItems) {
|
||||
auto item = scene->getItem(id);
|
||||
item.render(args);
|
||||
}
|
||||
|
||||
|
||||
args->_context->enqueueBatch((*args->_batch));
|
||||
args->_batch = nullptr;
|
||||
};
|
||||
|
||||
}
|
|
@ -16,19 +16,81 @@
|
|||
|
||||
namespace render {
|
||||
|
||||
template <class T> void jobRun(const T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { }
|
||||
|
||||
class DrawSceneTask : public Task {
|
||||
class Job {
|
||||
public:
|
||||
|
||||
DrawSceneTask() : Task() {}
|
||||
~DrawSceneTask();
|
||||
template <class T>
|
||||
Job(T data) : _concept(new Model<T>(data)) {}
|
||||
Job(const Job& other) : _concept(other._concept) {}
|
||||
~Job();
|
||||
|
||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
if (_concept) {
|
||||
_concept->run(sceneContext, renderContext);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
class Concept {
|
||||
public:
|
||||
virtual ~Concept() = default;
|
||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0;
|
||||
};
|
||||
|
||||
template <class T> class Model : public Concept {
|
||||
public:
|
||||
typedef T Data;
|
||||
|
||||
Data _data;
|
||||
Model(Data data): _data(data) {}
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); }
|
||||
};
|
||||
|
||||
std::shared_ptr<Concept> _concept;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
typedef std::vector<Job> Jobs;
|
||||
|
||||
void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems, ItemIDs& outITems);
|
||||
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDs& inItems, ItemIDs& outITems);
|
||||
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDs& inItems);
|
||||
|
||||
|
||||
class DrawOpaque {
|
||||
public:
|
||||
};
|
||||
template <> void jobRun(const DrawOpaque& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
class DrawTransparent {
|
||||
public:
|
||||
};
|
||||
template <> void jobRun(const DrawTransparent& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
class DrawLight {
|
||||
public:
|
||||
};
|
||||
template <> void jobRun(const DrawLight& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
class DrawSceneTask : public Task {
|
||||
public:
|
||||
|
||||
DrawSceneTask();
|
||||
~DrawSceneTask();
|
||||
|
||||
Jobs _jobs;
|
||||
|
||||
virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // hifi_render_Task_h
|
||||
|
|
|
@ -47,6 +47,8 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey
|
|||
void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
|
||||
(*this)[ItemFilter::Builder::opaqueShape()];
|
||||
(*this)[ItemFilter::Builder::transparentShape()];
|
||||
auto lightFilter = ItemFilter::Builder().withTypeLight().build();
|
||||
(*this)[lightFilter];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
ItemKey(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
class Builder {
|
||||
Flags _flags;
|
||||
Flags _flags{ 0 };
|
||||
public:
|
||||
Builder() {}
|
||||
|
||||
|
@ -72,6 +72,7 @@ public:
|
|||
// 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(); }
|
||||
};
|
||||
|
||||
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
|
||||
|
@ -111,8 +112,8 @@ public:
|
|||
ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {}
|
||||
|
||||
class Builder {
|
||||
ItemKey::Flags _value;
|
||||
ItemKey::Flags _mask;
|
||||
ItemKey::Flags _value{ 0 };
|
||||
ItemKey::Flags _mask{ 0 };
|
||||
public:
|
||||
Builder() {}
|
||||
|
||||
|
@ -142,8 +143,9 @@ public:
|
|||
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().build(); }
|
||||
static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().build(); }
|
||||
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(); }
|
||||
};
|
||||
|
||||
// Item Filter operator testing if a key pass the filter
|
||||
|
@ -152,10 +154,10 @@ public:
|
|||
class Less {
|
||||
public:
|
||||
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
|
||||
if (left._value.to_ulong() >= right._value.to_ulong()) {
|
||||
if (left._value.to_ulong() == right._value.to_ulong()) {
|
||||
return left._mask.to_ulong() < right._mask.to_ulong();
|
||||
} else {
|
||||
return true;
|
||||
return left._value.to_ulong() < right._value.to_ulong();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue