mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-08 07:57:46 +02:00
Refining the culling test and defining the 2 new jobs FetchSPatialTree and CullSpatialSelection
This commit is contained in:
parent
edbcef20d4
commit
0478450205
7 changed files with 388 additions and 110 deletions
|
@ -52,7 +52,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
ShapePlumberPointer shapePlumber = std::make_shared<ShapePlumber>();
|
||||
initDeferredPipelines(*shapePlumber);
|
||||
|
||||
// CPU: Fetch the renderOpaques
|
||||
|
||||
/* // CPU: Fetch the renderOpaques
|
||||
const auto fetchedOpaques = addJob<FetchItems>("FetchOpaque");
|
||||
const auto culledOpaques = addJob<CullItems<RenderDetails::OPAQUE_ITEM>>("CullOpaque", fetchedOpaques, cullFunctor);
|
||||
const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", culledOpaques);
|
||||
|
@ -63,6 +64,18 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
const auto culledTransparents =
|
||||
addJob<CullItems<RenderDetails::TRANSLUCENT_ITEM>>("CullTransparent", fetchedTransparents, cullFunctor);
|
||||
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", culledTransparents, DepthSortItems(false));
|
||||
*/
|
||||
// CPU: Fetch the renderOpaques
|
||||
const auto opaqueSelection = addJob<FetchSpatialTree>("FetchOpaque");
|
||||
const auto culledOpaques = addJob<CullSpatialSelection>("CullOpaque", opaqueSelection, cullFunctor);
|
||||
const auto opaques = addJob<DepthSortItems>("DepthSortOpaque", culledOpaques);
|
||||
|
||||
// CPU only, create the list of renderedTransparents items
|
||||
const auto fetchedTransparents = addJob<FetchItems>("FetchTransparent", FetchItems(
|
||||
ItemFilter::Builder::transparentShape().withoutLayered()));
|
||||
const auto culledTransparents =
|
||||
addJob<CullItems<RenderDetails::TRANSLUCENT_ITEM>>("CullTransparent", fetchedTransparents, cullFunctor);
|
||||
const auto transparents = addJob<DepthSortItems>("DepthSortTransparent", culledTransparents, DepthSortItems(false));
|
||||
|
||||
// GPU Jobs: Start preparing the deferred and lighting buffer
|
||||
addJob<PrepareDeferred>("PrepareDeferred");
|
||||
|
|
|
@ -131,8 +131,6 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo
|
|||
|
||||
|
||||
void FetchItems::configure(const Config& config) {
|
||||
_justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum);
|
||||
_freezeFrustum = config.freezeFrustum;
|
||||
}
|
||||
|
||||
void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemBounds& outItems) {
|
||||
|
@ -143,7 +141,7 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
|
||||
outItems.clear();
|
||||
|
||||
/* const auto& bucket = scene->getMasterBucket();
|
||||
const auto& bucket = scene->getMasterBucket();
|
||||
const auto& items = bucket.find(_filter);
|
||||
if (items != bucket.end()) {
|
||||
outItems.reserve(items->second.size());
|
||||
|
@ -152,8 +150,27 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
outItems.emplace_back(ItemBound(id, item.getBound()));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FetchSpatialTree::configure(const Config& config) {
|
||||
_justFrozeFrustum = (config.freezeFrustum && !_freezeFrustum);
|
||||
_freezeFrustum = config.freezeFrustum;
|
||||
}
|
||||
|
||||
void FetchSpatialTree::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
RenderArgs* args = renderContext->args;
|
||||
auto& scene = sceneContext->_scene;
|
||||
|
||||
// start fresh
|
||||
outSelection.clear();
|
||||
|
||||
// Eventually use a frozen frustum
|
||||
auto queryFrustum = *args->_viewFrustum;
|
||||
if (_freezeFrustum) {
|
||||
if (_justFrozeFrustum) {
|
||||
|
@ -163,18 +180,110 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
queryFrustum = _frozenFrutstum;
|
||||
}
|
||||
|
||||
// Try that:
|
||||
ItemIDs fetchedItems;
|
||||
scene->getSpatialTree().fetch(fetchedItems, _filter, queryFrustum);
|
||||
// Octree selection!
|
||||
scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum);
|
||||
}
|
||||
|
||||
// Now get the bound, and filter individually against the _filter
|
||||
outItems.reserve(fetchedItems.size());
|
||||
for (auto id : fetchedItems) {
|
||||
void CullSpatialSelection::configure(const Config& config) {
|
||||
}
|
||||
|
||||
void CullSpatialSelection::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
RenderArgs* args = renderContext->args;
|
||||
auto& scene = sceneContext->_scene;
|
||||
ViewFrustum* frustum = args->_viewFrustum;
|
||||
|
||||
auto& details = args->_details.edit(_detailType);
|
||||
details._considered += inSelection.numItems();
|
||||
|
||||
// Culling Frustum / solidAngle test helper class
|
||||
struct Test {
|
||||
CullFunctor _functor;
|
||||
RenderArgs* _args;
|
||||
RenderDetails::Item& _renderDetails;
|
||||
|
||||
Test(CullFunctor& functor, RenderArgs* pargs, RenderDetails::Item& renderDetails) :
|
||||
_functor(functor),
|
||||
_args(pargs),
|
||||
_renderDetails(renderDetails)
|
||||
{}
|
||||
|
||||
bool frustumTest(const AABox& bound) {
|
||||
PerformanceTimer perfTimer("frustumItemTest");
|
||||
if (_args->_viewFrustum->boxInFrustum(bound) == ViewFrustum::OUTSIDE) {
|
||||
_renderDetails._outOfView++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solidAngleTest(const AABox& bound) {
|
||||
PerformanceTimer perfTimer("solidAngleItemTest");
|
||||
if (!_functor(_args, bound)) {
|
||||
_renderDetails._tooSmall++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Test test(_cullFunctor, args, details);
|
||||
|
||||
// Now we have a selection of items to render
|
||||
|
||||
outItems.clear();
|
||||
outItems.reserve(inSelection.numItems());
|
||||
|
||||
// Now get the bound, and
|
||||
// filter individually against the _filter
|
||||
// visibility cull if partially selected ( octree cell contianing it was partial)
|
||||
// distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item)
|
||||
|
||||
// inside & fit items: easy, just filter
|
||||
for (auto id : inSelection.insideItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
outItems.emplace_back(ItemBound(id, item.getBound()));
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
|
||||
// inside & subcell items: filter & distance cull
|
||||
for (auto id : inSelection.insideSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.solidAngleTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & fit items: filter & frustum cull
|
||||
for (auto id : inSelection.partialItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// partial & subcell items:: filter & frutum cull & solidangle cull
|
||||
for (auto id : inSelection.partialSubcellItems) {
|
||||
auto& item = scene->getItem(id);
|
||||
if (_filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
if (test.frustumTest(itemBound.bound)) {
|
||||
if (test.solidAngleTest(itemBound.bound)) {
|
||||
outItems.emplace_back(itemBound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
details._rendered += outItems.size();
|
||||
|
||||
std::static_pointer_cast<Config>(renderContext->jobConfig)->numItems = (int)outItems.size();
|
||||
}
|
||||
|
|
|
@ -23,29 +23,16 @@ namespace render {
|
|||
const ItemBounds& inItems, ItemBounds& outItems);
|
||||
void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems);
|
||||
|
||||
|
||||
class FetchItemsConfig : public Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numItems READ getNumItems)
|
||||
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
|
||||
public:
|
||||
int getNumItems() { return numItems; }
|
||||
|
||||
int numItems{ 0 };
|
||||
|
||||
bool freezeFrustum{ false };
|
||||
|
||||
public slots:
|
||||
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class FetchItems {
|
||||
bool _freezeFrustum{ false }; // initialized by Config
|
||||
bool _justFrozeFrustum{ false };
|
||||
ViewFrustum _frozenFrutstum;
|
||||
public:
|
||||
using Config = FetchItemsConfig;
|
||||
using JobModel = Job::ModelO<FetchItems, ItemBounds, Config>;
|
||||
|
@ -79,6 +66,69 @@ namespace render {
|
|||
CullFunctor _cullFunctor;
|
||||
};
|
||||
|
||||
|
||||
class FetchSpatialTreeConfig : public Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numItems READ getNumItems)
|
||||
Q_PROPERTY(bool freezeFrustum MEMBER freezeFrustum WRITE setFreezeFrustum)
|
||||
public:
|
||||
int numItems{ 0 };
|
||||
int getNumItems() { return numItems; }
|
||||
|
||||
bool freezeFrustum{ false };
|
||||
public slots:
|
||||
void setFreezeFrustum(bool enabled) { freezeFrustum = enabled; emit dirty(); }
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
class FetchSpatialTree {
|
||||
bool _freezeFrustum{ false }; // initialized by Config
|
||||
bool _justFrozeFrustum{ false };
|
||||
ViewFrustum _frozenFrutstum;
|
||||
public:
|
||||
using Config = FetchSpatialTreeConfig;
|
||||
using JobModel = Job::ModelO<FetchSpatialTree, ItemSpatialTree::ItemSelection, Config>;
|
||||
|
||||
FetchSpatialTree() {}
|
||||
FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {}
|
||||
|
||||
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection);
|
||||
};
|
||||
|
||||
class CullSpatialSelectionConfig : public Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numItems READ getNumItems)
|
||||
public:
|
||||
int numItems{ 0 };
|
||||
int getNumItems() { return numItems; }
|
||||
};
|
||||
|
||||
class CullSpatialSelection {
|
||||
public:
|
||||
using Config = CullSpatialSelectionConfig;
|
||||
using JobModel = Job::ModelIO<CullSpatialSelection, ItemSpatialTree::ItemSelection, ItemBounds, Config>;
|
||||
|
||||
CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type, const ItemFilter& filter) :
|
||||
_cullFunctor{ cullFunctor },
|
||||
_detailType(type),
|
||||
_filter(filter) {}
|
||||
|
||||
CullSpatialSelection(CullFunctor cullFunctor) :
|
||||
_cullFunctor{ cullFunctor } {}
|
||||
|
||||
CullFunctor _cullFunctor;
|
||||
RenderDetails::Type _detailType{ RenderDetails::OPAQUE_ITEM };
|
||||
ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() };
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& outSelection, ItemBounds& outItems);
|
||||
};
|
||||
|
||||
class DepthSortItems {
|
||||
public:
|
||||
using JobModel = Job::ModelIO<DepthSortItems, ItemBounds, ItemBounds>;
|
||||
|
@ -90,4 +140,4 @@ namespace render {
|
|||
};
|
||||
}
|
||||
|
||||
#endif // hifi_render_CullTask_h
|
||||
#endif // hifi_render_CullTask_h;
|
|
@ -98,9 +98,8 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext,
|
|||
}
|
||||
|
||||
// Try that:
|
||||
render::Octree::Indices itemsBrick;
|
||||
render::Octree::Indices itemsCell;
|
||||
scene->getSpatialTree().select(itemsBrick, itemsCell, queryFrustum);
|
||||
Octree::CellSelection selection;
|
||||
scene->getSpatialTree().selectCells(selection, queryFrustum);
|
||||
|
||||
|
||||
// Allright, something to render let's do it
|
||||
|
@ -118,12 +117,21 @@ void DrawSceneOctree::run(const SceneContextPointer& sceneContext,
|
|||
// bind the one gpu::Pipeline we need
|
||||
batch.setPipeline(getDrawCellBoundsPipeline());
|
||||
|
||||
/* const auto& inCells = scene->getSpatialTree()._cells;
|
||||
for (const auto& cellID : selection.insideCells) {
|
||||
auto cell = scene->getSpatialTree().getConcreteCell(cellID);
|
||||
|
||||
for (const auto& cell: inCells ) {
|
||||
*/
|
||||
|
||||
for (const auto& cellID : itemsCell) {
|
||||
auto cellLoc = cell.getlocation();
|
||||
|
||||
glm::ivec4 cellLocation(cellLoc.pos.x, cellLoc.pos.y, cellLoc.pos.z, cellLoc.depth);
|
||||
if (cell.isBrickEmpty() || !cell.hasBrick()) {
|
||||
cellLocation.w *= -1;
|
||||
}
|
||||
|
||||
batch._glUniform4iv(_drawCellLocationLoc, 1, ((const int*)(&cellLocation)));
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
for (const auto& cellID : selection.partialCells) {
|
||||
auto cell = scene->getSpatialTree().getConcreteCell(cellID);
|
||||
|
||||
auto cellLoc = cell.getlocation();
|
||||
|
|
|
@ -59,6 +59,9 @@ public:
|
|||
ItemKey() : _flags(0) {}
|
||||
ItemKey(const Flags& flags) : _flags(flags) {}
|
||||
|
||||
bool operator== (const ItemKey& rhs) const { return _flags == rhs._flags; }
|
||||
bool operator!= (const ItemKey& rhs) const { return _flags != rhs._flags; }
|
||||
|
||||
class Builder {
|
||||
friend class ItemKey;
|
||||
Flags _flags{ 0 };
|
||||
|
|
|
@ -34,6 +34,24 @@ const double Octree::INV_DEPTH_DIM[] = {
|
|||
1.0 / 16384.0,
|
||||
1.0 / 32768.0 };
|
||||
|
||||
const float Octree::COORD_SUBCELL_WIDTH[] = { // 2 ^ MAX_DEPTH / 2 ^ (depth + 1)
|
||||
16384.0f,
|
||||
8192.0f,
|
||||
4096.0f,
|
||||
2048.0f,
|
||||
1024.0f,
|
||||
512.0f,
|
||||
256.0f,
|
||||
128.0f,
|
||||
64.0f,
|
||||
32.0f,
|
||||
16.0f,
|
||||
8.0f,
|
||||
4.0f,
|
||||
2.0f,
|
||||
1.0f,
|
||||
0.5f };
|
||||
|
||||
|
||||
Octree::Location::vector Octree::Location::pathTo(const Location& dest) {
|
||||
Location current{ dest };
|
||||
|
@ -169,11 +187,10 @@ Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const
|
|||
ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey& key, const ItemID& item) {
|
||||
// Add the item to the brick (and a brick if needed)
|
||||
auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) {
|
||||
if (key.isSmall()) {
|
||||
brick.items.push_back(item);
|
||||
} else {
|
||||
brick.subcellItems.push_back(item);
|
||||
}
|
||||
auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items);
|
||||
|
||||
itemIn.push_back(item);
|
||||
|
||||
cell.signalBrickFilled();
|
||||
}, true);
|
||||
|
||||
|
@ -183,6 +200,10 @@ ItemSpatialTree::Index ItemSpatialTree::insertItem(Index cellIdx, const ItemKey&
|
|||
bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item) {
|
||||
auto success = false;
|
||||
|
||||
// only if key changed
|
||||
assert(oldKey != key);
|
||||
|
||||
// Get to the brick where the item is and update where it s stored
|
||||
auto brickID = accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index cellID) {
|
||||
auto& itemIn = (key.isSmall() ? brick.subcellItems : brick.items);
|
||||
auto& itemOut = (oldKey.isSmall() ? brick.subcellItems : brick.items);
|
||||
|
@ -197,12 +218,12 @@ bool ItemSpatialTree::updateItem(Index cellIdx, const ItemKey& oldKey, const Ite
|
|||
bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID& item) {
|
||||
auto success = false;
|
||||
|
||||
// Access the brick at the cell (without createing new ones)
|
||||
// Remove the item from the brick
|
||||
accessCellBrick(cellIdx, [&](Cell& cell, Brick& brick, Octree::Index brickID) {
|
||||
// remove the item from the list
|
||||
auto& itemList = (key.isSmall() ? brick.subcellItems : brick.items);
|
||||
|
||||
itemList.erase(std::find(itemList.begin(), itemList.end(), item));
|
||||
|
||||
if (brick.items.empty() && brick.subcellItems.empty()) {
|
||||
cell.signalBrickEmpty();
|
||||
}
|
||||
|
@ -211,33 +232,6 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID
|
|||
|
||||
return success;
|
||||
}
|
||||
/*
|
||||
ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const Location& location, const ItemID& item) {
|
||||
auto newCell = indexCell(location);
|
||||
|
||||
// Early exit if nothing changed
|
||||
if (newCell == oldCell) {
|
||||
return newCell;
|
||||
}
|
||||
// do we know about this item ?
|
||||
else if (oldCell == Item::INVALID_CELL) {
|
||||
insertItem(newCell, item);
|
||||
return newCell;
|
||||
}
|
||||
// A true update of cell is required
|
||||
else {
|
||||
// Add the item to the brick (and a brick if needed)
|
||||
accessCellBrick(newCell, [&](Cell& cell, Brick& brick, Octree::Index cellID) {
|
||||
brick.items.push_back(item);
|
||||
cell.signalBrickFilled();
|
||||
}, true);
|
||||
|
||||
// And remove it from the previous one
|
||||
removeItem(oldCell, item);
|
||||
|
||||
return newCell;
|
||||
}
|
||||
}*/
|
||||
|
||||
ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) {
|
||||
auto minCoordf = evalCoordf(bound.getMinimumPoint());
|
||||
|
@ -255,8 +249,9 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey&
|
|||
|
||||
auto newCell = indexCell(location);
|
||||
|
||||
// Early exit if nothing changed
|
||||
// Staying in the same cell
|
||||
if (newCell == oldCell) {
|
||||
// Did the key changed, if yes update
|
||||
if (newKey._flags != oldKey._flags) {
|
||||
updateItem(newCell, oldKey, newKey, item);
|
||||
}
|
||||
|
@ -279,11 +274,10 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey&
|
|||
}
|
||||
}
|
||||
|
||||
int Octree::select(Indices& selectedBricks, Indices& selectedCells, const glm::vec4 frustum[6]) const {
|
||||
int Octree::select(CellSelection& selection, const FrustumSelector& selector) const {
|
||||
|
||||
Index cellID = ROOT_CELL;
|
||||
selectTraverse(cellID, selectedBricks, selectedCells, frustum);
|
||||
return selectedCells.size();
|
||||
return selectTraverse(cellID, selection, selector);
|
||||
}
|
||||
|
||||
|
||||
|
@ -345,11 +339,13 @@ Octree::Location::Intersection Octree::Location::intersectCell(const Location& c
|
|||
return Inside;
|
||||
}
|
||||
|
||||
int Octree::selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const {
|
||||
int numSelectedsIn = selectedCells.size();
|
||||
int Octree::selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
|
||||
int numSelectedsIn = selection.size();
|
||||
auto cell = getConcreteCell(cellID);
|
||||
|
||||
auto intersection = Octree::Location::intersectCell(cell.getlocation(), frustum);
|
||||
auto cellLocation = cell.getlocation();
|
||||
|
||||
auto intersection = Octree::Location::intersectCell(cellLocation, selector.frustum);
|
||||
switch (intersection) {
|
||||
case Octree::Location::Outside:
|
||||
// cell is outside, stop traversing this branch
|
||||
|
@ -357,82 +353,110 @@ int Octree::selectTraverse(Index cellID, Indices& selectedBricks, Indices& selec
|
|||
break;
|
||||
case Octree::Location::Inside: {
|
||||
// traverse all the Cell Branch and collect items in the selection
|
||||
selectBranch(cellID, selectedBricks, selectedCells, frustum);
|
||||
selectBranch(cellID, selection, selector);
|
||||
break;
|
||||
}
|
||||
case Octree::Location::Intersect:
|
||||
default: {
|
||||
// Cell is partially in
|
||||
|
||||
// Select within this cell
|
||||
selectInCell(cellID, selectedBricks, selectedCells, frustum);
|
||||
// Test for lod
|
||||
auto cellLocation = cell.getlocation();
|
||||
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (lod < 0.0) {
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Select this cell partially in frustum
|
||||
selectCellBrick(cellID, selection, false);
|
||||
|
||||
// then traverse deeper
|
||||
for (int i = 0; i < NUM_OCTANTS; i++) {
|
||||
Index subCellID = cell.child((Link)i);
|
||||
if (subCellID != INVALID) {
|
||||
selectTraverse(subCellID, selectedBricks, selectedCells, frustum);
|
||||
selectTraverse(subCellID, selection, selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedCells.size() - numSelectedsIn;
|
||||
return selection.size() - numSelectedsIn;
|
||||
}
|
||||
|
||||
|
||||
int Octree::selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const {
|
||||
int numSelectedsIn = selectedCells.size();
|
||||
int Octree::selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const {
|
||||
int numSelectedsIn = selection.size();
|
||||
auto cell = getConcreteCell(cellID);
|
||||
|
||||
// Select within this cell
|
||||
selectInCell(cellID, selectedBricks, selectedCells, frustum);
|
||||
auto cellLocation = cell.getlocation();
|
||||
float lod = selector.testSolidAngle(cellLocation.getCenter(), Octree::getCoordSubcellWidth(cellLocation.depth));
|
||||
if (lod < 0.0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Select this cell fully inside the frustum
|
||||
selectCellBrick(cellID, selection, true);
|
||||
|
||||
// then traverse deeper
|
||||
for (int i = 0; i < NUM_OCTANTS; i++) {
|
||||
Index subCellID = cell.child((Link)i);
|
||||
if (subCellID != INVALID) {
|
||||
selectBranch(subCellID, selectedBricks, selectedCells, frustum);
|
||||
selectBranch(subCellID, selection, selector);
|
||||
}
|
||||
}
|
||||
|
||||
return selectedCells.size() - numSelectedsIn;
|
||||
return selection.size() - numSelectedsIn;
|
||||
}
|
||||
|
||||
int Octree::selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const {
|
||||
int numSelectedsIn = selectedCells.size();
|
||||
int Octree::selectCellBrick(Index cellID, CellSelection& selection, bool inside) const {
|
||||
int numSelectedsIn = selection.size();
|
||||
auto cell = getConcreteCell(cellID);
|
||||
selectedCells.push_back(cellID);
|
||||
selection.cells(inside).push_back(cellID);
|
||||
|
||||
if (!cell.isBrickEmpty()) {
|
||||
// Collect the items of this cell
|
||||
selectedBricks.push_back(cell.brick());
|
||||
selection.bricks(inside).push_back(cell.brick());
|
||||
}
|
||||
|
||||
return selectedCells.size() - numSelectedsIn;
|
||||
return selection.size() - numSelectedsIn;
|
||||
}
|
||||
|
||||
|
||||
int ItemSpatialTree::select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const {
|
||||
int ItemSpatialTree::selectCells(CellSelection& selection, const ViewFrustum& frustum) const {
|
||||
auto worldPlanes = frustum.getPlanes();
|
||||
Coord4f planes[6];
|
||||
FrustumSelector selector;
|
||||
for (int i = 0; i < ViewFrustum::NUM_PLANES; i++) {
|
||||
::Plane octPlane;
|
||||
octPlane.setNormalAndPoint(worldPlanes[i].getNormal(), evalCoordf(worldPlanes[i].getPoint(), ROOT_DEPTH));
|
||||
planes[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
|
||||
selector.frustum[i] = Coord4f(octPlane.getNormal(), octPlane.getDCoefficient());
|
||||
}
|
||||
return Octree::select(selectedBricks, selectedCells, planes);
|
||||
|
||||
selector.eyePos = evalCoordf(frustum.getPosition(), ROOT_DEPTH);
|
||||
selector.setAngle(glm::radians(2.0f));
|
||||
|
||||
return Octree::select(selection, selector);
|
||||
}
|
||||
|
||||
int ItemSpatialTree::fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const {
|
||||
Indices bricks;
|
||||
Indices cells;
|
||||
select(bricks, cells, frustum);
|
||||
int ItemSpatialTree::selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const {
|
||||
selectCells(selection.cellSelection, frustum);
|
||||
|
||||
for (auto brickId : bricks) {
|
||||
// Just grab the items in every selected bricks
|
||||
for (auto brickId : selection.cellSelection.insideBricks) {
|
||||
auto& brickItems = getConcreteBrick(brickId).items;
|
||||
fetchedItems.insert(fetchedItems.end(), brickItems.begin(), brickItems.end());
|
||||
selection.insideItems.insert(selection.insideItems.end(), brickItems.begin(), brickItems.end());
|
||||
|
||||
auto& brickSubcellItems = getConcreteBrick(brickId).subcellItems;
|
||||
selection.insideSubcellItems.insert(selection.insideSubcellItems.end(), brickSubcellItems.begin(), brickSubcellItems.end());
|
||||
}
|
||||
|
||||
return bricks.size();
|
||||
for (auto brickId : selection.cellSelection.partialBricks) {
|
||||
auto& brickItems = getConcreteBrick(brickId).items;
|
||||
selection.partialItems.insert(selection.partialItems.end(), brickItems.begin(), brickItems.end());
|
||||
|
||||
auto& brickSubcellItems = getConcreteBrick(brickId).subcellItems;
|
||||
selection.partialSubcellItems.insert(selection.partialSubcellItems.end(), brickSubcellItems.begin(), brickSubcellItems.end());
|
||||
}
|
||||
|
||||
return selection.numItems();
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ namespace render {
|
|||
|
||||
static int getDepthDimension(Depth depth) { return 1 << depth; }
|
||||
static double getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; }
|
||||
static float getCoordSubcellWidth(Depth depth) { return (float) (1.7320 * getInvDepthDimension(depth) * 0.5); }
|
||||
|
||||
// Need 16bits integer coordinates on each axes: 32768 cell positions
|
||||
using Coord = int16_t;
|
||||
|
@ -128,6 +129,11 @@ namespace render {
|
|||
uint8_t spare{ 0 };
|
||||
Depth depth{ ROOT_DEPTH };
|
||||
|
||||
Coord3f getCenter() const {
|
||||
Coord3f center = (Coord3f(pos) + Coord3f(0.5f)) * Coord3f(Octree::getInvDepthDimension(depth));
|
||||
return center;
|
||||
}
|
||||
|
||||
bool operator== (const Location& right) const { return pos == right.pos && depth == right.depth; }
|
||||
|
||||
// Eval the octant of this cell relative to its parent
|
||||
|
@ -138,6 +144,7 @@ namespace render {
|
|||
Location parent() const { return Location{ (pos >> Coord3(1)), Depth(depth <= 0 ? 0 : depth - 1) }; }
|
||||
Location child(Link octant) const { return Location{ (pos << Coord3(1)) | octantAxes(octant), Depth(depth + 1) }; }
|
||||
|
||||
|
||||
// Eval the list of locations (the path) from root to the destination.
|
||||
// the root cell is included
|
||||
static vector pathTo(const Location& destination);
|
||||
|
@ -152,7 +159,6 @@ namespace render {
|
|||
Inside,
|
||||
};
|
||||
static Intersection intersectCell(const Location& cell, const Coord4f frustum[6]);
|
||||
|
||||
};
|
||||
using Locations = Location::vector;
|
||||
|
||||
|
@ -243,11 +249,51 @@ namespace render {
|
|||
return _bricks[index];
|
||||
}
|
||||
|
||||
// Selection and traverse
|
||||
int select(Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const;
|
||||
int selectTraverse(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const;
|
||||
int selectBranch(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const;
|
||||
int selectInCell(Index cellID, Indices& selectedBricks, Indices& selectedCells, const Coord4f frustum[6]) const;
|
||||
// Cell Selection and traversal
|
||||
|
||||
class CellSelection {
|
||||
public:
|
||||
Indices insideCells;
|
||||
Indices insideBricks;
|
||||
Indices partialCells;
|
||||
Indices partialBricks;
|
||||
|
||||
Indices& cells(bool inside) { return (inside ? insideCells : partialCells); }
|
||||
Indices& bricks(bool inside) { return (inside ? insideBricks : partialBricks); }
|
||||
|
||||
int size() const { return insideBricks.size() + partialBricks.size(); }
|
||||
|
||||
void clear() {
|
||||
insideCells.clear();
|
||||
insideBricks.clear();
|
||||
partialCells.clear();
|
||||
partialBricks.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class FrustumSelector {
|
||||
public:
|
||||
Coord4f frustum[6];
|
||||
Coord3f eyePos;
|
||||
float angle;
|
||||
float squareTanAlpha;
|
||||
|
||||
void setAngle(float a) {
|
||||
angle = std::max(glm::radians(1.0f/60.0f), a); // no better than 1 minute of degree
|
||||
auto tanAlpha = tan(angle);
|
||||
squareTanAlpha = (float)(tanAlpha * tanAlpha);
|
||||
}
|
||||
|
||||
float testSolidAngle(const Coord3f& point, float size) const {
|
||||
auto eyeToPoint = point - eyePos;
|
||||
return (size * size / glm::dot(eyeToPoint, eyeToPoint)) - squareTanAlpha;
|
||||
}
|
||||
};
|
||||
|
||||
int select(CellSelection& selection, const FrustumSelector& selector) const;
|
||||
int selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const;
|
||||
int selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const;
|
||||
int selectCellBrick(Index cellID, CellSelection& selection, bool inside) const;
|
||||
|
||||
protected:
|
||||
Index allocateCell(Index parent, const Location& location);
|
||||
|
@ -314,8 +360,33 @@ namespace render {
|
|||
Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey);
|
||||
|
||||
// Selection and traverse
|
||||
int select(Indices& selectedBricks, Indices& selectedCells, const ViewFrustum& frustum) const;
|
||||
int fetch(ItemIDs& fetchedItems, const ItemFilter& filter, const ViewFrustum& frustum) const;
|
||||
int selectCells(CellSelection& selection, const ViewFrustum& frustum) const;
|
||||
|
||||
class ItemSelection {
|
||||
public:
|
||||
CellSelection cellSelection;
|
||||
ItemIDs insideItems;
|
||||
ItemIDs insideSubcellItems;
|
||||
ItemIDs partialItems;
|
||||
ItemIDs partialSubcellItems;
|
||||
|
||||
ItemIDs& items(bool inside) { return (inside ? insideItems : partialItems); }
|
||||
ItemIDs& subcellItems(bool inside) { return (inside ? insideSubcellItems : partialSubcellItems); }
|
||||
|
||||
int insideNumItems() const { return insideItems.size() + insideSubcellItems.size(); }
|
||||
int partialNumItems() const { return partialItems.size() + partialSubcellItems.size(); }
|
||||
int numItems() const { return insideNumItems() + partialNumItems(); }
|
||||
|
||||
void clear() {
|
||||
cellSelection.clear();
|
||||
insideItems.clear();
|
||||
insideSubcellItems.clear();
|
||||
partialItems.clear();
|
||||
partialSubcellItems.clear();
|
||||
}
|
||||
};
|
||||
|
||||
int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue