Refining the culling test and defining the 2 new jobs FetchSPatialTree and CullSpatialSelection

This commit is contained in:
samcake 2016-02-03 18:38:57 -08:00
parent edbcef20d4
commit 0478450205
7 changed files with 388 additions and 110 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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