Fixed a bug in LOD boundaries

- changed the way we calculate whether or not to render a parent "average" voxel so
  that if any of it's most distant child would not be visible, then it's used instead
  of it's children
- added precalculated value for topFarLeft corner of AABox (optimization)
- changed VoxelSystem::newTreeToArrays() and VoxelTree::encodeTreeBitstreamRecursion()
  to use the same help function for determining this LOD boundary behavior
- deleted old dead code in voxel-server and VoxelTree for picking which node to start
  sending with, since it wasn't being used
- added VoxelNode::furthestDistanceToCamera() which tells you not the distance to the
  center of the voxel, but the distance from the camera to the furthest corner relative
  to the camera.
- added ViewFrustum::getFurthestPointFromCamera() which given an axis-aligned box will
  tell you which vertex of the box is furthest from the camera
This commit is contained in:
ZappoMan 2013-07-15 01:32:14 -07:00
parent 861ee9b389
commit f415f4081a
11 changed files with 98 additions and 169 deletions

View file

@ -318,8 +318,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) :
_lastAcceleration(0),
_totalPacketsReceived(0),
_firstPacketReceivedTime(),
_echoSamplesLeft(NULL),
_packetsReceivedThisPlayback(0),
_echoSamplesLeft(NULL),
_isSendingEchoPing(false),
_pingAnalysisPending(false),
_pingFramesToRecord(0),

View file

@ -319,14 +319,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
int voxelsUpdated = 0;
bool shouldRender = false; // assume we don't need to render it
// if it's colored, we might need to render it!
if (node->isColored()) {
float distanceToNode = node->distanceToCamera(*Application::getInstance()->getViewFrustum());
float boundary = boundaryDistanceForRenderLevel(node->getLevel());
float childBoundary = boundaryDistanceForRenderLevel(node->getLevel() + 1);
bool inBoundary = (distanceToNode <= boundary);
bool inChildBoundary = (distanceToNode <= childBoundary);
shouldRender = (node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary);
}
shouldRender = node->calculateShouldRender(Application::getInstance()->getViewFrustum());
node->setShouldRender(shouldRender && !node->isStagedForDeletion());
// let children figure out their renderness
if (!node->isLeaf()) {

View file

@ -18,6 +18,7 @@ void AABox::scale(float scale) {
_corner = _corner * scale;
_size = _size * scale;
_center = _center * scale;
_topFarLeft = _topFarLeft * scale;
}
@ -60,6 +61,7 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
_corner.z -= _size.z;
}
_center = _corner + (_size * 0.5f);
_topFarLeft = _corner + _size;
}
glm::vec3 AABox::getVertexP(const glm::vec3& normal) const {

View file

@ -41,10 +41,13 @@ class AABox
public:
AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size) { };
AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z) { };
AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size) { };
AABox() : _corner(0,0,0), _size(0,0,0) { }
AABox(const glm::vec3& corner, float size) :
_corner(corner), _size(size, size, size) { _topFarLeft = _corner + _size; };
AABox(const glm::vec3& corner, float x, float y, float z) :
_corner(corner), _size(x, y, z) { _topFarLeft = _corner + _size; };
AABox(const glm::vec3& corner, const glm::vec3& size) :
_corner(corner), _size(size) { _topFarLeft = _corner + _size; };
AABox() : _corner(0,0,0), _size(0,0,0), _topFarLeft(0,0,0) { }
~AABox() { }
void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); };
@ -56,9 +59,10 @@ public:
void scale(float scale);
const glm::vec3& getCorner() const { return _corner; };
const glm::vec3& getSize() const { return _size; };
const glm::vec3& getCenter() const { return _center; };
const glm::vec3& getCorner() const { return _corner; };
const glm::vec3& getSize() const { return _size; };
const glm::vec3& getCenter() const { return _center; };
const glm::vec3& getTopFarLeft() const { return _topFarLeft; };
glm::vec3 getVertex(BoxVertex vertex) const;
@ -81,6 +85,7 @@ private:
glm::vec3 _corner;
glm::vec3 _center;
glm::vec3 _size;
glm::vec3 _topFarLeft;
};

View file

@ -530,8 +530,10 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT
};
VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
glm::vec3 bottomNearRight = box.getCorner();
glm::vec3 topFarLeft = box.getCorner() + box.getSize();
const glm::vec3& bottomNearRight = box.getCorner();
const glm::vec3& topFarLeft = box.getTopFarLeft();
int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit
+ ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to
+ ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera
@ -593,3 +595,40 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const {
projectedPolygon.setProjectionType(lookUp); // remember the projection type
return projectedPolygon;
}
// Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the
// axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for
// squares and square-roots. Just compares.
glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const {
const glm::vec3& center = box.getCenter();
const glm::vec3& bottomNearRight = box.getCorner();
const glm::vec3& topFarLeft = box.getTopFarLeft();
glm::vec3 furthestPoint;
if (_position.x < center.x) {
// we are to the right of the center, so the left edge is furthest
furthestPoint.x = topFarLeft.x;
} else {
// we are to the left of the center, so the right edge is furthest (at center ok too)
furthestPoint.x = bottomNearRight.x;
}
if (_position.y < center.y) {
// we are below of the center, so the top edge is furthest
furthestPoint.y = topFarLeft.y;
} else {
// we are above the center, so the lower edge is furthest (at center ok too)
furthestPoint.y = bottomNearRight.y;
}
if (_position.z < center.z) {
// we are to the near side of the center, so the far side edge is furthest
furthestPoint.z = topFarLeft.z;
} else {
// we are to the far side of the center, so the near side edge is furthest (at center ok too)
furthestPoint.z = bottomNearRight.z;
}
return furthestPoint;
}

