Clean up and document Chunk code

This commit is contained in:
kalbl
2016-10-27 21:51:03 +02:00
parent 4548394f1c
commit 708ddd8400
8 changed files with 164 additions and 108 deletions
+26 -20
View File
@@ -43,14 +43,15 @@ namespace globebrowsing {
const float Chunk::DEFAULT_HEIGHT = 0.0f;
Chunk::Chunk(const RenderableGlobe& owner, const TileIndex& tileIndex, bool initVisible)
Chunk::Chunk(
const RenderableGlobe& owner,
const TileIndex& tileIndex,
bool initVisible)
: _owner(owner)
, _surfacePatch(tileIndex)
, _tileIndex(tileIndex)
, _isVisible(initVisible)
{
}
{ }
const GeodeticPatch& Chunk::surfacePatch() const {
return _surfacePatch;
@@ -68,15 +69,12 @@ namespace globebrowsing {
return _isVisible;
}
void Chunk::setIndex(const TileIndex& index) {
_tileIndex = index;
_surfacePatch = GeodeticPatch(index);
}
Chunk::Status Chunk::update(const RenderData& data) {
auto savedCamera = _owner.savedCamera();
const Camera& camRef = savedCamera != nullptr ? *savedCamera : data.camera;
RenderData myRenderData = { camRef, data.position, data.doPerformanceMeasurement };
const Camera& camRef =
savedCamera != nullptr ? *savedCamera : data.camera;
RenderData myRenderData =
{ camRef, data.position, data.doPerformanceMeasurement };
_isVisible = true;
@@ -102,13 +100,17 @@ namespace globebrowsing {
// One must also handle how to sample pick one out of multiplte heightmaps
auto layerManager = owner().chunkedLodGlobe()->layerManager();
auto heightMapProviders = layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
auto heightMapProviders =
layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
// The raster of a height map is the first one. We assume that the height map is
// a single raster image. If it is not we will just use the first raster
// (that is channel 0).
size_t HEIGHT_CHANNEL = 0;
const LayerGroup& heightmaps = layerManager->layerGroup(LayerManager::HeightLayers);
std::vector<ChunkTile> tiles = TileSelector::getTilesSortedByHighestResolution(heightmaps, _tileIndex);
const LayerGroup& heightmaps =
layerManager->layerGroup(LayerManager::HeightLayers);
std::vector<ChunkTile> tiles =
TileSelector::getTilesSortedByHighestResolution(heightmaps, _tileIndex);
bool lastHadMissingData = true;
for (auto tile : tiles) {
bool goodTile = tile.tile.status == Tile::Status::OK;
@@ -119,8 +121,10 @@ namespace globebrowsing {
if (!boundingHeights.available) {
if (preprocessData->hasMissingData[HEIGHT_CHANNEL]) {
boundingHeights.min = std::min(DEFAULT_HEIGHT, preprocessData->minValues[HEIGHT_CHANNEL]);
boundingHeights.max = std::max(DEFAULT_HEIGHT, preprocessData->maxValues[HEIGHT_CHANNEL]);
boundingHeights.min = std::min(
DEFAULT_HEIGHT, preprocessData->minValues[HEIGHT_CHANNEL]);
boundingHeights.max = std::max(
DEFAULT_HEIGHT, preprocessData->maxValues[HEIGHT_CHANNEL]);
}
else {
boundingHeights.min = preprocessData->minValues[HEIGHT_CHANNEL];
@@ -129,8 +133,10 @@ namespace globebrowsing {
boundingHeights.available = true;
}
else {
boundingHeights.min = std::min(boundingHeights.min, preprocessData->minValues[HEIGHT_CHANNEL]);
boundingHeights.max = std::max(boundingHeights.max, preprocessData->maxValues[HEIGHT_CHANNEL]);
boundingHeights.min = std::min(
boundingHeights.min, preprocessData->minValues[HEIGHT_CHANNEL]);
boundingHeights.max = std::max(
boundingHeights.max, preprocessData->maxValues[HEIGHT_CHANNEL]);
}
lastHadMissingData = preprocessData->hasMissingData[HEIGHT_CHANNEL];
}
+31 -9
View File
@@ -46,7 +46,6 @@ namespace globebrowsing {
class Chunk {
public:
const static float DEFAULT_HEIGHT;
struct BoundingHeights {
@@ -60,28 +59,51 @@ namespace globebrowsing {
WANT_SPLIT,
};
Chunk(const RenderableGlobe& owner, const TileIndex& tileIndex, bool initVisible = true);
Chunk(
const RenderableGlobe& owner,
const TileIndex& tileIndex,
bool initVisible = true);
/// Updates chunk internally and returns a desired level
/**
* Updates the Chunk internally and returns the Status of the Chunk.
*
* Tests if the Chunk is cullable and gets the desired level of the Chunk. If the
* Chunk is cullable it will be set to invisible and return Status::WANT_MERGE.
* If the desired level is smaller than the current level of the chunk it will
* return Status::WANT_MERGE, if it is larger it will return Status::WANT_SPLIT,
* otherwise Status::DO_NOTHING.
*
* \returns The Status of the chunk.
*/
Status update(const RenderData& data);
/**
* Returns a convex polyhedron of eight vertices tightly bounding the volume of
* the Chunk.
*/
std::vector<glm::dvec4> getBoundingPolyhedronCorners() const;
const GeodeticPatch& surfacePatch() const;
const RenderableGlobe& owner() const;
const TileIndex tileIndex() const;
bool isVisible() const;
/**
* Returns BoundingHeights that fits the Chunk as tightly as possible.
*
* If the Chunk uses more than one HightLayer, the BoundingHeights will be set
* to cover all HightLayers. If the Chunk has a higher level than its highest
* resolution HightLayer Tile, it will base its BoundingHeights on that Tile.
* This means that high level Chunks can have BoundingHeights that are not
* tightly fitting.
*/
BoundingHeights getBoundingHeights() const;
void setIndex(const TileIndex& index);
private:
const RenderableGlobe& _owner;
TileIndex _tileIndex;
const TileIndex _tileIndex;
bool _isVisible;
GeodeticPatch _surfacePatch;
const GeodeticPatch _surfacePatch;
};
} // namespace globebrowsing
@@ -42,9 +42,12 @@ namespace {
namespace openspace {
namespace globebrowsing {
int EvaluateChunkLevelByDistance::getDesiredLevel(const Chunk& chunk, const RenderData& data) const {
// Calculations are done in the reference frame of the globe. Hence, the camera
// position needs to be transformed with the inverse model matrix
int EvaluateChunkLevelByDistance::getDesiredLevel(
const Chunk& chunk,
const RenderData& data) const {
// Calculations are done in the reference frame of the globe
// (model space). Hence, the camera position needs to be transformed
// with the inverse model matrix
glm::dmat4 inverseModelTransform = chunk.owner().inverseModelTransform();
const RenderableGlobe& globe = chunk.owner();
const Ellipsoid& ellipsoid = globe.ellipsoid();
@@ -69,21 +72,26 @@ namespace globebrowsing {
Scalar distanceToPatch = glm::length(cameraToChunk);
Scalar distance = distanceToPatch;
Scalar scaleFactor = globe.generalProperties().lodScaleFactor * ellipsoid.minimumRadius();
Scalar scaleFactor =
globe.generalProperties().lodScaleFactor * ellipsoid.minimumRadius();
Scalar projectedScaleFactor = scaleFactor / distance;
int desiredLevel = ceil(log2(projectedScaleFactor));
return desiredLevel;
}
int EvaluateChunkLevelByProjectedArea::getDesiredLevel(const Chunk& chunk, const RenderData& data) const {
// Calculations are done in the reference frame of the globe. Hence, the camera
// position needs to be transformed with the inverse model matrix
int EvaluateChunkLevelByProjectedArea::getDesiredLevel(
const Chunk& chunk,
const RenderData& data) const {
// Calculations are done in the reference frame of the globe
// (model space). Hence, the camera position needs to be transformed
// with the inverse model matrix
glm::dmat4 inverseModelTransform = chunk.owner().inverseModelTransform();
const RenderableGlobe& globe = chunk.owner();
const Ellipsoid& ellipsoid = globe.ellipsoid();
glm::dvec4 cameraPositionModelSpace = glm::dvec4(data.camera.positionVec3(), 1);
Vec3 cameraPosition = glm::dvec3(inverseModelTransform * cameraPositionModelSpace);
Vec3 cameraPosition =
glm::dvec3(inverseModelTransform * cameraPositionModelSpace);
Vec3 cameraToEllipsoidCenter = -cameraPosition;
Geodetic2 cameraGeodeticPos = ellipsoid.cartesianToGeodetic2(cameraPosition);
@@ -95,8 +103,8 @@ namespace globebrowsing {
// full patch is very curved (e.g. stretches from latitude 0 to 90 deg).
const Geodetic2 center = chunk.surfacePatch().center();
const Geodetic2 closestCorner = chunk.surfacePatch().closestCorner(cameraGeodeticPos);
const Geodetic2 closestCorner =
chunk.surfacePatch().closestCorner(cameraGeodeticPos);
// Camera
// |
@@ -112,8 +120,6 @@ namespace globebrowsing {
// | center |
// | |
// +-----------------+ <-- south east corner
Chunk::BoundingHeights heights = chunk.getBoundingHeights();
const Geodetic3 c = { center, heights.min };
@@ -154,32 +160,37 @@ namespace globebrowsing {
//
// If the geodetic patch is small (i.e. has small width), that means the patch in
// cartesian space will be almost flat, and in turn, the triangle ABC will roughly
// cartesian space will be almost flat, and in turn, the triangle ABC will roughly
// correspond to 1/8 of the full area
const Vec3 AB = B - A;
const Vec3 AC = C - A;
double areaABC = 0.5 * glm::length(glm::cross(AC, AB));
double projectedChunkAreaApprox = 8 * areaABC;
double scaledArea = globe.generalProperties().lodScaleFactor * projectedChunkAreaApprox;
double scaledArea =
globe.generalProperties().lodScaleFactor * projectedChunkAreaApprox;
return chunk.tileIndex().level + round(scaledArea - 1);
}
int EvaluateChunkLevelByAvailableTileData::getDesiredLevel(const Chunk& chunk, const RenderData& data) const {
int EvaluateChunkLevelByAvailableTileData::getDesiredLevel(
const Chunk& chunk,
const RenderData& data) const {
auto layerManager = chunk.owner().chunkedLodGlobe()->layerManager();
auto heightLayers = layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
auto heightLayers =
layerManager->layerGroup(LayerManager::HeightLayers).activeLayers();
int currLevel = chunk.tileIndex().level;
for (size_t i = 0; i < LayerManager::NUM_LAYER_GROUPS; i++) {
for (auto& layer : layerManager->layerGroup(i).activeLayers()) {
Tile::Status tileStatus = layer->tileProvider()->getTileStatus(chunk.tileIndex());
Tile::Status tileStatus =
layer->tileProvider()->getTileStatus(chunk.tileIndex());
if (tileStatus == Tile::Status::OK) {
return UNKNOWN_DESIRED_LEVEL;
}
}
}
return currLevel - 1;;
return currLevel - 1;
}
} // namespace globebrowsing
} // namespace openspace
} // namespace openspace
@@ -34,25 +34,44 @@
namespace openspace {
namespace globebrowsing {
/**
* Abstract class defining an interface for accessing a desired level of a Chunk.
* The desired level can be used in the process of determining whether a Chunk should
* want to split, merge or do nothing.
*/
class ChunkLevelEvaluator {
public:
virtual int getDesiredLevel(const Chunk& chunk, const RenderData& data) const = 0;
static const int UNKNOWN_DESIRED_LEVEL = -1;
};
/**
* Evaluate the Chunk level depending on the distance from the Camera to the Chunk.
* This evaluation method aims to keep the screen size (horizontal length and not
* area) of all chunks constant.
*/
class EvaluateChunkLevelByDistance : public ChunkLevelEvaluator {
public:
virtual int getDesiredLevel(const Chunk& chunk, const RenderData& data) const;
};
/**
* Evaluate the chunk level using the area of the non-heightmapped Chunk projected
* on a sphere with the center in the position of the camera. A Chunk near the
* horizon will have a small projected area and hence a lower desired level. This
* evaluation is more forgiving than EvaluateChunkLevelByDistance, meaning it results
* in lower desired levels.
*/
class EvaluateChunkLevelByProjectedArea : public ChunkLevelEvaluator {
public:
virtual int getDesiredLevel(const Chunk& chunk, const RenderData& data) const;
};
/**
* If this chunk has available tile data for any LayerGroup on any of its active
* Layers it will return an UNKNOWN_DESIRED_LEVEL. If no data is available it will
* evaluate to a level that is <code>current level -1</code>.
*/
class EvaluateChunkLevelByAvailableTileData : public ChunkLevelEvaluator {
public:
virtual int getDesiredLevel(const Chunk& chunk, const RenderData& data) const;
@@ -60,6 +79,4 @@ namespace globebrowsing {
} // namespace globebrowsing
} // namespace openspace
#endif // __CHUNK_LEVEL_EVALUATOR_H__
+3 -19
View File
@@ -33,7 +33,6 @@
#include <modules/globebrowsing/globes/chunkedlodglobe.h>
#include <modules/globebrowsing/chunk/culling.h>
namespace {
const std::string _loggerCat = "ChunkNode";
}
@@ -66,12 +65,7 @@ bool ChunkNode::isLeaf() const {
return _children[0] == nullptr;
}
// Returns true or false wether this node can be merge or not
bool ChunkNode::updateChunkTree(const RenderData& data) {
//Geodetic2 center = _chunk.surfacePatch.center();
//LDEBUG("x: " << patch.x << " y: " << patch.y << " level: " << patch.level << " lat: " << center.lat << " lon: " << center.lon);
if (isLeaf()) {
Chunk::Status status = _chunk.update(data);
if (status == Chunk::Status::WANT_SPLIT) {
@@ -127,7 +121,8 @@ void ChunkNode::breadthFirst(const std::function<void(const ChunkNode&)>& f) con
}
}
void ChunkNode::reverseBreadthFirst(const std::function<void(const ChunkNode&)>& f) const {
void ChunkNode::reverseBreadthFirst(
const std::function<void(const ChunkNode&)>& f) const {
std::stack<const ChunkNode*> S;
std::queue<const ChunkNode*> Q;
@@ -175,25 +170,15 @@ const ChunkNode& ChunkNode::find(const Geodetic2& location) const {
return *node;
}
ChunkNode& ChunkNode::find(const Geodetic2& location) {
ChunkNode* node = this;
CHUNK_NODE_FIND(node, location);
return *node;
}
const ChunkNode& ChunkNode::getChild(Quad quad) const {
return *_children[quad];
}
ChunkNode& ChunkNode::getChild(Quad quad) {
return *_children[quad];
}
void ChunkNode::split(int depth) {
if (depth > 0 && isLeaf()) {
for (size_t i = 0; i < 4; i++) {
Chunk chunk(_chunk.owner(), _chunk.tileIndex().child((Quad)i));
_children[i] = std::unique_ptr<ChunkNode>(new ChunkNode(chunk, this));
_children[i] = std::make_unique<ChunkNode>(chunk, this);
}
}
@@ -215,7 +200,6 @@ void ChunkNode::merge() {
ghoul_assert(isLeaf(), "ChunkNode must be leaf after merge");
}
const Chunk& ChunkNode::getChunk() const {
return _chunk;
}
+22 -16
View File
@@ -37,8 +37,6 @@
#include <modules/globebrowsing/rendering/chunkrenderer.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
namespace openspace {
namespace globebrowsing {
@@ -49,32 +47,42 @@ public:
ChunkNode(const Chunk& chunk, ChunkNode* parent = nullptr);
~ChunkNode();
/**
* Recursively split the ChunkNode.
*
* \param depth defines how deep the recursion should go. If depth == 1 (default),
* the ChunkNode will only split once.
*/
void split(int depth = 1);
/**
* Deletes all children of the ChunkNode recursively.
*/
void merge();
bool isRoot() const;
bool isLeaf() const;
void depthFirst(const std::function<void(const ChunkNode&)>& f) const;
void breadthFirst(const std::function<void(const ChunkNode&)>& f) const;
void reverseBreadthFirst(const std::function<void(const ChunkNode&)>& f) const;
void depthFirst( const std::function<void(const ChunkNode&)>& f) const;
void breadthFirst( const std::function<void(const ChunkNode&)>& f) const;
void reverseBreadthFirst( const std::function<void(const ChunkNode&)>& f) const;
const ChunkNode& find(const Geodetic2& location) const;
ChunkNode& find(const Geodetic2& location);
const ChunkNode& getChild(Quad quad) const;
ChunkNode& getChild(Quad quad);
const Chunk& getChunk() const;
/**
* Updates all children recursively. If this ChunkNode wants to split it will,
* otherwise check if the children wants to merge. If all children wants to merge
* and the Status of this Chunk is not Status::WANT_SPLIT it will merge.
*
* \returns true if the ChunkNode can merge and false if it can not merge.
*/
bool updateChunkTree(const RenderData& data);
static int chunkNodeCount;
private:
private:
ChunkNode* _parent;
std::unique_ptr<ChunkNode> _children[4];
@@ -84,6 +92,4 @@ private:
} // namespace globebrowsing
} // namespace openspace
#endif // __QUADTREE_H__
#endif // __QUADTREE_H__
+6 -12
View File
@@ -22,10 +22,6 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/chunk/culling.h>
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/chunk/chunk.h>
@@ -45,7 +41,7 @@ namespace openspace {
namespace globebrowsing {
//////////////////////////////////////////////////////////////////////////////////////
// FRUSTUM CULLER //
// FRUSTUM CULLER //
//////////////////////////////////////////////////////////////////////////////////////
FrustumCuller::FrustumCuller(const AABB3 viewFrustum)
: _viewFrustum(viewFrustum){
@@ -65,7 +61,6 @@ namespace globebrowsing {
const std::vector<glm::dvec4>& corners = chunk.getBoundingPolyhedronCorners();
// Create a bounding box that fits the patch corners
AABB3 bounds; // in screen space
std::vector<vec4> clippingSpaceCorners(8);
@@ -73,16 +68,16 @@ namespace globebrowsing {
dvec4 cornerClippingSpace = modelViewProjectionTransform * corners[i];
clippingSpaceCorners[i] = cornerClippingSpace;
dvec3 cornerScreenSpace = (1.0f / glm::abs(cornerClippingSpace.w)) * cornerClippingSpace;
dvec3 cornerScreenSpace =
(1.0f / glm::abs(cornerClippingSpace.w)) * cornerClippingSpace;
bounds.expand(cornerScreenSpace);
}
return !_viewFrustum.intersects(bounds);
}
//////////////////////////////////////////////////////////////////////////////////////
// HORIZON CULLER //
// HORIZON CULLER //
//////////////////////////////////////////////////////////////////////////////////////
HorizonCuller::HorizonCuller() {
@@ -93,8 +88,6 @@ namespace globebrowsing {
}
bool HorizonCuller::isCullable(const Chunk& chunk, const RenderData& data) {
//return !isVisible(data, chunk.surfacePatch(), chunk.owner()->ellipsoid(), chunk.owner()->chunkHeight);
// Calculations are done in the reference frame of the globe. Hence, the camera
// position needs to be transformed with the inverse model matrix
glm::dmat4 inverseModelTransform = chunk.owner().inverseModelTransform();
@@ -147,7 +140,8 @@ namespace globebrowsing {
Scalar minimumGlobeRadius)
{
Scalar distanceToHorizon =
sqrt(pow(length(cameraPosition - globePosition), 2) - pow(minimumGlobeRadius, 2));
sqrt(pow(length(cameraPosition - globePosition), 2) -
pow(minimumGlobeRadius, 2));
Scalar minimumAllowedDistanceToObjectFromHorizon = sqrt(
pow(length(objectPosition - globePosition), 2) -
pow(minimumGlobeRadius - objectBoundingSphereRadius, 2));
+24 -8
View File
@@ -47,12 +47,29 @@ namespace globebrowsing {
class ChunkCuller {
public:
virtual void update() { }
/**
* Determine if the Chunk is cullable. That is return true if removing the
* Chunk 'culling it' will not have any result on the final rendering. Culling
* it will make the rendering faster.
*/
virtual bool isCullable(const Chunk& chunk, const RenderData& renderData) = 0;
};
/**
* Culls all chunks that are completely outside the view frustum.
*
* The frustum culling uses a 2D axis aligned bounding box for the Chunk in
* screen space. This is calculated from a bounding polyhedron bounding the
* Chunk. Hence the culling will not be 'perfect' but fast and good enough for
* culling chunks outside the frustum with some margin.
*/
class FrustumCuller : public ChunkCuller {
public:
/**
* \param viewFrustum is the view space in screen space. Hence it is an axis
* aligned bounding box and not a real frustum.
*/
FrustumCuller(const AABB3 viewFrustum);
~FrustumCuller();
@@ -60,15 +77,14 @@ namespace globebrowsing {
private:
const AABB3 _viewFrustum;
};
//In the current implementation of the horizon culling and the distance to the
//camera, the closer the ellipsoid is to a
//sphere, the better this will make the splitting. Using the minimum radius to
//be safe. This means that if the ellipsoid has high difference between radii,
//splitting might accur even though it is not needed.
/**
* In this implementation of the horizon culling, the closer the ellipsoid is to a
* sphere, the better this will make the culling. Using the minimum radius to
* be safe. This means that if the ellipsoid has high difference between radii,
* splitting might accur even though it may not be needed.
*/
class HorizonCuller : public ChunkCuller {
public:
HorizonCuller();