mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 08:04:01 +02:00
Added second cut at new bitstream writers
- added encodeTreeBitstream() which is new version of bitstream encoder that handles only encoding nodes that are in view. Also handles detecting packet overflow with new technique - added encodeTreeBitstreamRecursion() which is the private work horse function - moved temporary code to searchAndEncodeMultiTreeBitstream() which is still not ready for prime time
This commit is contained in:
parent
667fe11694
commit
3c7c85a98c
2 changed files with 327 additions and 12 deletions
|
@ -804,18 +804,17 @@ void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool
|
|||
this->reaverageVoxelColors(this->rootNode);
|
||||
}
|
||||
|
||||
|
||||
int VoxelTree::bhgLoadBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
// This will encode a larger tree into multiple subtree bitstreams. Given a node it will search for deeper subtrees that
|
||||
// have color. It will search for sub trees, and upon finding a subTree, it will call encodeTreeBitstream() to encode that
|
||||
// tree. Once the subtree is encoded, it will keep searching for additional subtrees.
|
||||
int VoxelTree::searchAndEncodeMultiTreeBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char*& lastOctalCode, bool& startedWriting,
|
||||
unsigned char*& outputBuffer, int availableBytes) const {
|
||||
|
||||
// Some debugging code... where are we in the tree..
|
||||
AABox box;
|
||||
node->getAABox(box);
|
||||
printf("bhgLoadBitstream() box.corner=(%f,%f,%f) size=%f node=",
|
||||
box.getCorner().x, box.getCorner().y, box.getCorner().z,box.getSize().x);
|
||||
printOctalCode(node->octalCode);
|
||||
|
||||
// Some debugging code... where are we in the tree..
|
||||
node->printDebugDetails("encodeTreeBitstream() node=");
|
||||
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesAtThisLevel = 0;
|
||||
|
||||
|
@ -1036,7 +1035,7 @@ int VoxelTree::bhgLoadBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
|||
|
||||
printf("about to dig deeper, priorWritingState=%s\n",(priorWritingState ? "yes" : "no"));
|
||||
|
||||
int childTreeBytesOut = bhgLoadBitstream(childNode,viewFrustum,lastOctalCode,startedWriting,
|
||||
int childTreeBytesOut = searchAndEncodeMultiTreeBitstream(childNode,viewFrustum,lastOctalCode,startedWriting,
|
||||
outputBuffer,availableBytes);
|
||||
|
||||
printf("back from dig deeper, priorWritingState=%s startedWriting=%s childTreeBytesOut=%d\n",
|
||||
|
@ -1060,6 +1059,15 @@ int VoxelTree::bhgLoadBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// THIS IS NOT WORKING!!!!
|
||||
//
|
||||
// Second trees are basically blowing up like crazy... not even sure what is going on...
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
printf("bhgLoadBitstream() at bottom, returning... bytesAtThisLevel=%d\n",bytesAtThisLevel );
|
||||
|
||||
// If this was the level that we wrote our octcode at.. AND our prior state was not writing, THEN THIS
|
||||
|
@ -1072,5 +1080,304 @@ int VoxelTree::bhgLoadBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
|||
startedWriting = false;
|
||||
}
|
||||
return bytesAtThisLevel;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// This will encode a tree bitstream, given a node it will encode the full tree from that point onward.
|
||||
// It will ignore any branches that are not in view. But other than that, it will not (can not) do any
|
||||
// prioritization of branches or deeper searching of the branches for optimization.
|
||||
//
|
||||
// NOTE: This STUB function DOES add the octcode to the buffer, then it calls the recursive helper to
|
||||
// actually encode the tree
|
||||
//
|
||||
// extraTrees is assumed to me an allocated array of VoxelNode*, if we're unable to fully encode the tree
|
||||
// because we run out of room on the outputBuffer, then we will add VoxelNode*'s of the trees that need
|
||||
// to be encoded to that array. If the array
|
||||
int VoxelTree::encodeTreeBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNode**& extraTrees, int& sizeExtraTrees, int& countExtraTrees) const {
|
||||
|
||||
// Some debugging code... where are we in the tree..
|
||||
node->printDebugDetails("encodeTreeBitstream() node=");
|
||||
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesWritten = 0;
|
||||
|
||||
// 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)) {
|
||||
printf("encodeTreeBitstream() node NOT IN VIEW\n");
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// write the octal code
|
||||
int codeLength = bytesRequiredForCodeLength(*node->octalCode);
|
||||
memcpy(outputBuffer,node->octalCode,codeLength);
|
||||
|
||||
outputBuffer += codeLength; // move the pointer
|
||||
bytesWritten += codeLength; // keep track of byte count
|
||||
availableBytes -= codeLength; // keep track or remaining space
|
||||
|
||||
int childBytesWritten = encodeTreeBitstreamRecursion(node, viewFrustum,
|
||||
outputBuffer, availableBytes, extraTrees, sizeExtraTrees, countExtraTrees);
|
||||
|
||||
// if childBytesWritten == 1 then something went wrong... that's not possible
|
||||
assert(childBytesWritten != 1);
|
||||
|
||||
// if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason
|
||||
// couldn't be written... so reset them here...
|
||||
if (childBytesWritten == 2) {
|
||||
childBytesWritten = 0;
|
||||
}
|
||||
|
||||
// if we wrote child bytes, then return our result of all bytes written
|
||||
if (childBytesWritten) {
|
||||
bytesWritten += childBytesWritten;
|
||||
} else {
|
||||
// otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code
|
||||
printf("encodeTreeBitstreamRecursion() no bytes below...\n");
|
||||
bytesWritten = 0;
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// This will encode a tree bitstream, given a node it will encode the full tree from that point onward.
|
||||
// It will ignore any branches that are not in view. But other than that, it will not (can not) do any
|
||||
// prioritization of branches or deeper searching of the branches for optimization.
|
||||
//
|
||||
// NOTE: This recursive function DOES NOT add the octcode to the buffer. It's assumed that the caller has
|
||||
// already done that.
|
||||
int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNode**& extraTrees, int& sizeExtraTrees, int& countExtraTrees) const {
|
||||
|
||||
// Some debugging code... where are we in the tree..
|
||||
node->printDebugDetails("encodeTreeBitstreamRecursion() node=");
|
||||
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesAtThisLevel = 0;
|
||||
|
||||
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
||||
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
|
||||
// we're out of view
|
||||
if (!node->isInView(viewFrustum)) {
|
||||
printf("encodeTreeBitstreamRecursion() node NOT IN VIEW\n");
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
|
||||
|
||||
// At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level
|
||||
// is 1 byte for child colors + 3*8 bytes for the actual colors + 1 byte for child trees. There could be sub trees
|
||||
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
|
||||
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
|
||||
const int CHILD_COLOR_MASK_BYTES = 1;
|
||||
const int MAX_CHILDREN = 8;
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
const int CHILD_TREE_EXISTS_BYTES = 1;
|
||||
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + MAX_CHILDREN * BYTES_PER_COLOR + CHILD_TREE_EXISTS_BYTES;
|
||||
|
||||
// Make our local buffer large enough to handle writing at this level in case we need to.
|
||||
unsigned char thisLevelBuffer[MAX_LEVEL_BYTES];
|
||||
unsigned char* writeToThisLevelBuffer = &thisLevelBuffer[0];
|
||||
|
||||
unsigned char childrenExistBits = 0;
|
||||
unsigned char childrenColoredBits = 0;
|
||||
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 < MAX_CHILDREN; i++) {
|
||||
VoxelNode* childNode = node->children[i];
|
||||
bool childExists = (childNode != NULL);
|
||||
|
||||
if (childExists) {
|
||||
childNode->printDebugDetails("encodeTreeBitstream() looping children");
|
||||
}
|
||||
|
||||
bool childIsColored = (childExists && childNode->isColored());
|
||||
bool childIsInView = (childExists && childNode->isInView(viewFrustum));
|
||||
bool childIsLeaf = (childExists && childNode->isLeaf());
|
||||
|
||||
printf("childExists=%s childIsColored=%s childIsInView=%s childIsLeaf=%s \n",
|
||||
(childExists ? "yes" : "no"), (childIsColored ? "yes" : "no"),
|
||||
(childIsInView ? "yes" : "no"), (childIsLeaf ? "yes" : "no") );
|
||||
|
||||
if (childIsInView) {
|
||||
inViewCount++;
|
||||
|
||||
// track children in view as existing and not a leaf, if they're a leaf,
|
||||
// we don't care about recursing deeper on them, and we don't consider their
|
||||
// subtree to exist
|
||||
if (!childIsLeaf) {
|
||||
childrenExistBits += (1 << (7 - i));
|
||||
inViewNotLeafCount++;
|
||||
}
|
||||
|
||||
// track children with actual color
|
||||
if (childIsColored) {
|
||||
childrenColoredBits += (1 << (7 - i));
|
||||
inViewWithColorCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("bhgLoadBitstream() child nodes in view... inViewCount=%d\n",inViewCount);
|
||||
printf("bhgLoadBitstream() child nodes in view with color... inViewWithColorCount=%d\n",inViewWithColorCount);
|
||||
printf("bhgLoadBitstream() child nodes in view NOT LEAF... inViewNotLeafCount=%d\n",inViewNotLeafCount);
|
||||
|
||||
// write the child color bits
|
||||
printf("bhgLoadBitstream() writing child color bits=");
|
||||
outputBits(childrenColoredBits);
|
||||
|
||||
*writeToThisLevelBuffer = childrenColoredBits;
|
||||
|
||||
writeToThisLevelBuffer += sizeof(childrenColoredBits); // move the pointer
|
||||
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
|
||||
|
||||
// write the color data...
|
||||
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||
if (oneAtBit(childrenColoredBits, i)) {
|
||||
printf("bhgLoadBitstream() writing color for child %d\n",i);
|
||||
memcpy(writeToThisLevelBuffer,&node->children[i]->getColor(),BYTES_PER_COLOR);
|
||||
writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color
|
||||
bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color
|
||||
}
|
||||
}
|
||||
|
||||
printf("inViewNotLeafCount=%d \n", inViewNotLeafCount );
|
||||
|
||||
printf("bhgLoadBitstream() writing child trees exist bits=");
|
||||
outputBits(childrenExistBits);
|
||||
|
||||
// write the child exist bits
|
||||
*writeToThisLevelBuffer = childrenExistBits;
|
||||
|
||||
writeToThisLevelBuffer += sizeof(childrenExistBits); // move the pointer
|
||||
bytesAtThisLevel += sizeof(childrenExistBits); // keep track of byte count
|
||||
|
||||
// We only need to keep digging, if there is at least one child that is inView, and not a leaf.
|
||||
keepDiggingDeeper = (inViewNotLeafCount > 0);
|
||||
|
||||
// at this point we need to do a gut check. If we're writing, then we need to check how many bytes we've
|
||||
// written at this level, and how many bytes we have available in the outputBuffer. If we can fit what we've got so far
|
||||
// and room for the no more children terminator, then let's write what we got so far.
|
||||
|
||||
printf("availableBytes=%d bytesAtThisLevel=%d keepDiggingDeeper=%s \n",
|
||||
availableBytes, bytesAtThisLevel, (keepDiggingDeeper ? "yes" : "no") );
|
||||
|
||||
// If we have enough room to copy our local results into the buffer, then do so...
|
||||
if (availableBytes >= bytesAtThisLevel) {
|
||||
memcpy(outputBuffer,&thisLevelBuffer[0],bytesAtThisLevel);
|
||||
|
||||
outputBuffer += bytesAtThisLevel;
|
||||
availableBytes -= bytesAtThisLevel;
|
||||
|
||||
printf("actually write our local bytes to the outputBuffer bytesAtThisLevel=%d availableBytes=%d\n",
|
||||
bytesAtThisLevel, availableBytes);
|
||||
} else {
|
||||
// we've run out of room!!! What do we do!!!
|
||||
printf("we don't have room to write bytes bytesAtThisLevel=%d availableBytes=%d\n",bytesAtThisLevel, availableBytes);
|
||||
|
||||
// 1) return 0 for bytes written so upper levels do the right thing, namely prune us from their trees
|
||||
// 2) add our node to the list of extra nodes for later output...
|
||||
// Note: we don't do any termination for this level, we just return 0, then the upper level is in charge
|
||||
// of handling things. For example, in case of child iteration, it needs to unset the child exist bit for
|
||||
// this child.
|
||||
|
||||
// add our node the the list of extra nodes to output later...
|
||||
|
||||
// If we have room on our extra tree list, then add ourselves to the end.
|
||||
if (countExtraTrees < sizeExtraTrees) {
|
||||
extraTrees[countExtraTrees] = node;
|
||||
countExtraTrees++;
|
||||
} else {
|
||||
// do we want to expand the extraTrees array here, or do we bail???
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (keepDiggingDeeper) {
|
||||
|
||||
printf("keepDiggingDeeper == TRUE... dig deeper!\n");
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Since this recursive function assumes we're already writing, we know we've already written our
|
||||
// childrenExistBits. But... we don't really know how big the child tree will be. And we don't know if
|
||||
// we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is
|
||||
// write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they
|
||||
// write something, we keep them in the bits, if they don't, we take them out.
|
||||
//
|
||||
// we know the last thing we wrote to the outputBuffer was our childrenExistBits. Let's remember where that was!
|
||||
unsigned char* childExistsPlaceHolder = outputBuffer-sizeof(childrenExistBits);
|
||||
|
||||
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||
|
||||
if (oneAtBit(childrenExistBits, i)) {
|
||||
VoxelNode* childNode = node->children[i];
|
||||
|
||||
printf("about to dig deeper child[%d] outputBuffer=%p, availableBytes=%d\n",i,outputBuffer,availableBytes);
|
||||
|
||||
int childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, viewFrustum,
|
||||
outputBuffer, availableBytes, extraTrees, sizeExtraTrees, countExtraTrees);
|
||||
|
||||
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
|
||||
// basically, the children below don't contain any info.
|
||||
|
||||
// if the child tree wrote 1 byte??? something must have gone wrong... because it must have at least the color
|
||||
// byte and the child exist byte.
|
||||
//
|
||||
assert(childTreeBytesOut != 1);
|
||||
|
||||
// if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because...
|
||||
// if it had colors it would write 1 byte for the color mask,
|
||||
// and at least a color's worth of bytes for the node of colors.
|
||||
// if it had child trees (with something in them) then it would have the 1 byte for child mask
|
||||
// and some number of bytes of lower children...
|
||||
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
|
||||
//
|
||||
// we can make this act like no bytes out, by just resetting the bytes out in this case
|
||||
if (2 == childTreeBytesOut) {
|
||||
printf("after to dig deeper child[%d] childTreeBytesOut was 2, we're resetting to 0\n",i);
|
||||
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
||||
}
|
||||
|
||||
bytesAtThisLevel += childTreeBytesOut;
|
||||
availableBytes -= childTreeBytesOut;
|
||||
outputBuffer += childTreeBytesOut;
|
||||
|
||||
printf("after dig deeper child[%d] childTreeBytesOut=%d outputBuffer=%p, availableBytes=%d\n",
|
||||
i,childTreeBytesOut, outputBuffer, availableBytes);
|
||||
|
||||
// If we had previously started writing, and if the child DIDN'T write any bytes,
|
||||
// then we want to remove their bit from the childExistsPlaceHolder bitmask
|
||||
if (0 == childTreeBytesOut) {
|
||||
|
||||
printf("bhgLoadBitstream() child %d didn't write bytes, removing from bitmask\n",i );
|
||||
|
||||
// remove this child's bit...
|
||||
childrenExistBits -= (1 << (7 - i));
|
||||
|
||||
// repair the child exists mask
|
||||
*childExistsPlaceHolder = childrenExistBits;
|
||||
|
||||
// Note: no need to move the pointer, cause we already stored this
|
||||
|
||||
} // end if (0 == childTreeBytesOut)
|
||||
|
||||
} // end if (oneAtBit(childrenExistBits, i))
|
||||
} // end for
|
||||
|
||||
} // end keepDiggingDeeper
|
||||
|
||||
|
||||
printf("bhgLoadBitstream() at bottom, returning... bytesAtThisLevel=%d\n",bytesAtThisLevel );
|
||||
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,19 @@ public:
|
|||
|
||||
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
|
||||
|
||||
int bhgLoadBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char*& lastOctalCode, bool& startedWriting,
|
||||
unsigned char*& outputBuffer, int availableBytes) const;
|
||||
int encodeTreeBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNode**& extraTrees, int& sizeExtraTrees, int& countExtraTrees) const;
|
||||
|
||||
int searchAndEncodeMultiTreeBitstream(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char*& lastOctalCode, bool& startedWriting,
|
||||
unsigned char*& outputBuffer, int availableBytes) const;
|
||||
|
||||
private:
|
||||
int encodeTreeBitstreamRecursion(VoxelNode* node, const ViewFrustum& viewFrustum,
|
||||
unsigned char* outputBuffer, int availableBytes,
|
||||
VoxelNode**& extraTrees, int& sizeExtraTrees, int& countExtraTrees) const;
|
||||
|
||||
void recurseNodeWithOperation(VoxelNode* node, RecurseVoxelTreeOperation operation, void* extraData);
|
||||
VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode);
|
||||
VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate);
|
||||
|
|
Loading…
Reference in a new issue