View file

@ -90,6 +90,7 @@ public:
glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const;
VoxelProjectedPolygon getProjectedPolygon(const AABox& box) const;
glm::vec3 getFurthestPointFromCamera(const AABox& box) const;
private:

View file

@ -335,6 +335,39 @@ ViewFrustum::location VoxelNode::inFrustum(const ViewFrustum& viewFrustum) const
return viewFrustum.boxInFrustum(box);
}
// There are two types of nodes for which we want to "render"
// 1) Leaves that are in the LOD
// 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children
// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE
// in the LOD, and others that are not. In this case we want to render the parent, and none of the children.
//
// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest"
// corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of.
// By doing this, we don't need to test each child voxel's position vs the LOD boundary
bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust) const {
bool shouldRender = false;
if (isColored()) {
float furthestDistance = furthestDistanceToCamera(*viewFrustum);
float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust);
float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust);
bool inBoundary = (furthestDistance <= boundary);
bool inChildBoundary = (furthestDistance <= childBoundary);
shouldRender = (isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary);
}
return shouldRender;
}
// Calculates the distance to the furthest point of the voxel to the camera
float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box = getAABox();
box.scale(TREE_SCALE);
glm::vec3 furthestPoint = viewFrustum.getFurthestPointFromCamera(box);
glm::vec3 temp = viewFrustum.getPosition() - furthestPoint;
float distanceSquared = glm::dot(temp, temp);
float distanceToVoxelCenter = sqrtf(distanceSquared);
return distanceToVoxelCenter;
}
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
glm::vec3 center = _box.getCenter() * (float)TREE_SCALE;
glm::vec3 temp = viewFrustum.getPosition() - center;

View file

@ -70,6 +70,9 @@ public:
bool isInView(const ViewFrustum& viewFrustum) const;
ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const;
float distanceToCamera(const ViewFrustum& viewFrustum) const;
float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const;
bool calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust = 0) const;
// points are assumed to be in Voxel Coordinates (not TREE_SCALE'd)
float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this.

View file

@ -891,17 +891,6 @@ void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float v
}
}
int VoxelTree::searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) {
// call the recursive version, this will add all found colored node roots to the bag
int currentSearchLevel = 0;
int levelReached = searchForColoredNodesRecursion(maxSearchLevel, currentSearchLevel, rootNode,
viewFrustum, bag, deltaViewFrustum, lastViewFrustum);
return levelReached;
}
// combines the ray cast arguments into a single object
class RayArgs {
public:
@ -1014,88 +1003,6 @@ bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3&
return args.found;
}
int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum) {
// Keep track of how deep we've searched.
currentSearchLevel++;
// If we've passed our max Search Level, then stop searching. return last level searched
if (currentSearchLevel > maxSearchLevel) {
return currentSearchLevel-1;
}
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
if (!node->isInView(viewFrustum)) {
return currentSearchLevel;
}
// Ok, this is a little tricky, each child may have been deeper than the others, so we need to track
// how deep each child went. And we actually return the maximum of each child. We use these variables below
// when we recurse the children.
int thisLevel = currentSearchLevel;
int maxChildLevel = thisLevel;
VoxelNode* inViewChildren[NUMBER_OF_CHILDREN];
float distancesToChildren[NUMBER_OF_CHILDREN];
int positionOfChildren[NUMBER_OF_CHILDREN];
int inViewCount = 0;
int inViewNotLeafCount = 0;
int inViewWithColorCount = 0;
// for each child node, check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
bool childIsColored = (childNode && childNode->isColored());
bool childIsInView = (childNode && childNode->isInView(viewFrustum));
bool childIsLeaf = (childNode && childNode->isLeaf());
if (childIsInView) {
// track children in view as existing and not a leaf
if (!childIsLeaf) {
inViewNotLeafCount++;
}
// track children with actual color
if (childIsColored) {
inViewWithColorCount++;
}
float distance = childNode->distanceToCamera(viewFrustum);
if (distance < boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1)) {
inViewCount = insertIntoSortedArrays((void*)childNode, distance, i,
(void**)&inViewChildren, (float*)&distancesToChildren,
(int*)&positionOfChildren, inViewCount, NUMBER_OF_CHILDREN);
}
}
}
// If we have children with color, then we do want to add this node (and it's descendants) to the bag to be written
// we don't need to dig deeper.
//
// XXXBHG - this might be a good time to look at colors and add them to a dictionary? But we're not planning
// on scanning the whole tree, so we won't actually see all the colors, so maybe no point in that.
if (inViewWithColorCount) {
bag.insert(node);
} else {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about. We will iterate
// these based on which tree is closer.
for (int i = 0; i < inViewCount; i++) {
VoxelNode* childNode = inViewChildren[i];
thisLevel = currentSearchLevel; // reset this, since the children will munge it up
int childLevelReached = searchForColoredNodesRecursion(maxSearchLevel, thisLevel, childNode, viewFrustum, bag,
deltaViewFrustum, lastViewFrustum);
maxChildLevel = std::max(maxChildLevel, childLevelReached);
}
}
return maxChildLevel;
}
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params) const {
@ -1344,19 +1251,10 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
} // wants occlusion culling & isLeaf()
// There are two types of nodes for which we want to send colors:
// 1) Leaves - obviously
// 2) Non-leaves who's children would be visible but are beyond our LOD.
bool isLeafOrLOD = childNode->isLeaf();
if (params.viewFrustum && childNode->isColored() && !childNode->isLeaf()) {
int childLevel = childNode->getLevel();
float childBoundary = boundaryDistanceForRenderLevel(childLevel + params.boundaryLevelAdjust);
float grandChildBoundary = boundaryDistanceForRenderLevel(childLevel + 1 + params.boundaryLevelAdjust);
isLeafOrLOD = ((distance <= childBoundary) && !(distance <= grandChildBoundary));
}
bool shouldRender = childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust);
// track children with actual color, only if the child wasn't previously in view!
if (childNode && isLeafOrLOD && childNode->isColored() && !childIsOccluded) {
if (shouldRender && !childIsOccluded) {
bool childWasInView = false;
if (childNode && params.deltaViewFrustum && params.lastViewFrustum) {
@ -1368,7 +1266,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
} else {
childWasInView = location == ViewFrustum::INSIDE;
}
}
}
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items
if (!childWasInView) {

View file

@ -123,9 +123,6 @@ public:
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params) const;
int searchForColoredNodes(int maxSearchLevel, VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
bool deltaViewFrustum = false, const ViewFrustum* lastViewFrustum = NULL);
bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; };
void setDirtyBit() { _isDirty = true; };
@ -171,10 +168,6 @@ private:
int encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel) const;
int searchForColoredNodesRecursion(int maxSearchLevel, int& currentSearchLevel,
VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag,
bool deltaViewFrustum, const ViewFrustum* lastViewFrustum);
static bool countVoxelsOperation(VoxelNode* node, void* extraData);
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const;

View file

@ -60,7 +60,6 @@ bool wantLocalDomain = false;
bool wantColorRandomizer = false;
bool debugVoxelSending = false;
bool shouldShowAnimationDebug = false;
bool wantSearchForColoredNodes = false;
EnvironmentData environmentData[3];
@ -121,8 +120,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
pthread_mutex_lock(&::treeLock);
int maxLevelReached = 0;
uint64_t start = usecTimestampNow();
int truePacketsSent = 0;
int trueBytesSent = 0;
@ -200,38 +197,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
nodeData->map.erase();
}
// For now, we're going to disable the "search for colored nodes" because that strategy doesn't work when we support
// deletion of nodes. Instead if we just start at the root we get the correct behavior we want. We are keeping this
// code for now because we want to be able to go back to it and find a solution to support both. The search method
// helps improve overall bitrate performance.
if (::wantSearchForColoredNodes) {
// If the bag was empty, then send everything in view, not just the delta
maxLevelReached = serverTree.searchForColoredNodes(INT_MAX, serverTree.rootNode, nodeData->getCurrentViewFrustum(),
nodeData->nodeBag, wantDelta, lastViewFrustum);
// if nothing was found in view, send the root node.
if (nodeData->nodeBag.isEmpty()){
nodeData->nodeBag.insert(serverTree.rootNode);
}
nodeData->setViewSent(false);
} else {
nodeData->nodeBag.insert(serverTree.rootNode);
}
}
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;
printf("WARNING! searchForColoredNodes() took %d seconds to identify %d nodes at level %d\n",
elapsedsec, nodeData->nodeBag.count(), maxLevelReached);
} else {
printf("WARNING! searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n",
elapsedmsec, nodeData->nodeBag.count(), maxLevelReached);
}
} else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %d milliseconds to identify %d nodes at level %d\n",
elapsedmsec, nodeData->nodeBag.count(), maxLevelReached);
nodeData->nodeBag.insert(serverTree.rootNode);
}
// If we have something in our nodeBag, then turn them into packets and send them out...
@ -438,10 +404,6 @@ int main(int argc, const char * argv[]) {
::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER);
printf("wantColorRandomizer=%s\n", debug::valueOf(::wantColorRandomizer));
const char* WANT_SEARCH_FOR_NODES = "--wantSearchForColoredNodes";
::wantSearchForColoredNodes = cmdOptionExists(argc, argv, WANT_SEARCH_FOR_NODES);
printf("wantSearchForColoredNodes=%s\n", debug::valueOf(::wantSearchForColoredNodes));
// By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) {