mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 07:43:57 +02:00
Making the octree more useful and cleaning up the interaction with the Scene
This commit is contained in:
parent
d1b29d561b
commit
1b1ffa494b
13 changed files with 728 additions and 654 deletions
|
@ -215,7 +215,7 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
|
|||
}
|
||||
};
|
||||
|
||||
void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
|
||||
void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
|
||||
assert(renderContext->getArgs());
|
||||
assert(renderContext->getArgs()->_viewFrustum);
|
||||
|
||||
|
@ -241,7 +241,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
|
|||
});
|
||||
}
|
||||
|
||||
void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
|
||||
void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
|
||||
assert(renderContext->getArgs());
|
||||
assert(renderContext->getArgs()->_viewFrustum);
|
||||
|
||||
|
@ -294,7 +294,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
|||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered());
|
||||
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
ItemBounds inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
auto& item = scene->getItem(id);
|
||||
|
@ -399,7 +399,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
|
|||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
|
||||
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
ItemBounds inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
inItems.emplace_back(id);
|
||||
|
|
|
@ -52,9 +52,9 @@ public:
|
|||
class DrawOpaqueDeferred {
|
||||
public:
|
||||
DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems);
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
|
||||
|
||||
using JobModel = render::Task::Job::ModelI<DrawOpaqueDeferred, render::ItemIDsBounds>;
|
||||
using JobModel = render::Task::Job::ModelI<DrawOpaqueDeferred, render::ItemBounds>;
|
||||
|
||||
protected:
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
|
@ -63,9 +63,9 @@ protected:
|
|||
class DrawTransparentDeferred {
|
||||
public:
|
||||
DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {}
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems);
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemBounds& inItems);
|
||||
|
||||
using JobModel = render::Task::Job::ModelI<DrawTransparentDeferred, render::ItemIDsBounds>;
|
||||
using JobModel = render::Task::Job::ModelI<DrawTransparentDeferred, render::ItemBounds>;
|
||||
|
||||
protected:
|
||||
render::ShapePlumberPointer _shapePlumber;
|
||||
|
|
|
@ -72,53 +72,14 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext,
|
|||
_octreeInfo = std::make_shared<gpu::Buffer>();;
|
||||
}*/
|
||||
|
||||
const auto& inCells = scene->_spatialTree._cells;
|
||||
const auto& inCells = scene->getSpatialTree()._cells;
|
||||
_cells->resize(inCells.size() * sizeof(AABox));
|
||||
AABox* cellAABox = reinterpret_cast<AABox*> (_cells->editData());
|
||||
for (const auto& cell : inCells) {
|
||||
(*cellAABox) = scene->_spatialTree.evalBound(cell.cellpos);
|
||||
(*cellAABox) = scene->getSpatialTree().evalBound(cell.getlocation());
|
||||
nbCells++;
|
||||
cellAABox++;
|
||||
}
|
||||
|
||||
/*
|
||||
_cells->resize((inItems.size() * sizeof(AABox)));
|
||||
_itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4)));
|
||||
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
|
||||
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
|
||||
for (auto& item : inItems) {
|
||||
if (!item.bounds.isInvalid()) {
|
||||
if (!item.bounds.isNull()) {
|
||||
(*itemAABox) = item.bounds;
|
||||
} else {
|
||||
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
|
||||
}
|
||||
auto& itemScene = scene->getItem(item.id);
|
||||
|
||||
auto itemStatusPointer = itemScene.getStatus();
|
||||
if (itemStatusPointer) {
|
||||
// Query the current status values, this is where the statusGetter lambda get called
|
||||
auto&& currentStatusValues = itemStatusPointer->getCurrentValues();
|
||||
int valueNum = 0;
|
||||
for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) {
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
for (int component = 0; component < VEC4_LENGTH; component++) {
|
||||
valueNum = vec4Num * VEC4_LENGTH + component;
|
||||
if (valueNum < (int)currentStatusValues.size()) {
|
||||
(*itemStatus)[component] = currentStatusValues[valueNum].getPackedData();
|
||||
}
|
||||
}
|
||||
itemStatus++;
|
||||
}
|
||||
} else {
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
itemStatus++;
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
itemStatus++;
|
||||
}
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (nbCells == 0) {
|
||||
|
|
|
@ -96,7 +96,7 @@ const gpu::TexturePointer DrawStatus::getStatusIconMap() const {
|
|||
|
||||
void DrawStatus::run(const SceneContextPointer& sceneContext,
|
||||
const RenderContextPointer& renderContext,
|
||||
const ItemIDsBounds& inItems) {
|
||||
const ItemBounds& inItems) {
|
||||
assert(renderContext->getArgs());
|
||||
assert(renderContext->getArgs()->_viewFrustum);
|
||||
RenderArgs* args = renderContext->getArgs();
|
||||
|
@ -119,11 +119,11 @@ void DrawStatus::run(const SceneContextPointer& sceneContext,
|
|||
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
|
||||
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
|
||||
for (auto& item : inItems) {
|
||||
if (!item.bounds.isInvalid()) {
|
||||
if (!item.bounds.isNull()) {
|
||||
(*itemAABox) = item.bounds;
|
||||
if (!item.bound.isInvalid()) {
|
||||
if (!item.bound.isNull()) {
|
||||
(*itemAABox) = item.bound;
|
||||
} else {
|
||||
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
|
||||
(*itemAABox).setBox(item.bound.getCorner(), 0.1f);
|
||||
}
|
||||
auto& itemScene = scene->getItem(item.id);
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@ namespace render {
|
|||
DrawStatus() {}
|
||||
DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); }
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems);
|
||||
|
||||
using JobModel = Task::Job::ModelI<DrawStatus, ItemIDsBounds>;
|
||||
using JobModel = Task::Job::ModelI<DrawStatus, ItemBounds>;
|
||||
|
||||
const gpu::PipelinePointer getDrawItemBoundsPipeline();
|
||||
const gpu::PipelinePointer getDrawItemStatusPipeline();
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
using namespace render;
|
||||
|
||||
void render::cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
|
||||
const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
|
||||
const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
assert(renderContext->getArgs());
|
||||
assert(renderContext->getArgs()->_viewFrustum);
|
||||
|
||||
|
@ -32,7 +32,7 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
|
|||
|
||||
// Culling / LOD
|
||||
for (auto item : inItems) {
|
||||
if (item.bounds.isNull()) {
|
||||
if (item.bound.isNull()) {
|
||||
outItems.emplace_back(item); // One more Item to render
|
||||
continue;
|
||||
}
|
||||
|
@ -42,13 +42,13 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
|
|||
bool outOfView;
|
||||
{
|
||||
PerformanceTimer perfTimer("boxInFrustum");
|
||||
outOfView = frustum->boxInFrustum(item.bounds) == ViewFrustum::OUTSIDE;
|
||||
outOfView = frustum->boxInFrustum(item.bound) == ViewFrustum::OUTSIDE;
|
||||
}
|
||||
if (!outOfView) {
|
||||
bool bigEnoughToRender;
|
||||
{
|
||||
PerformanceTimer perfTimer("shouldRender");
|
||||
bigEnoughToRender = cullFunctor(args, item.bounds);
|
||||
bigEnoughToRender = cullFunctor(args, item.bound);
|
||||
}
|
||||
if (bigEnoughToRender) {
|
||||
outItems.emplace_back(item); // One more Item to render
|
||||
|
@ -62,30 +62,30 @@ void render::cullItems(const RenderContextPointer& renderContext, const CullFunc
|
|||
details._rendered += outItems.size();
|
||||
}
|
||||
|
||||
struct ItemBound {
|
||||
struct ItemBoundSort {
|
||||
float _centerDepth = 0.0f;
|
||||
float _nearDepth = 0.0f;
|
||||
float _farDepth = 0.0f;
|
||||
ItemID _id = 0;
|
||||
AABox _bounds;
|
||||
|
||||
ItemBound() {}
|
||||
ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
|
||||
ItemBoundSort() {}
|
||||
ItemBoundSort(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {}
|
||||
};
|
||||
|
||||
struct FrontToBackSort {
|
||||
bool operator() (const ItemBound& left, const ItemBound& right) {
|
||||
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
|
||||
return (left._centerDepth < right._centerDepth);
|
||||
}
|
||||
};
|
||||
|
||||
struct BackToFrontSort {
|
||||
bool operator() (const ItemBound& left, const ItemBound& right) {
|
||||
bool operator() (const ItemBoundSort& left, const ItemBoundSort& right) {
|
||||
return (left._centerDepth > right._centerDepth);
|
||||
}
|
||||
};
|
||||
|
||||
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
|
||||
void render::depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
assert(renderContext->getArgs());
|
||||
assert(renderContext->getArgs()->_viewFrustum);
|
||||
|
||||
|
@ -99,33 +99,33 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende
|
|||
|
||||
|
||||
// Make a local dataset of the center distance and closest point distance
|
||||
std::vector<ItemBound> itemBounds;
|
||||
itemBounds.reserve(outItems.size());
|
||||
std::vector<ItemBoundSort> itemBoundSorts;
|
||||
itemBoundSorts.reserve(outItems.size());
|
||||
|
||||
for (auto itemDetails : inItems) {
|
||||
auto item = scene->getItem(itemDetails.id);
|
||||
auto bound = itemDetails.bounds; // item.getBound();
|
||||
auto bound = itemDetails.bound; // item.getBound();
|
||||
float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter());
|
||||
|
||||
itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound));
|
||||
itemBoundSorts.emplace_back(ItemBoundSort(distance, distance, distance, itemDetails.id, bound));
|
||||
}
|
||||
|
||||
// sort against Z
|
||||
if (frontToBack) {
|
||||
FrontToBackSort frontToBackSort;
|
||||
std::sort (itemBounds.begin(), itemBounds.end(), frontToBackSort);
|
||||
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), frontToBackSort);
|
||||
} else {
|
||||
BackToFrontSort backToFrontSort;
|
||||
std::sort (itemBounds.begin(), itemBounds.end(), backToFrontSort);
|
||||
std::sort(itemBoundSorts.begin(), itemBoundSorts.end(), backToFrontSort);
|
||||
}
|
||||
|
||||
// FInally once sorted result to a list of itemID
|
||||
for (auto& itemBound : itemBounds) {
|
||||
outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds));
|
||||
for (auto& item : itemBoundSorts) {
|
||||
outItems.emplace_back(ItemBound(item._id, item._bounds));
|
||||
}
|
||||
}
|
||||
|
||||
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
|
||||
void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems) {
|
||||
auto& scene = sceneContext->_scene;
|
||||
RenderArgs* args = renderContext->getArgs();
|
||||
|
||||
|
@ -151,7 +151,7 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons
|
|||
}
|
||||
|
||||
void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext,
|
||||
const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems) {
|
||||
const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems) {
|
||||
auto& scene = sceneContext->_scene;
|
||||
RenderArgs* args = renderContext->getArgs();
|
||||
|
||||
|
@ -162,7 +162,7 @@ void render::renderShapes(const SceneContextPointer& sceneContext, const RenderC
|
|||
}
|
||||
}
|
||||
|
||||
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) {
|
||||
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) {
|
||||
auto& scene = sceneContext->_scene;
|
||||
|
||||
outItems.clear();
|
||||
|
@ -173,7 +173,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
outItems.reserve(items->second.size());
|
||||
for (auto& id : items->second) {
|
||||
auto& item = scene->getItem(id);
|
||||
outItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
|
||||
outItems.emplace_back(ItemBound(id, item.getBound()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
}
|
||||
}
|
||||
|
||||
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
|
||||
void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems);
|
||||
}
|
||||
|
||||
|
@ -194,17 +194,17 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
|
|||
auto& scene = sceneContext->_scene;
|
||||
auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light());
|
||||
|
||||
ItemIDsBounds inItems;
|
||||
ItemBounds inItems;
|
||||
inItems.reserve(items.size());
|
||||
for (auto id : items) {
|
||||
auto item = scene->getItem(id);
|
||||
inItems.emplace_back(ItemIDAndBounds(id, item.getBound()));
|
||||
inItems.emplace_back(ItemBound(id, item.getBound()));
|
||||
}
|
||||
|
||||
RenderArgs* args = renderContext->getArgs();
|
||||
|
||||
auto& details = args->_details.edit(RenderDetails::OTHER_ITEM);
|
||||
ItemIDsBounds culledItems;
|
||||
ItemBounds culledItems;
|
||||
culledItems.reserve(inItems.size());
|
||||
cullItems(renderContext, _cullFunctor, details, inItems, culledItems);
|
||||
|
||||
|
@ -215,7 +215,7 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext
|
|||
});
|
||||
}
|
||||
|
||||
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes) {
|
||||
void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes) {
|
||||
auto& scene = sceneContext->_scene;
|
||||
outShapes.clear();
|
||||
|
||||
|
@ -223,7 +223,7 @@ void PipelineSortShapes::run(const SceneContextPointer& sceneContext, const Rend
|
|||
auto key = scene->getItem(item.id).getShapeKey();
|
||||
auto outItems = outShapes.find(key);
|
||||
if (outItems == outShapes.end()) {
|
||||
outItems = outShapes.insert(std::make_pair(key, ItemIDsBounds{})).first;
|
||||
outItems = outShapes.insert(std::make_pair(key, ItemBounds{})).first;
|
||||
outItems->second.reserve(inItems.size());
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ void DepthSortShapes::run(const SceneContextPointer& sceneContext, const RenderC
|
|||
auto& inItems = pipeline.second;
|
||||
auto outItems = outShapes.find(pipeline.first);
|
||||
if (outItems == outShapes.end()) {
|
||||
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemIDsBounds{})).first;
|
||||
outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first;
|
||||
}
|
||||
|
||||
depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems->second);
|
||||
|
|
|
@ -22,10 +22,10 @@ namespace render {
|
|||
using CullFunctor = std::function<bool(const RenderArgs*, const AABox&)>;
|
||||
|
||||
void cullItems(const RenderContextPointer& renderContext, const CullFunctor& cullFunctor, RenderDetails::Item& details,
|
||||
const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
|
||||
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
|
||||
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
|
||||
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1);
|
||||
const ItemBounds& inItems, ItemBounds& outItems);
|
||||
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
|
||||
void renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems);
|
||||
void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemBounds& inItems, int maxDrawnItems = -1);
|
||||
|
||||
class FetchItems {
|
||||
public:
|
||||
|
@ -37,8 +37,8 @@ public:
|
|||
ItemFilter _filter = ItemFilter::Builder::opaqueShape().withoutLayered();
|
||||
ProbeNumItems _probeNumItems;
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems);
|
||||
using JobModel = Task::Job::ModelO<FetchItems, ItemIDsBounds>;
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems);
|
||||
using JobModel = Task::Job::ModelO<FetchItems, ItemBounds>;
|
||||
};
|
||||
|
||||
template<RenderDetails::Type T = RenderDetails::Type::OTHER_ITEM>
|
||||
|
@ -46,7 +46,7 @@ class CullItems {
|
|||
public:
|
||||
CullItems(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {}
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) {
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) {
|
||||
const auto& args = renderContext->getArgs();
|
||||
auto& details = args->_details.edit(T);
|
||||
outItems.clear();
|
||||
|
@ -54,7 +54,7 @@ public:
|
|||
render::cullItems(renderContext, _cullFunctor, details, inItems, outItems);
|
||||
}
|
||||
|
||||
using JobModel = Task::Job::ModelIO<CullItems<T>, ItemIDsBounds, ItemIDsBounds>;
|
||||
using JobModel = Task::Job::ModelIO<CullItems<T>, ItemBounds, ItemBounds>;
|
||||
|
||||
protected:
|
||||
CullFunctor _cullFunctor;
|
||||
|
@ -65,8 +65,8 @@ public:
|
|||
bool _frontToBack;
|
||||
DepthSortItems(bool frontToBack = true) : _frontToBack(frontToBack) {}
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
|
||||
using JobModel = Task::Job::ModelIO<DepthSortItems, ItemIDsBounds, ItemIDsBounds>;
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems);
|
||||
using JobModel = Task::Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
|
||||
};
|
||||
|
||||
class DrawLight {
|
||||
|
@ -81,8 +81,8 @@ protected:
|
|||
|
||||
class PipelineSortShapes {
|
||||
public:
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ShapesIDsBounds& outShapes);
|
||||
using JobModel = Task::Job::ModelIO<PipelineSortShapes, ItemIDsBounds, ShapesIDsBounds>;
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemBounds& inItems, ShapesIDsBounds& outShapes);
|
||||
using JobModel = Task::Job::ModelIO<PipelineSortShapes, ItemBounds, ShapesIDsBounds>;
|
||||
};
|
||||
|
||||
class DepthSortShapes {
|
||||
|
|
73
libraries/render/src/render/Item.cpp
Normal file
73
libraries/render/src/render/Item.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Item.cpp
|
||||
// render/src/render
|
||||
//
|
||||
// Created by Sam Gateau on 1/26/16.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
#include "Item.h"
|
||||
|
||||
#include <numeric>
|
||||
#include "gpu/Batch.h"
|
||||
|
||||
using namespace render;
|
||||
|
||||
const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value();
|
||||
|
||||
const float Item::Status::Value::RED = 0.0f;
|
||||
const float Item::Status::Value::YELLOW = 60.0f;
|
||||
const float Item::Status::Value::GREEN = 120.0f;
|
||||
const float Item::Status::Value::CYAN = 180.0f;
|
||||
const float Item::Status::Value::BLUE = 240.0f;
|
||||
const float Item::Status::Value::MAGENTA = 300.0f;
|
||||
|
||||
void Item::Status::Value::setScale(float scale) {
|
||||
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
|
||||
}
|
||||
|
||||
void Item::Status::Value::setColor(float hue) {
|
||||
// Convert the HUe from range [0, 360] to signed normalized value
|
||||
const float HUE_MAX = 360.0f;
|
||||
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
|
||||
}
|
||||
void Item::Status::Value::setIcon(unsigned char icon) {
|
||||
_icon = icon;
|
||||
}
|
||||
|
||||
Item::Status::Values Item::Status::getCurrentValues() const {
|
||||
Values currentValues(_values.size());
|
||||
auto currentValue = currentValues.begin();
|
||||
for (auto& getter : _values) {
|
||||
(*currentValue) = getter();
|
||||
currentValue++;
|
||||
}
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
|
||||
if (!_status) {
|
||||
_status = std::make_shared<Status>();
|
||||
}
|
||||
_status->addGetter(getter);
|
||||
}
|
||||
|
||||
void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
|
||||
if (!_status) {
|
||||
_status = std::make_shared<Status>();
|
||||
}
|
||||
for (auto& g : getters) {
|
||||
_status->addGetter(g);
|
||||
}
|
||||
}
|
||||
|
||||
void Item::resetPayload(const PayloadPointer& payload) {
|
||||
if (!payload) {
|
||||
kill();
|
||||
} else {
|
||||
_payload = payload;
|
||||
_key = _payload->getKey();
|
||||
}
|
||||
}
|
444
libraries/render/src/render/Item.h
Normal file
444
libraries/render/src/render/Item.h
Normal file
|
@ -0,0 +1,444 @@
|
|||
//
|
||||
// Item.h
|
||||
// render/src/render
|
||||
//
|
||||
// Created by Sam Gateau on 1/26/16.
|
||||
// Copyright 2014 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_render_Item_h
|
||||
#define hifi_render_Item_h
|
||||
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <AABox.h>
|
||||
#include <RenderArgs.h>
|
||||
|
||||
#include "model/Material.h"
|
||||
#include "ShapePipeline.h"
|
||||
|
||||
|
||||
namespace render {
|
||||
|
||||
class Context;
|
||||
|
||||
// Key is the KEY to filter Items and create specialized lists
|
||||
class ItemKey {
|
||||
public:
|
||||
enum FlagBit {
|
||||
TYPE_SHAPE = 0, // Item is a Shape
|
||||
TYPE_LIGHT, // Item is a Light
|
||||
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
|
||||
DEFORMED, // Deformed within bound, not solid
|
||||
INVISIBLE, // Visible or not? could be just here to cast shadow
|
||||
SHADOW_CASTER, // Item cast shadows
|
||||
PICKABLE, // Item can be picked/selected
|
||||
LAYERED, // Item belongs to one of the layers different from the default layer
|
||||
|
||||
NUM_FLAGS, // Not a valid flag
|
||||
};
|
||||
typedef std::bitset<NUM_FLAGS> Flags;
|
||||
|
||||
// The key is the Flags
|
||||
Flags _flags;
|
||||
|
||||
ItemKey() : _flags(0) {}
|
||||
ItemKey(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
class Builder {
|
||||
friend class ItemKey;
|
||||
Flags _flags{ 0 };
|
||||
public:
|
||||
Builder() {}
|
||||
|
||||
ItemKey build() const { return ItemKey(_flags); }
|
||||
|
||||
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
|
||||
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
|
||||
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
|
||||
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
|
||||
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
|
||||
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
|
||||
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
||||
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
||||
Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
|
||||
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
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 isShape() const { return _flags[TYPE_SHAPE]; }
|
||||
bool isLight() const { return _flags[TYPE_LIGHT]; }
|
||||
|
||||
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
|
||||
bool isTransparent() const { return _flags[TRANSLUCENT]; }
|
||||
|
||||
bool isWorldSpace() const { return !_flags[VIEW_SPACE]; }
|
||||
bool isViewSpace() const { return _flags[VIEW_SPACE]; }
|
||||
|
||||
bool isStatic() const { return !_flags[DYNAMIC]; }
|
||||
bool isDynamic() const { return _flags[DYNAMIC]; }
|
||||
|
||||
bool isRigid() const { return !_flags[DEFORMED]; }
|
||||
bool isDeformed() const { return _flags[DEFORMED]; }
|
||||
|
||||
bool isVisible() const { return !_flags[INVISIBLE]; }
|
||||
bool isInvisible() const { return _flags[INVISIBLE]; }
|
||||
|
||||
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
|
||||
|
||||
bool isPickable() const { return _flags[PICKABLE]; }
|
||||
|
||||
bool isLayered() const { return _flags[LAYERED]; }
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
|
||||
debug << "[ItemKey: isOpaque:" << itemKey.isOpaque()
|
||||
<< ", isStatic:" << itemKey.isStatic()
|
||||
<< ", isWorldSpace:" << itemKey.isWorldSpace()
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
class ItemFilter {
|
||||
public:
|
||||
ItemKey::Flags _value{ 0 };
|
||||
ItemKey::Flags _mask{ 0 };
|
||||
|
||||
|
||||
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:
|
||||
Builder() {}
|
||||
|
||||
ItemFilter build() const { return ItemFilter(_value, _mask); }
|
||||
|
||||
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& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
|
||||
Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
|
||||
Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
|
||||
|
||||
Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
|
||||
Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
|
||||
|
||||
Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
|
||||
Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
|
||||
|
||||
Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
|
||||
Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
|
||||
|
||||
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
|
||||
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); 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 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); }
|
||||
|
||||
class Less {
|
||||
public:
|
||||
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
|
||||
if (left._value.to_ulong() == right._value.to_ulong()) {
|
||||
return left._mask.to_ulong() < right._mask.to_ulong();
|
||||
} else {
|
||||
return left._value.to_ulong() < right._value.to_ulong();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
|
||||
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
||||
class Item {
|
||||
public:
|
||||
typedef std::vector<Item> Vector;
|
||||
typedef unsigned int ID;
|
||||
|
||||
static const ID INVALID_ITEM_ID = 0;
|
||||
|
||||
// Bound is the AABBox fully containing this item
|
||||
typedef AABox Bound;
|
||||
|
||||
// Status records the life history and performances of this item while performing at rendering and updating.
|
||||
// This is Used for monitoring and dynamically adjust the quality
|
||||
class Status {
|
||||
public:
|
||||
|
||||
// Status::Value class is the data used to represent the transient information of a status as a square icon
|
||||
// The "icon" is a square displayed in the 3D scene over the render::Item AABB center.
|
||||
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
|
||||
class Value {
|
||||
unsigned short _scale = 0xFFFF;
|
||||
unsigned char _color = 0xFF;
|
||||
unsigned char _icon = 0xFF;
|
||||
public:
|
||||
const static Value INVALID; // Invalid value meanss the status won't show
|
||||
|
||||
Value() {}
|
||||
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
|
||||
|
||||
// It can be scaled in the range [0, 1]
|
||||
void setScale(float scale);
|
||||
// the color hue in the range [0, 360] representing the color wheel hue
|
||||
void setColor(float hue);
|
||||
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
|
||||
// hopefully have an icon available to display (see DrawStatusJob)
|
||||
void setIcon(unsigned char icon);
|
||||
|
||||
// Standard color Hue
|
||||
static const float RED; // 0.0f;
|
||||
static const float YELLOW; // 60.0f;
|
||||
static const float GREEN; // 120.0f;
|
||||
static const float CYAN; // 180.0f;
|
||||
static const float BLUE; // 240.0f;
|
||||
static const float MAGENTA; // 300.0f;
|
||||
|
||||
// Retreive the Value data tightely packed as an int
|
||||
int getPackedData() const { return *((const int*) this); }
|
||||
};
|
||||
|
||||
typedef std::function<Value()> Getter;
|
||||
typedef std::vector<Getter> Getters;
|
||||
|
||||
Getters _values;
|
||||
|
||||
void addGetter(const Getter& getter) { _values.push_back(getter); }
|
||||
|
||||
size_t getNumValues() const { return _values.size(); }
|
||||
|
||||
using Values = std::vector <Value>;
|
||||
Values getCurrentValues() const;
|
||||
};
|
||||
typedef std::shared_ptr<Status> StatusPointer;
|
||||
|
||||
// Update Functor
|
||||
class UpdateFunctorInterface {
|
||||
public:
|
||||
virtual ~UpdateFunctorInterface() {}
|
||||
};
|
||||
typedef std::shared_ptr<UpdateFunctorInterface> UpdateFunctorPointer;
|
||||
|
||||
// Payload is whatever is in this Item and implement the Payload Interface
|
||||
class PayloadInterface {
|
||||
public:
|
||||
virtual const ItemKey getKey() const = 0;
|
||||
virtual const Bound getBound() const = 0;
|
||||
virtual int getLayer() const = 0;
|
||||
|
||||
virtual void render(RenderArgs* args) = 0;
|
||||
|
||||
virtual const ShapeKey getShapeKey() const = 0;
|
||||
|
||||
~PayloadInterface() {}
|
||||
|
||||
// Status interface is local to the base class
|
||||
const StatusPointer& getStatus() const { return _status; }
|
||||
void addStatusGetter(const Status::Getter& getter);
|
||||
void addStatusGetters(const Status::Getters& getters);
|
||||
|
||||
protected:
|
||||
StatusPointer _status;
|
||||
|
||||
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 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) const { _payload->render(args); }
|
||||
|
||||
// Shape Type Interface
|
||||
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
|
||||
|
||||
// Access the status
|
||||
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
||||
|
||||
protected:
|
||||
PayloadPointer _payload;
|
||||
ItemKey _key;
|
||||
|
||||
friend class Scene;
|
||||
};
|
||||
|
||||
|
||||
typedef Item::UpdateFunctorInterface UpdateFunctorInterface;
|
||||
typedef Item::UpdateFunctorPointer UpdateFunctorPointer;
|
||||
typedef std::vector<UpdateFunctorPointer> UpdateFunctors;
|
||||
|
||||
template <class T> class UpdateFunctor : public Item::UpdateFunctorInterface {
|
||||
public:
|
||||
typedef std::function<void(T&)> Func;
|
||||
Func _func;
|
||||
|
||||
UpdateFunctor(Func func): _func(func) {}
|
||||
~UpdateFunctor() {}
|
||||
};
|
||||
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const Item& item) {
|
||||
debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
// THe Payload class is the real Payload to be used
|
||||
// THis allow anything to be turned into a Payload as long as the required interface functions are available
|
||||
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
|
||||
// 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
|
||||
// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape.
|
||||
// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline,
|
||||
// implying that the shape will setup its own pipeline without the use of the ShapeKey.
|
||||
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
|
||||
|
||||
template <class T> class Payload : public Item::PayloadInterface {
|
||||
public:
|
||||
typedef std::shared_ptr<T> DataPointer;
|
||||
typedef UpdateFunctor<T> Updater;
|
||||
|
||||
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 ShapeKey getShapeKey() const { return shapeGetShapeKey<T>(_data); }
|
||||
|
||||
protected:
|
||||
DataPointer _data;
|
||||
|
||||
// Update mechanics
|
||||
virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast<Updater>(functor)->_func((*_data)); }
|
||||
friend class Item;
|
||||
};
|
||||
|
||||
// Let's show how to make a simple FooPayload example:
|
||||
/*
|
||||
class Foo {
|
||||
public:
|
||||
mutable ItemKey _myownKey;
|
||||
void makeMywnKey() const {
|
||||
_myownKey = ItemKey::Builder().withTypeShape().build();
|
||||
}
|
||||
|
||||
const Item::Bound evaluateMyBound() {
|
||||
// Do stuff here to get your final Bound
|
||||
return Item::Bound();
|
||||
}
|
||||
};
|
||||
|
||||
typedef Payload<Foo> FooPayload;
|
||||
typedef std::shared_ptr<Foo> FooPointer;
|
||||
|
||||
// In a Source file, not a header, implement the Payload interface function specialized for Foo:
|
||||
template <> const ItemKey payloadGetKey(const FooPointer& foo) {
|
||||
// Foo's way of provinding its Key
|
||||
foo->makeMyKey();
|
||||
return foo->_myownKey;
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const FooPointer& foo) {
|
||||
// evaluate Foo's own bound
|
||||
return foo->evaluateMyBound();
|
||||
}
|
||||
|
||||
// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing
|
||||
|
||||
*/
|
||||
// End of the example
|
||||
|
||||
typedef Item::PayloadPointer PayloadPointer;
|
||||
typedef std::vector< PayloadPointer > Payloads;
|
||||
|
||||
// A few typedefs for standard containers of ItemIDs
|
||||
using ItemID = Item::ID;
|
||||
using ItemIDs = std::vector<ItemID>;
|
||||
using ItemIDSet = std::set<ItemID>;
|
||||
|
||||
// Handy type to just pass the ID and the bound of an item
|
||||
class ItemBound {
|
||||
public:
|
||||
ItemBound(ItemID id) : id(id) { }
|
||||
ItemBound(ItemID id, const AABox& bound) : id(id), bound(bound) { }
|
||||
|
||||
ItemID id;
|
||||
AABox bound;
|
||||
};
|
||||
// many Item Bounds in a vector
|
||||
using ItemBounds = std::vector<ItemBound>;
|
||||
|
||||
// A map of items by ShapeKey to optimize rendering pipeline assignments
|
||||
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
|
||||
|
||||
}
|
||||
|
||||
#endif // hifi_render_Item_h
|
|
@ -13,26 +13,54 @@
|
|||
|
||||
using namespace render;
|
||||
|
||||
|
||||
const double Octree::INV_DEPTH_DIM[] = {
|
||||
1.0,
|
||||
1.0 / 2.0,
|
||||
1.0 / 4.0,
|
||||
1.0 / 8.0,
|
||||
1.0 / 16.0,
|
||||
1.0 / 32.0,
|
||||
1.0 / 64.0,
|
||||
1.0 / 128.0,
|
||||
1.0 / 256.0,
|
||||
1.0 / 512.0,
|
||||
1.0 / 1024.0,
|
||||
1.0 / 2048.0,
|
||||
1.0 / 4096.0,
|
||||
1.0 / 8192.0,
|
||||
1.0 / 16384.0,
|
||||
1.0 / 32768.0 };
|
||||
|
||||
Octree::Indices Octree::allocateCellPath(const CellPath& path) {
|
||||
Indices cellPath;
|
||||
|
||||
Index currentIndex = 0;
|
||||
Cell* currentCell = _cells.data();
|
||||
int d = 0;
|
||||
cellPath.push_back(currentIndex);
|
||||
|
||||
for (; d <= path.back().depth; d++) {
|
||||
auto& cellPoint = path[d];
|
||||
for (int d = 0; d <= path.back().depth; d++) {
|
||||
auto& location = path[d];
|
||||
|
||||
auto currentIndex = currentCell->child(cellPoint.octant());
|
||||
auto currentIndex = currentCell->child(location.octant());
|
||||
if (currentIndex == INVALID) {
|
||||
currentIndex = _cells.size();
|
||||
currentCell->links[cellPoint.octant()] = currentIndex;
|
||||
_cells.push_back(Cell(cellPath.back(), cellPoint));
|
||||
currentCell->setChild(location.octant(), currentIndex);
|
||||
_cells.push_back(Cell(cellPath.back(), location));
|
||||
}
|
||||
cellPath.push_back(currentIndex);
|
||||
currentCell = _cells.data() + currentIndex;
|
||||
}
|
||||
|
||||
return cellPath;
|
||||
}
|
||||
|
||||
|
||||
void ItemSpatialTree::insert(const ItemBounds& items) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ItemSpatialTree::erase(const ItemBounds& items) {
|
||||
|
||||
}
|
|
@ -18,12 +18,10 @@
|
|||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/bit.hpp>
|
||||
|
||||
|
||||
#include <AABox.h>
|
||||
|
||||
namespace render {
|
||||
|
||||
|
||||
class Octree {
|
||||
public:
|
||||
|
||||
|
@ -55,7 +53,7 @@ namespace render {
|
|||
using Octant = Link;
|
||||
|
||||
|
||||
// Depth, Width, Volume
|
||||
// Depth, Dim, Volume
|
||||
// {0, 1, 1}
|
||||
// {1, 2, 8}
|
||||
// {2, 4, 64}
|
||||
|
@ -73,21 +71,30 @@ namespace render {
|
|||
// {14, 16384, 4398046511104}
|
||||
// {15, 32768, 35184372088832}
|
||||
// {16, 65536, 281474976710656}
|
||||
|
||||
using Depth = int8_t; // Max depth is 15 => 32Km root down to 1m cells
|
||||
using Coord = int16_t;// Need 16bits integer coordinates on each axes: 32768 cell positions
|
||||
|
||||
// Max depth is 15 => 32Km root down to 1m cells
|
||||
using Depth = int8_t;
|
||||
static const Depth MAX_DEPTH { 15 };
|
||||
static const double INV_DEPTH_DIM[Octree::MAX_DEPTH + 1];
|
||||
|
||||
static int getDepthDimension(Depth depth) { return 2 << depth; }
|
||||
static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; }
|
||||
|
||||
|
||||
// Need 16bits integer coordinates on each axes: 32768 cell positions
|
||||
using Coord = int16_t;
|
||||
using Coord3 = glm::i16vec3;
|
||||
using Coord4 = glm::i16vec4;
|
||||
|
||||
class CellPoint {
|
||||
|
||||
class Location {
|
||||
void assertValid() {
|
||||
assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0));
|
||||
assert((pos.x < (2 << depth)) && (pos.y < (2 << depth)) && (pos.z < (2 << depth)));
|
||||
}
|
||||
public:
|
||||
CellPoint() {}
|
||||
CellPoint(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
|
||||
CellPoint(Depth d) : pos(0), depth(d) { assertValid(); }
|
||||
Location() {}
|
||||
Location(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
|
||||
Location(Depth d) : pos(0), depth(d) { assertValid(); }
|
||||
|
||||
Coord3 pos{ 0 };
|
||||
uint8_t spare{ 0 };
|
||||
|
@ -97,17 +104,17 @@ namespace render {
|
|||
// Eval the octant of this cell relative to its parent
|
||||
Octant octant() const { return Octant((pos.x & XAxis) | (pos.y & YAxis) | (pos.z & ZAxis)); }
|
||||
|
||||
// Get the Parent cell pos of this cell
|
||||
CellPoint parent() const {
|
||||
return CellPoint{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) };
|
||||
// Get the Parent cell Location of this cell
|
||||
Location parent() const {
|
||||
return Location{ pos >> Coord3(1), Depth(depth <= 0 ? 0 : depth - 1) };
|
||||
}
|
||||
CellPoint child(Link octant) const {
|
||||
return CellPoint{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) };
|
||||
Location child(Link octant) const {
|
||||
return Location{ pos << Coord3(1) | Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)), Depth(depth + 1) };
|
||||
}
|
||||
|
||||
using vector = std::vector< CellPoint >;
|
||||
static vector rootTo(const CellPoint& dest) {
|
||||
CellPoint current{ dest };
|
||||
using vector = std::vector< Location >;
|
||||
static vector rootTo(const Location& dest) {
|
||||
Location current{ dest };
|
||||
vector path(dest.depth + 1);
|
||||
path[dest.depth] = dest;
|
||||
while (current.depth > 0) {
|
||||
|
@ -117,9 +124,9 @@ namespace render {
|
|||
return path;
|
||||
}
|
||||
};
|
||||
using CellPath = CellPoint::vector;
|
||||
using CellPath = Location::vector;
|
||||
|
||||
|
||||
|
||||
class Range {
|
||||
public:
|
||||
Coord3 _min;
|
||||
|
@ -129,66 +136,113 @@ namespace render {
|
|||
// Cell or Brick Indexing
|
||||
using Index = int32_t;
|
||||
static const Index INVALID = -1;
|
||||
static const Index ROOT = 0;
|
||||
using Indices = std::vector<Index>;
|
||||
|
||||
// the cell description
|
||||
class Cell {
|
||||
public:
|
||||
const Location& getlocation() const { return _location; }
|
||||
|
||||
CellPoint cellpos;
|
||||
|
||||
std::array<Index, NUM_LINKS> links;
|
||||
|
||||
Index parent() const { return links[Parent]; }
|
||||
Index parent() const { return _links[Parent]; }
|
||||
bool asParent() const { return parent() != INVALID; }
|
||||
|
||||
Index child(Link octant) const { return links[octant]; }
|
||||
Index child(Link octant) const { return _links[octant]; }
|
||||
bool asChild(Link octant) const { return child(octant) != INVALID; }
|
||||
void setChild(Link octant, Index child) { _links[octant] = child; }
|
||||
|
||||
Cell() :
|
||||
_links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } })
|
||||
{}
|
||||
|
||||
Cell(Index parent, Location loc) :
|
||||
_location(loc),
|
||||
_links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } })
|
||||
{}
|
||||
|
||||
Index brick{ INVALID };
|
||||
|
||||
Cell() :
|
||||
links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID } })
|
||||
{}
|
||||
|
||||
Cell(Index parent, CellPoint pos) :
|
||||
cellpos(pos),
|
||||
links({ { INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, parent } })
|
||||
{}
|
||||
private:
|
||||
Location _location;
|
||||
std::array<Index, NUM_LINKS> _links;
|
||||
};
|
||||
using Cells = std::vector< Cell >;
|
||||
|
||||
|
||||
|
||||
class Brick {
|
||||
public:
|
||||
|
||||
|
||||
};
|
||||
using Bricks = std::vector< Brick >;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Octree members
|
||||
Cells _cells = Cells(1, Cell()); // start with only the Cell root
|
||||
Bricks _bricks;
|
||||
|
||||
float _size = 320.0f;
|
||||
|
||||
|
||||
Octree() {};
|
||||
|
||||
// allocatePath
|
||||
Indices allocateCellPath(const CellPath& path);
|
||||
|
||||
AABox evalBound(const CellPoint& point) const {
|
||||
|
||||
float width = (float) (_size / double(1 << point.depth));
|
||||
glm::vec3 corner = glm::vec3(-_size * 0.5f) + glm::vec3(point.pos) * width;
|
||||
return AABox(corner, width);
|
||||
|
||||
// reach to Cell and allocate the cell path to it if needed
|
||||
Index editCell(const Location& loc) {
|
||||
auto cells = allocateCellPath(Location::rootTo(loc));
|
||||
return cells.back();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
// CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it
|
||||
#include "Item.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
// An octree of Items organizing them efficiently for culling
|
||||
// The octree only cares about the bound & the key of an item to store it a the right cell location
|
||||
class ItemSpatialTree : public Octree {
|
||||
float _size{ 32000.0f };
|
||||
double _invSize{ 1.0 / _size };
|
||||
glm::vec3 _origin{ -_size };
|
||||
public:
|
||||
|
||||
|
||||
float getSize() const { _size; }
|
||||
glm::vec3 getOrigin() const { _origin; }
|
||||
|
||||
float getCellWidth(Depth depth) const { return (float) _size * getInvDepthDimension(depth); }
|
||||
float getInvCellWidth(Depth depth) const { return (float) getDepthDimension(depth) * _invSize; }
|
||||
|
||||
glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::MAX_DEPTH) const {
|
||||
return getOrigin() + glm::vec3(coord) * getCellWidth(depth);
|
||||
}
|
||||
glm::vec3 evalPos(const Coord3& coord, float cellWidth) const {
|
||||
return getOrigin() + glm::vec3(coord) * cellWidth;
|
||||
}
|
||||
|
||||
Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::MAX_DEPTH) const {
|
||||
return Coord3((pos - getOrigin()) * getInvCellWidth(depth));
|
||||
}
|
||||
|
||||
AABox evalBound(const Location& loc) const {
|
||||
float cellWidth = getCellWidth(loc.depth);
|
||||
return AABox(evalPos(loc.pos, cellWidth), cellWidth);
|
||||
}
|
||||
|
||||
|
||||
Location evalLocation(const AABox& bound) const {
|
||||
auto minPos = evalCoord(bound.getMinimumPoint());
|
||||
auto maxPos = evalCoord(bound.getMaximumPoint());
|
||||
auto range = maxPos - minPos;
|
||||
//range
|
||||
return Location(minPos, 4);
|
||||
}
|
||||
|
||||
ItemSpatialTree() {}
|
||||
|
||||
void insert(const ItemBounds& items);
|
||||
void erase(const ItemBounds& items);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // hifi_render_Octree_h
|
||||
|
|
|
@ -56,62 +56,6 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() {
|
|||
(*this)[ItemFilter::Builder::transparentShape().withLayered()];
|
||||
}
|
||||
|
||||
const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value();
|
||||
|
||||
const float Item::Status::Value::RED = 0.0f;
|
||||
const float Item::Status::Value::YELLOW = 60.0f;
|
||||
const float Item::Status::Value::GREEN = 120.0f;
|
||||
const float Item::Status::Value::CYAN = 180.0f;
|
||||
const float Item::Status::Value::BLUE = 240.0f;
|
||||
const float Item::Status::Value::MAGENTA = 300.0f;
|
||||
|
||||
void Item::Status::Value::setScale(float scale) {
|
||||
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
|
||||
}
|
||||
|
||||
void Item::Status::Value::setColor(float hue) {
|
||||
// Convert the HUe from range [0, 360] to signed normalized value
|
||||
const float HUE_MAX = 360.0f;
|
||||
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
|
||||
}
|
||||
void Item::Status::Value::setIcon(unsigned char icon) {
|
||||
_icon = icon;
|
||||
}
|
||||
|
||||
Item::Status::Values Item::Status::getCurrentValues() const {
|
||||
Values currentValues(_values.size());
|
||||
auto currentValue = currentValues.begin();
|
||||
for (auto& getter : _values) {
|
||||
(*currentValue) = getter();
|
||||
currentValue++;
|
||||
}
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
|
||||
if (!_status) {
|
||||
_status = std::make_shared<Status>();
|
||||
}
|
||||
_status->addGetter(getter);
|
||||
}
|
||||
|
||||
void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) {
|
||||
if (!_status) {
|
||||
_status = std::make_shared<Status>();
|
||||
}
|
||||
for (auto& g : getters) {
|
||||
_status->addGetter(g);
|
||||
}
|
||||
}
|
||||
|
||||
void Item::resetPayload(const PayloadPointer& payload) {
|
||||
if (!payload) {
|
||||
kill();
|
||||
} else {
|
||||
_payload = payload;
|
||||
_key = _payload->getKey();
|
||||
}
|
||||
}
|
||||
|
||||
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
|
||||
_resetItems.push_back(id);
|
||||
|
@ -127,7 +71,6 @@ void PendingChanges::updateItem(ItemID id, const UpdateFunctorPointer& functor)
|
|||
_updateFunctors.push_back(functor);
|
||||
}
|
||||
|
||||
|
||||
void PendingChanges::merge(PendingChanges& changes) {
|
||||
_resetItems.insert(_resetItems.end(), changes._resetItems.begin(), changes._resetItems.end());
|
||||
_resetPayloads.insert(_resetPayloads.end(), changes._resetPayloads.begin(), changes._resetPayloads.end());
|
||||
|
@ -139,12 +82,6 @@ void PendingChanges::merge(PendingChanges& changes) {
|
|||
Scene::Scene() {
|
||||
_items.push_back(Item()); // add the itemID #0 to nothing
|
||||
_masterBucketMap.allocateStandardOpaqueTranparentBuckets();
|
||||
|
||||
|
||||
_spatialTree;
|
||||
Octree::CellPoint point{ Octree::Coord3{ 2, 4, 3 }, 15};
|
||||
auto path = Octree::CellPoint::rootTo(point);
|
||||
auto indices = _spatialTree.allocateCellPath(path);
|
||||
}
|
||||
|
||||
ItemID Scene::allocateID() {
|
||||
|
@ -200,8 +137,9 @@ void Scene::resetItems(const ItemIDs& ids, Payloads& payloads) {
|
|||
item.resetPayload(*resetPayload);
|
||||
|
||||
_masterBucketMap.reset((*resetID), oldKey, item.getKey());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::removeItems(const ItemIDs& ids) {
|
||||
|
|
|
@ -12,436 +12,12 @@
|
|||
#ifndef hifi_render_Scene_h
|
||||
#define hifi_render_Scene_h
|
||||
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <AABox.h>
|
||||
#include <RenderArgs.h>
|
||||
|
||||
#include "model/Material.h"
|
||||
#include "ShapePipeline.h"
|
||||
|
||||
#include "Item.h"
|
||||
#include "Octree.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
class Context;
|
||||
|
||||
// Key is the KEY to filter Items and create specialized lists
|
||||
class ItemKey {
|
||||
public:
|
||||
enum FlagBit {
|
||||
TYPE_SHAPE = 0, // Item is a Shape
|
||||
TYPE_LIGHT, // Item is a Light
|
||||
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
|
||||
DEFORMED, // Deformed within bound, not solid
|
||||
INVISIBLE, // Visible or not? could be just here to cast shadow
|
||||
SHADOW_CASTER, // Item cast shadows
|
||||
PICKABLE, // Item can be picked/selected
|
||||
LAYERED, // Item belongs to one of the layers different from the default layer
|
||||
|
||||
NUM_FLAGS, // Not a valid flag
|
||||
};
|
||||
typedef std::bitset<NUM_FLAGS> Flags;
|
||||
|
||||
// The key is the Flags
|
||||
Flags _flags;
|
||||
|
||||
ItemKey() : _flags(0) {}
|
||||
ItemKey(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
class Builder {
|
||||
friend class ItemKey;
|
||||
Flags _flags{ 0 };
|
||||
public:
|
||||
Builder() {}
|
||||
|
||||
ItemKey build() const { return ItemKey(_flags); }
|
||||
|
||||
Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); }
|
||||
Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); }
|
||||
Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); }
|
||||
Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); }
|
||||
Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); }
|
||||
Builder& withDeformed() { _flags.set(DEFORMED); return (*this); }
|
||||
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
||||
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
||||
Builder& withPickable() { _flags.set(PICKABLE); return (*this); }
|
||||
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
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 isShape() const { return _flags[TYPE_SHAPE]; }
|
||||
bool isLight() const { return _flags[TYPE_LIGHT]; }
|
||||
|
||||
bool isOpaque() const { return !_flags[TRANSLUCENT]; }
|
||||
bool isTransparent() const { return _flags[TRANSLUCENT]; }
|
||||
|
||||
bool isWorldSpace() const { return !_flags[VIEW_SPACE]; }
|
||||
bool isViewSpace() const { return _flags[VIEW_SPACE]; }
|
||||
|
||||
bool isStatic() const { return !_flags[DYNAMIC]; }
|
||||
bool isDynamic() const { return _flags[DYNAMIC]; }
|
||||
|
||||
bool isRigid() const { return !_flags[DEFORMED]; }
|
||||
bool isDeformed() const { return _flags[DEFORMED]; }
|
||||
|
||||
bool isVisible() const { return !_flags[INVISIBLE]; }
|
||||
bool isInvisible() const { return _flags[INVISIBLE]; }
|
||||
|
||||
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
|
||||
|
||||
bool isPickable() const { return _flags[PICKABLE]; }
|
||||
|
||||
bool isLayered() const { return _flags[LAYERED]; }
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) {
|
||||
debug << "[ItemKey: isOpaque:" << itemKey.isOpaque()
|
||||
<< ", isStatic:" << itemKey.isStatic()
|
||||
<< ", isWorldSpace:" << itemKey.isWorldSpace()
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
class ItemFilter {
|
||||
public:
|
||||
ItemKey::Flags _value{ 0 };
|
||||
ItemKey::Flags _mask{ 0 };
|
||||
|
||||
|
||||
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:
|
||||
Builder() {}
|
||||
|
||||
ItemFilter build() const { return ItemFilter(_value, _mask); }
|
||||
|
||||
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& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); }
|
||||
|
||||
Builder& withWorldSpace() { _value.reset(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
|
||||
Builder& withViewSpace() { _value.set(ItemKey::VIEW_SPACE); _mask.set(ItemKey::VIEW_SPACE); return (*this); }
|
||||
|
||||
Builder& withStatic() { _value.reset(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
|
||||
Builder& withDynamic() { _value.set(ItemKey::DYNAMIC); _mask.set(ItemKey::DYNAMIC); return (*this); }
|
||||
|
||||
Builder& withRigid() { _value.reset(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
|
||||
Builder& withDeformed() { _value.set(ItemKey::DEFORMED); _mask.set(ItemKey::DEFORMED); return (*this); }
|
||||
|
||||
Builder& withVisible() { _value.reset(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
|
||||
Builder& withInvisible() { _value.set(ItemKey::INVISIBLE); _mask.set(ItemKey::INVISIBLE); return (*this); }
|
||||
|
||||
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
|
||||
Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); 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 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); }
|
||||
|
||||
class Less {
|
||||
public:
|
||||
bool operator() (const ItemFilter& left, const ItemFilter& right) const {
|
||||
if (left._value.to_ulong() == right._value.to_ulong()) {
|
||||
return left._mask.to_ulong() < right._mask.to_ulong();
|
||||
} else {
|
||||
return left._value.to_ulong() < right._value.to_ulong();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const ItemFilter& me) {
|
||||
debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build())
|
||||
<< "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
||||
class Item {
|
||||
public:
|
||||
typedef std::vector<Item> Vector;
|
||||
typedef unsigned int ID;
|
||||
|
||||
static const ID INVALID_ITEM_ID = 0;
|
||||
|
||||
// Bound is the AABBox fully containing this item
|
||||
typedef AABox Bound;
|
||||
|
||||
// Status records the life history and performances of this item while performing at rendering and updating.
|
||||
// This is Used for monitoring and dynamically adjust the quality
|
||||
class Status {
|
||||
public:
|
||||
|
||||
// Status::Value class is the data used to represent the transient information of a status as a square icon
|
||||
// The "icon" is a square displayed in the 3D scene over the render::Item AABB center.
|
||||
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
|
||||
class Value {
|
||||
unsigned short _scale = 0xFFFF;
|
||||
unsigned char _color = 0xFF;
|
||||
unsigned char _icon = 0xFF;
|
||||
public:
|
||||
const static Value INVALID; // Invalid value meanss the status won't show
|
||||
|
||||
Value() {}
|
||||
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
|
||||
|
||||
// It can be scaled in the range [0, 1]
|
||||
void setScale(float scale);
|
||||
// the color hue in the range [0, 360] representing the color wheel hue
|
||||
void setColor(float hue);
|
||||
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
|
||||
// hopefully have an icon available to display (see DrawStatusJob)
|
||||
void setIcon(unsigned char icon);
|
||||
|
||||
// Standard color Hue
|
||||
static const float RED; // 0.0f;
|
||||
static const float YELLOW; // 60.0f;
|
||||
static const float GREEN; // 120.0f;
|
||||
static const float CYAN; // 180.0f;
|
||||
static const float BLUE; // 240.0f;
|
||||
static const float MAGENTA; // 300.0f;
|
||||
|
||||
// Retreive the Value data tightely packed as an int
|
||||
int getPackedData() const { return *((const int*) this); }
|
||||
};
|
||||
|
||||
typedef std::function<Value()> Getter;
|
||||
typedef std::vector<Getter> Getters;
|
||||
|
||||
Getters _values;
|
||||
|
||||
void addGetter(const Getter& getter) { _values.push_back(getter); }
|
||||
|
||||
size_t getNumValues() const { return _values.size(); }
|
||||
|
||||
using Values = std::vector <Value>;
|
||||
Values getCurrentValues() const;
|
||||
};
|
||||
typedef std::shared_ptr<Status> StatusPointer;
|
||||
|
||||
// Update Functor
|
||||
class UpdateFunctorInterface {
|
||||
public:
|
||||
virtual ~UpdateFunctorInterface() {}
|
||||
};
|
||||
typedef std::shared_ptr<UpdateFunctorInterface> UpdateFunctorPointer;
|
||||
|
||||
// Payload is whatever is in this Item and implement the Payload Interface
|
||||
class PayloadInterface {
|
||||
public:
|
||||
virtual const ItemKey getKey() const = 0;
|
||||
virtual const Bound getBound() const = 0;
|
||||
virtual int getLayer() const = 0;
|
||||
|
||||
virtual void render(RenderArgs* args) = 0;
|
||||
|
||||
virtual const ShapeKey getShapeKey() const = 0;
|
||||
|
||||
~PayloadInterface() {}
|
||||
|
||||
// Status interface is local to the base class
|
||||
const StatusPointer& getStatus() const { return _status; }
|
||||
void addStatusGetter(const Status::Getter& getter);
|
||||
void addStatusGetters(const Status::Getters& getters);
|
||||
|
||||
protected:
|
||||
StatusPointer _status;
|
||||
|
||||
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 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) const { _payload->render(args); }
|
||||
|
||||
// Shape Type Interface
|
||||
const ShapeKey getShapeKey() const { return _payload->getShapeKey(); }
|
||||
|
||||
// Access the status
|
||||
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
||||
|
||||
protected:
|
||||
PayloadPointer _payload;
|
||||
ItemKey _key;
|
||||
|
||||
friend class Scene;
|
||||
};
|
||||
|
||||
|
||||
typedef Item::UpdateFunctorInterface UpdateFunctorInterface;
|
||||
typedef Item::UpdateFunctorPointer UpdateFunctorPointer;
|
||||
typedef std::vector<UpdateFunctorPointer> UpdateFunctors;
|
||||
|
||||
template <class T> class UpdateFunctor : public Item::UpdateFunctorInterface {
|
||||
public:
|
||||
typedef std::function<void(T&)> Func;
|
||||
Func _func;
|
||||
|
||||
UpdateFunctor(Func func): _func(func) {}
|
||||
~UpdateFunctor() {}
|
||||
};
|
||||
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const Item& item) {
|
||||
debug << "[Item: _key:" << item.getKey() << ", bounds:" << item.getBound() << "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
// THe Payload class is the real Payload to be used
|
||||
// THis allow anything to be turned into a Payload as long as the required interface functions are available
|
||||
// When creating a new kind of payload from a new "stuff" class then you need to create specialized version for "stuff"
|
||||
// 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
|
||||
// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape.
|
||||
// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline,
|
||||
// implying that the shape will setup its own pipeline without the use of the ShapeKey.
|
||||
template <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
|
||||
|
||||
template <class T> class Payload : public Item::PayloadInterface {
|
||||
public:
|
||||
typedef std::shared_ptr<T> DataPointer;
|
||||
typedef UpdateFunctor<T> Updater;
|
||||
|
||||
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 ShapeKey getShapeKey() const { return shapeGetShapeKey<T>(_data); }
|
||||
|
||||
protected:
|
||||
DataPointer _data;
|
||||
|
||||
// Update mechanics
|
||||
virtual void update(const UpdateFunctorPointer& functor) { std::static_pointer_cast<Updater>(functor)->_func((*_data)); }
|
||||
friend class Item;
|
||||
};
|
||||
|
||||
// Let's show how to make a simple FooPayload example:
|
||||
/*
|
||||
class Foo {
|
||||
public:
|
||||
mutable ItemKey _myownKey;
|
||||
void makeMywnKey() const {
|
||||
_myownKey = ItemKey::Builder().withTypeShape().build();
|
||||
}
|
||||
|
||||
const Item::Bound evaluateMyBound() {
|
||||
// Do stuff here to get your final Bound
|
||||
return Item::Bound();
|
||||
}
|
||||
};
|
||||
|
||||
typedef Payload<Foo> FooPayload;
|
||||
typedef std::shared_ptr<Foo> FooPointer;
|
||||
|
||||
// In a Source file, not a header, implement the Payload interface function specialized for Foo:
|
||||
template <> const ItemKey payloadGetKey(const FooPointer& foo) {
|
||||
// Foo's way of provinding its Key
|
||||
foo->makeMyKey();
|
||||
return foo->_myownKey;
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const FooPointer& foo) {
|
||||
// evaluate Foo's own bound
|
||||
return foo->evaluateMyBound();
|
||||
}
|
||||
|
||||
// In this example, do not specialize the payloadRender call which means the compiler will use the default version which does nothing
|
||||
|
||||
*/
|
||||
// End of the example
|
||||
|
||||
typedef Item::PayloadPointer PayloadPointer;
|
||||
typedef std::vector< PayloadPointer > Payloads;
|
||||
|
||||
// A few typedefs for standard containers of ItemIDs
|
||||
typedef Item::ID ItemID;
|
||||
typedef std::vector<ItemID> ItemIDs;
|
||||
typedef std::set<ItemID> ItemIDSet;
|
||||
|
||||
class ItemIDAndBounds {
|
||||
public:
|
||||
ItemIDAndBounds(ItemID id) : id(id) { }
|
||||
ItemIDAndBounds(ItemID id, const AABox& bounds) : id(id), bounds(bounds) { }
|
||||
|
||||
ItemID id;
|
||||
AABox bounds;
|
||||
};
|
||||
|
||||
// A list of items to be passed between rendering jobs
|
||||
using ItemIDsBounds = std::vector<ItemIDAndBounds>;
|
||||
|
||||
// A map of items by ShapeKey to optimize rendering pipeline assignments
|
||||
using ShapesIDsBounds = std::unordered_map<ShapeKey, ItemIDsBounds, ShapeKey::Hash, ShapeKey::KeyEqual>;
|
||||
|
||||
|
||||
// A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly
|
||||
// A map of ItemIDSets allowing to create bucket lists of items which are filtered according to their key
|
||||
class ItemBucketMap : public std::map<ItemFilter, ItemIDSet, ItemFilter::Less> {
|
||||
public:
|
||||
|
||||
|
@ -501,8 +77,8 @@ public:
|
|||
/// Enqueue change batch to the scene
|
||||
void enqueuePendingChanges(const PendingChanges& pendingChanges);
|
||||
|
||||
/// Access the main bucketmap of items
|
||||
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
|
||||
// Process the penging changes equeued
|
||||
void processPendingChangesQueue();
|
||||
|
||||
/// Access a particular item form its ID
|
||||
/// WARNING, There is No check on the validity of the ID, so this could return a bad Item
|
||||
|
@ -510,11 +86,11 @@ public:
|
|||
|
||||
size_t getNumItems() const { return _items.size(); }
|
||||
|
||||
/// Access the main bucketmap of items
|
||||
const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; }
|
||||
|
||||
void processPendingChangesQueue();
|
||||
|
||||
|
||||
Octree _spatialTree;
|
||||
/// Access the item spatial tree
|
||||
const ItemSpatialTree& getSpatialTree() const { return _masterSpatialTree; }
|
||||
|
||||
protected:
|
||||
// Thread safe elements that can be accessed from anywhere
|
||||
|
@ -527,6 +103,8 @@ protected:
|
|||
std::mutex _itemsMutex;
|
||||
Item::Vector _items;
|
||||
ItemBucketMap _masterBucketMap;
|
||||
ItemSpatialTree _masterSpatialTree;
|
||||
|
||||
|
||||
void resetItems(const ItemIDs& ids, Payloads& payloads);
|
||||
void removeItems(const ItemIDs& ids);
|
||||
|
@ -535,8 +113,6 @@ protected:
|
|||
friend class Engine;
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef std::shared_ptr<Scene> ScenePointer;
|
||||
typedef std::vector<ScenePointer> Scenes;
|
||||
|
||||
|
|
Loading…
Reference in a new issue