overte-JulianGro/libraries/voxels/src/VoxelTree.cpp

1172 lines
52 KiB
C++

//
// VoxelTree.cpp
// hifi
//
// Created by Stephen Birarda on 3/13/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
//
// Oct-Tree in bits, and child ordinals
//
// Location Decimal Bit Child in Array
// ------------------- ------- --- ---------------
// Bottom Right Near 128 8th 0
// Bottom Right Far 64 7th 1
// Top Right Near 32 6th 2
// Top Right Far 16 5th 3
// Bottom Left Near 8 4th 4
// Bottom Left Far 4 3rd 5
// Top Left Near 2 2nd 6
// Top Left Far 1 1st 7
//
// ^
// +-------|------+ + Y
// / / | /|
// / 1 / | 16 / |
// /------/-------+ |
// / / | /|16|
// / 2 / 32 | / | |
// / / |/ | /| / +Z
// +--------------+ 32|/ | /
// | | | / | /
// | | | /| | /
// | 2 | 32 | / |64| /
// 4----| | |/ | |/
// +------|------ + | /
// | | | | /
// | | |128|/
// | 8 | 128 | /
// | | | /
// | | | /
// < - - - +------+-------+/ - - - - - - >
// +X (0,0,0) -X
// /|
// / |
// / |
// -Z / V -Y
//
//
#ifdef _WIN32
#define _USE_MATH_DEFINES
#endif
#include <cstring>
#include <cstdio>
#include <cmath>
#include "SharedUtil.h"
#include "voxels_Log.h"
#include "PacketHeaders.h"
#include "OctalCode.h"
#include "VoxelTree.h"
#include "VoxelNodeBag.h"
#include "ViewFrustum.h"
#include <fstream> // to load voxels from file
using voxels_lib::printLog;
int boundaryDistanceForRenderLevel(unsigned int renderLevel) {
switch (renderLevel) {
case 1:
case 2:
case 3:
return 100;
case 4:
return 75;
break;
case 5:
return 50;
break;
case 6:
return 25;
break;
case 7:
return 12;
break;
default:
return 6;
break;
}
}
VoxelTree::VoxelTree() :
voxelsCreated(0),
voxelsColored(0),
voxelsBytesRead(0),
voxelsCreatedStats(100),
voxelsColoredStats(100),
voxelsBytesReadStats(100) {
rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1];
*rootNode->octalCode = 0;
}
VoxelTree::~VoxelTree() {
// delete the children of the root node
// this recursively deletes the tree
for (int i = 0; i < 8; i++) {
delete rootNode->children[i];
}
}
// Recurses voxel tree calling the RecurseVoxelTreeOperation function for each node.
// stops recursion if operation function returns false.
void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData) {
recurseNodeWithOperation(rootNode, operation,extraData);
}
// Recurses voxel node with an operation function
void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData) {
// call the operation function going "down" first, stop deeper recursion if function returns false
if (operation(node,true,extraData)) {
for (int i = 0; i < sizeof(node->children)/sizeof(node->children[0]); i++) {
VoxelNode* child = node->children[i];
if (child) {
recurseNodeWithOperation(child,operation,extraData);
}
}
// call operation on way back up
operation(node,false,extraData);
}
}
VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode, VoxelNode** parentOfFoundNode) {
// find the appropriate branch index based on this ancestorNode
if (*needleCode > 0) {
int branchForNeedle = branchIndexWithDescendant(ancestorNode->octalCode, needleCode);
VoxelNode *childNode = ancestorNode->children[branchForNeedle];
if (childNode != NULL) {
if (*childNode->octalCode == *needleCode) {
// If the caller asked for the parent, then give them that too...
if (parentOfFoundNode) {
*parentOfFoundNode=ancestorNode;
}
// the fact that the number of sections is equivalent does not always guarantee
// that this is the same node, however due to the recursive traversal
// we know that this is our node
return childNode;
} else {
// we need to go deeper
return nodeForOctalCode(childNode, needleCode,parentOfFoundNode);
}
}
}
// we've been given a code we don't have a node for
// return this node as the last created parent
return ancestorNode;
}
// returns the node created!
VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char* codeToReach) {
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->octalCode, codeToReach);
// we could be coming down a branch that was already created, so don't stomp on it.
if (lastParentNode->children[indexOfNewChild] == NULL) {
lastParentNode->addChildAtIndex(indexOfNewChild);
}
// This works because we know we traversed down the same tree so if the length is the same, then the whole code is the same
if (*lastParentNode->children[indexOfNewChild]->octalCode == *codeToReach) {
return lastParentNode->children[indexOfNewChild];
} else {
return createMissingNode(lastParentNode->children[indexOfNewChild], codeToReach);
}
}
// BHG Notes: We appear to call this function for every Voxel Node getting created.
// This is recursive in nature. So, for example, if we are given an octal code for
// a 1/256th size voxel, we appear to call this function 8 times. Maybe??
int VoxelTree::readNodeData(VoxelNode* destinationNode,
unsigned char* nodeData,
int bytesLeftToRead) {
// instantiate variable for bytes already read
int bytesRead = 1;
for (int i = 0; i < 8; i++) {
// check the colors mask to see if we have a child to color in
if (oneAtBit(*nodeData, i)) {
// create the child if it doesn't exist
if (destinationNode->children[i] == NULL) {
destinationNode->addChildAtIndex(i);
this->voxelsCreated++;
this->voxelsCreatedStats.updateAverage(1);
}
// pull the color for this child
nodeColor newColor;
memcpy(newColor, nodeData + bytesRead, 3);
newColor[3] = 1;
destinationNode->children[i]->setColor(newColor);
this->voxelsColored++;
this->voxelsColoredStats.updateAverage(1);
bytesRead += 3;
}
}
// average node's color based on color of children
destinationNode->setColorFromAverageOfChildren();
// give this destination node the child mask from the packet
unsigned char childMask = *(nodeData + bytesRead);
int childIndex = 0;
bytesRead++;
while (bytesLeftToRead - bytesRead > 0 && childIndex < 8) {
// check the exists mask to see if we have a child to traverse into
if (oneAtBit(childMask, childIndex)) {
if (destinationNode->children[childIndex] == NULL) {
// add a child at that index, if it doesn't exist
destinationNode->addChildAtIndex(childIndex);
this->voxelsCreated++;
this->voxelsCreatedStats.updateAverage(this->voxelsCreated);
}
// tell the child to read the subsequent data
bytesRead += readNodeData(destinationNode->children[childIndex],
nodeData + bytesRead,
bytesLeftToRead - bytesRead);
}
childIndex++;
}
return bytesRead;
}
void VoxelTree::readBitstreamToTree(unsigned char * bitstream, int bufferSizeBytes) {
int bytesRead = 0;
unsigned char* bitstreamAt = bitstream;
// Keep looping through the buffer calling readNodeData() this allows us to pack multiple root-relative Octal codes
// into a single network packet. readNodeData() basically goes down a tree from the root, and fills things in from there
// if there are more bytes after that, it's assumed to be another root relative tree
while (bitstreamAt < bitstream+bufferSizeBytes) {
VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL);
if (*bitstreamAt != *bitstreamRootNode->octalCode) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
// Note: we need to create this node relative to root, because we're assuming that the bitstream for the initial
// octal code is always relative to root!
bitstreamRootNode = createMissingNode(rootNode, (unsigned char *)bitstreamAt);
}
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
int theseBytesRead = 0;
theseBytesRead += octalCodeBytes;
theseBytesRead += readNodeData(bitstreamRootNode, bitstreamAt + octalCodeBytes, bufferSizeBytes - (bytesRead+octalCodeBytes));
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
bytesRead += theseBytesRead;
}
this->voxelsBytesRead += bufferSizeBytes;
this->voxelsBytesReadStats.updateAverage(bufferSizeBytes);
}
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the node from the tree.
void VoxelTree::deleteVoxelCodeFromTree(unsigned char *codeBuffer) {
VoxelNode* parentNode = NULL;
VoxelNode* nodeToDelete = nodeForOctalCode(rootNode, codeBuffer, &parentNode);
// If the node exists...
int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color!
if (0 == memcmp(nodeToDelete->octalCode,codeBuffer,lengthInBytes)) {
float* vertices = firstVertexForCode(nodeToDelete->octalCode);
delete []vertices;
if (parentNode) {
float* vertices = firstVertexForCode(parentNode->octalCode);
delete []vertices;
int childNDX = branchIndexWithDescendant(parentNode->octalCode, codeBuffer);
delete parentNode->children[childNDX]; // delete the child nodes
parentNode->children[childNDX]=NULL; // set it to NULL
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
}
}
}
void VoxelTree::eraseAllVoxels() {
// XXXBHG Hack attack - is there a better way to erase the voxel tree?
delete rootNode; // this will recurse and delete all children
rootNode = new VoxelNode();
rootNode->octalCode = new unsigned char[1];
*rootNode->octalCode = 0;
}
void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) {
VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL);
// create the node if it does not exist
if (*lastCreatedNode->octalCode != *codeColorBuffer) {
lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer);
}
// give this node its color
int octalCodeBytes = bytesRequiredForCodeLength(*codeColorBuffer);
nodeColor newColor;
memcpy(newColor, codeColorBuffer + octalCodeBytes, 3);
newColor[3] = 1;
lastCreatedNode->setColor(newColor);
}
unsigned char * VoxelTree::loadBitstreamBuffer(unsigned char *& bitstreamBuffer,
VoxelNode *currentVoxelNode,
MarkerNode *currentMarkerNode,
const glm::vec3& agentPosition,
float thisNodePosition[3],
const ViewFrustum& viewFrustum,
bool viewFrustumCulling,
unsigned char * stopOctalCode)
{
unsigned char * childStopOctalCode = NULL;
static unsigned char *initialBitstreamPos = bitstreamBuffer;
if (stopOctalCode == NULL) {
stopOctalCode = rootNode->octalCode;
}
// check if we have any children
bool hasAtLeastOneChild;
for (int i = 0; i < 8; i++) {
if (currentVoxelNode->children[i] != NULL) {
hasAtLeastOneChild = true;
}
}
// if we have at least one child, check if it will be worth recursing into our children
if (hasAtLeastOneChild) {
int firstIndexToCheck = 0;
unsigned char * childMaskPointer = NULL;
float halfUnitForVoxel = powf(0.5, *currentVoxelNode->octalCode) * (0.5 * TREE_SCALE);
float distanceToVoxelCenter = sqrtf(powf(agentPosition[0] - thisNodePosition[0] - halfUnitForVoxel, 2) +
powf(agentPosition[1] - thisNodePosition[1] - halfUnitForVoxel, 2) +
powf(agentPosition[2] - thisNodePosition[2] - halfUnitForVoxel, 2));
// If the voxel is outside of the view frustum, then don't bother sending or recursing
bool voxelInView = true;
/**** not yet working properly at this level! **************************************************************************
if (viewFrustumCulling) {
float fullUnitForVoxel = halfUnitForVoxel * 2.0f;
AABox voxelBox;
voxelBox.setBox(glm::vec3(thisNodePosition[0],thisNodePosition[1],thisNodePosition[2]),
fullUnitForVoxel,fullUnitForVoxel,fullUnitForVoxel);
//printf("VoxelTree::loadBitstreamBuffer() voxelBox.corner=(%f,%f,%f) x=%f \n",
// voxelBox.getCorner().x,voxelBox.getCorner().y,voxelBox.getCorner().z, voxelBox.getSize().x);
voxelInView = (ViewFrustum::OUTSIDE != viewFrustum.pointInFrustum(voxelBox.getCorner()));
} else {
voxelInView = true;
}
**********************************************************************************************************************/
// if the distance to this voxel's center is less than the threshold
// distance for its children, we should send the children
bool voxelIsClose = (distanceToVoxelCenter < boundaryDistanceForRenderLevel(*currentVoxelNode->octalCode + 1));
bool sendVoxel = voxelIsClose && voxelInView;
if (sendVoxel) {
// write this voxel's data if we're below or at
// or at the same level as the stopOctalCode
if (*currentVoxelNode->octalCode >= *stopOctalCode) {
if ((bitstreamBuffer - initialBitstreamPos) + MAX_TREE_SLICE_BYTES > MAX_VOXEL_PACKET_SIZE) {
// we can't send this packet, not enough room
// return our octal code as the stop
return currentVoxelNode->octalCode;
}
if (strcmp((char *)stopOctalCode, (char *)currentVoxelNode->octalCode) == 0) {
// this is is the root node for this packet
// add the leading V
*(bitstreamBuffer++) = PACKET_HEADER_VOXEL_DATA;
// add its octal code to the packet
int octalCodeBytes = bytesRequiredForCodeLength(*currentVoxelNode->octalCode);
memcpy(bitstreamBuffer, currentVoxelNode->octalCode, octalCodeBytes);
bitstreamBuffer += octalCodeBytes;
}
// default color mask is 0, increment pointer for colors
*bitstreamBuffer = 0;
// keep a colorPointer so we can check how many colors were added
unsigned char *colorPointer = bitstreamBuffer + 1;
for (int i = 0; i < 8; i++) {
// Rules for including a child:
// 1) child must exists
if ((currentVoxelNode->children[i] != NULL)) {
// 2) child must have a color...
if (currentVoxelNode->children[i]->isColored()) {
unsigned char* childOctalCode = currentVoxelNode->children[i]->octalCode;
float childPosition[3];
copyFirstVertexForCode(childOctalCode,(float*)&childPosition);
childPosition[0] *= TREE_SCALE; // scale it up
childPosition[1] *= TREE_SCALE; // scale it up
childPosition[2] *= TREE_SCALE; // scale it up
float halfChildVoxel = powf(0.5, *childOctalCode) * (0.5 * TREE_SCALE);
//float distanceToChildCenter = sqrtf(powf(agentPosition[0] - childPosition[0] - halfChildVoxel, 2) +
// powf(agentPosition[1] - childPosition[1] - halfChildVoxel, 2) +
// powf(agentPosition[2] - childPosition[2] - halfChildVoxel, 2));
float fullChildVoxel = halfChildVoxel * 2.0f;
AABox childBox;
childBox.setBox(glm::vec3(childPosition[0], childPosition[1], childPosition[2]),
fullChildVoxel, fullChildVoxel, fullChildVoxel);
// XXXBHG - not sure we want to do this "distance/LOD culling" at this level.
//bool childIsClose = (distanceToChildCenter < boundaryDistanceForRenderLevel(*childOctalCode + 1));
bool childIsClose = true; // for now, assume we're close enough
bool childInView = !viewFrustumCulling ||
(ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(childBox));
/// XXXBHG - debug code, switch this to true, and we'll send everything but include false coloring
// on voxels based on whether or not they match these rules.
bool falseColorInsteadOfCulling = false;
// removed childIsClose - until we determine if we want to include that
bool sendChild = (childInView) || falseColorInsteadOfCulling;
// if we sendAnyway, we'll do false coloring of the voxels based on childIsClose && childInView
if (sendChild) {
// copy in the childs color to bitstreamBuffer
if (childIsClose && childInView) {
// true color
memcpy(colorPointer, currentVoxelNode->children[i]->getTrueColor(), 3);
} else {
unsigned char red[3] = {255,0,0};
unsigned char green[3] = {0,255,0};
unsigned char blue[3] = {0,0,255};
if (!childIsClose && !childInView) {
// If both too far, and not in view, color them red
memcpy(colorPointer, red, 3);
} else if (!childIsClose) {
// If too far, but in view, color them blue
memcpy(colorPointer, blue, 3);
} else {
// If close, but out of view, color them green
memcpy(colorPointer, green, 3);
}
}
colorPointer += 3;
// set the colorMask by bitshifting the value of childExists
*bitstreamBuffer += (1 << (7 - i));
}
}
}
}
// push the bitstreamBuffer forwards for the number of added colors
bitstreamBuffer += (colorPointer - bitstreamBuffer);
// maintain a pointer to this spot in the buffer so we can set our child mask
// depending on the results of the recursion below
childMaskPointer = bitstreamBuffer++;
// reset the childMaskPointer for this node to 0
*childMaskPointer = 0;
} else {
firstIndexToCheck = *stopOctalCode > 0
? branchIndexWithDescendant(currentVoxelNode->octalCode, stopOctalCode)
: 0;
}
unsigned char * arrBufferBeforeChild = bitstreamBuffer;
for (int i = firstIndexToCheck; i < 8; i ++) {
// ask the child to load this bitstream buffer
// if they or their descendants fill the MTU we will receive the childStopOctalCode back
if (currentVoxelNode->children[i] != NULL) {
if (!oneAtBit(currentMarkerNode->childrenVisitedMask, i)) {
// create the marker node for this child if it does not yet exist
if (currentMarkerNode->children[i] == NULL) {
currentMarkerNode->children[i] = new MarkerNode();
}
float childNodePosition[3];
copyFirstVertexForCode(currentVoxelNode->children[i]->octalCode,(float*)&childNodePosition);
childNodePosition[0] *= TREE_SCALE; // scale it up
childNodePosition[1] *= TREE_SCALE; // scale it up
childNodePosition[2] *= TREE_SCALE; // scale it up
/**** disabled *****************************************************************************************
// Note: Stephen, I intentionally left this in so you would talk to me about it. Here's the deal, this
// code doesn't seem to work correctly. It returns X and Z flipped and the values are negative. Since
// we use the firstVertexForCode() function in VoxelSystem to calculate the child vertex and that DOES
// work, I've decided to use that function to calculate our position for LOD handling.
//
// calculate the child's position based on the parent position
for (int j = 0; j < 3; j++) {
childNodePosition[j] = thisNodePosition[j];
if (oneAtBit(branchIndexWithDescendant(currentVoxelNode->octalCode,
currentVoxelNode->children[i]->octalCode),
(7 - j))) {
childNodePosition[j] -= (powf(0.5, *currentVoxelNode->children[i]->octalCode) * TREE_SCALE);
}
}
**** disabled *****************************************************************************************/
// ask the child to load the bitstream buffer with their data
childStopOctalCode = loadBitstreamBuffer(bitstreamBuffer,
currentVoxelNode->children[i],
currentMarkerNode->children[i],
agentPosition,
childNodePosition,
viewFrustum,
viewFrustumCulling,
stopOctalCode);
if (bitstreamBuffer - arrBufferBeforeChild > 0) {
// this child added data to the packet - add it to our child mask
if (childMaskPointer != NULL) {
*childMaskPointer += (1 << (7 - i));
}
arrBufferBeforeChild = bitstreamBuffer;
}
}
}
if (childStopOctalCode != NULL) {
break;
} else {
// this child node has been covered
// add the appropriate bit to the childrenVisitedMask for the current marker node
currentMarkerNode->childrenVisitedMask += 1 << (7 - i);
// if we are above the stopOctal and we got a NULL code
// we cannot go to the next child
// so break and return the NULL stop code
if (*currentVoxelNode->octalCode < *stopOctalCode) {
break;
}
}
}
}
}
return childStopOctalCode;
}
void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int bufferSizeBytes) {
// XXXBHG: validate buffer is at least 4 bytes long? other guards??
unsigned short int itemNumber = (*((unsigned short int*)&bitstream[1]));
printLog("processRemoveVoxelBitstream() receivedBytes=%d itemNumber=%d\n",bufferSizeBytes,itemNumber);
int atByte = 3;
unsigned char* pVoxelData = (unsigned char*)&bitstream[3];
while (atByte < bufferSizeBytes) {
unsigned char octets = (unsigned char)*pVoxelData;
int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color!
float* vertices = firstVertexForCode(pVoxelData);
printLog("deleting voxel at: %f,%f,%f\n",vertices[0],vertices[1],vertices[2]);
delete []vertices;
deleteVoxelCodeFromTree(pVoxelData);
pVoxelData+=voxelDataSize;
atByte+=voxelDataSize;
}
}
void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
int colorMask = 0;
// create the color mask
for (int i = 0; i < 8; i++) {
if (startNode->children[i] != NULL && startNode->children[i]->isColored()) {
colorMask += (1 << (7 - i));
}
}
printLog("color mask: ");
outputBits(colorMask);
// output the colors we have
for (int j = 0; j < 8; j++) {
if (startNode->children[j] != NULL && startNode->children[j]->isColored()) {
printf("color %d : ",j);
for (int c = 0; c < 3; c++) {
outputBits(startNode->children[j]->getTrueColor()[c],false);
}
startNode->children[j]->printDebugDetails("");
//printf("\n");
}
}
unsigned char childMask = 0;
for (int k = 0; k < 8; k++) {
if (startNode->children[k] != NULL) {
childMask += (1 << (7 - k));
}
}
printLog("child mask: ");
outputBits(childMask);
if (childMask > 0) {
// ask children to recursively output their trees
// if they aren't a leaf
for (int l = 0; l < 8; l++) {
if (startNode->children[l] != NULL) {
printTreeForDebugging(startNode->children[l]);
}
}
}
}
void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) {
bool hasChildren = false;
for (int i = 0; i < 8; i++) {
if (startNode->children[i] != NULL) {
reaverageVoxelColors(startNode->children[i]);
hasChildren = true;
}
}
if (hasChildren) {
bool childrenCollapsed = startNode->collapseIdenticalLeaves();
if (!childrenCollapsed) {
startNode->setColorFromAverageOfChildren();
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: VoxelTree::loadVoxelsFile()
// Description: Loads HiFidelity encoded Voxels from a binary file. The current file
// format is a stream of single voxels with color data.
// Complaints: Brad :)
void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
int vCount = 0;
std::ifstream file(fileName, std::ios::in|std::ios::binary);
char octets;
unsigned int lengthInBytes;
int totalBytesRead = 0;
if(file.is_open()) {
printLog("loading file...\n");
bool bail = false;
while (!file.eof() && !bail) {
file.get(octets);
//printLog("octets=%d...\n",octets);
totalBytesRead++;
lengthInBytes = bytesRequiredForCodeLength(octets)-1; //(octets*3/8)+1;
unsigned char * voxelData = new unsigned char[lengthInBytes+1+3];
voxelData[0]=octets;
char byte;
for (size_t i = 0; i < lengthInBytes; i++) {
file.get(byte);
totalBytesRead++;
voxelData[i+1] = byte;
}
// read color data
char colorRead;
unsigned char red,green,blue;
file.get(colorRead);
red = (unsigned char)colorRead;
file.get(colorRead);
green = (unsigned char)colorRead;
file.get(colorRead);
blue = (unsigned char)colorRead;
printLog("voxel color from file red:%d, green:%d, blue:%d \n",red,green,blue);
vCount++;
int colorRandomizer = wantColorRandomizer ? randIntInRange (-5, 5) : 0;
voxelData[lengthInBytes+1] = std::max(0,std::min(255,red + colorRandomizer));
voxelData[lengthInBytes+2] = std::max(0,std::min(255,green + colorRandomizer));
voxelData[lengthInBytes+3] = std::max(0,std::min(255,blue + colorRandomizer));
printLog("voxel color after rand red:%d, green:%d, blue:%d\n",
voxelData[lengthInBytes+1], voxelData[lengthInBytes+2], voxelData[lengthInBytes+3]);
//printVoxelCode(voxelData);
this->readCodeColorBufferToTree(voxelData);
delete voxelData;
}
file.close();
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Method: VoxelTree::createSphere()
// Description: Creates a sphere of voxels in the local system at a given location/radius
// To Do: Move this function someplace better?
// Complaints: Brad :)
void VoxelTree::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) {
// About the color of the sphere... we're going to make this sphere be a gradient
// between two RGB colors. We will do the gradient along the phi spectrum
unsigned char dominantColor1 = randIntInRange(1,3); //1=r, 2=g, 3=b dominant
unsigned char dominantColor2 = randIntInRange(1,3);
if (dominantColor1==dominantColor2) {
dominantColor2 = dominantColor1+1%3;
}
unsigned char r1 = (dominantColor1==1)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char g1 = (dominantColor1==2)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char b1 = (dominantColor1==3)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char r2 = (dominantColor2==1)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char g2 = (dominantColor2==2)?randIntInRange(200,255):randIntInRange(40,100);
unsigned char b2 = (dominantColor2==3)?randIntInRange(200,255):randIntInRange(40,100);
// We initialize our rgb to be either "grey" in case of randomized surface, or
// the average of the gradient, in the case of the gradient sphere.
unsigned char red = wantColorRandomizer ? 128 : (r1+r2)/2; // average of the colors
unsigned char green = wantColorRandomizer ? 128 : (g1+g2)/2;
unsigned char blue = wantColorRandomizer ? 128 : (b1+b2)/2;
// Psuedocode for creating a sphere:
//
// for (theta from 0 to 2pi):
// for (phi from 0 to pi):
// x = xc+r*cos(theta)*sin(phi)
// y = yc+r*sin(theta)*sin(phi)
// z = zc+r*cos(phi)
int t=0; // total points
// We want to make sure that as we "sweep" through our angles we use a delta angle that's small enough to not skip any
// voxels we can calculate theta from our desired arc length
// lenArc = ndeg/360deg * 2pi*R ---> lenArc = theta/2pi * 2pi*R
// lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r
float angleDelta = (s/r);
// assume solid for now
float ri = 0.0;
if (!solid) {
ri=r; // just the outer surface
}
// If you also iterate form the interior of the sphere to the radius, makeing
// larger and larger sphere's you'd end up with a solid sphere. And lots of voxels!
for (; ri <= (r+(s/2.0)); ri+=s) {
//printLog("radius: ri=%f ri+s=%f (r+(s/2.0))=%f\n",ri,ri+s,(r+(s/2.0)));
for (float theta=0.0; theta <= 2*M_PI; theta += angleDelta) {
for (float phi=0.0; phi <= M_PI; phi += angleDelta) {
t++; // total voxels
float x = xc+ri*cos(theta)*sin(phi);
float y = yc+ri*sin(theta)*sin(phi);
float z = zc+ri*cos(phi);
// gradient color data
float gradient = (phi/M_PI);
// only use our actual desired color on the outer edge, otherwise
// use our "average" color
if (ri+(s*2.0)>=r) {
//printLog("painting candy shell radius: ri=%f r=%f\n",ri,r);
red = wantColorRandomizer ? randomColorValue(165) : r1+((r2-r1)*gradient);
green = wantColorRandomizer ? randomColorValue(165) : g1+((g2-g1)*gradient);
blue = wantColorRandomizer ? randomColorValue(165) : b1+((b2-b1)*gradient);
}
unsigned char* voxelData = pointToVoxel(x,y,z,s,red,green,blue);
this->readCodeColorBufferToTree(voxelData);
delete voxelData;
}
}
}
this->reaverageVoxelColors(this->rootNode);
}
// 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 stick the node in the bag to for later
// endcoding.
void VoxelTree::searchForColoredNodes(VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) {
// call the recursive version, this will add all found colored node roots to the bag
searchForColoredNodesRecursion(rootNode, viewFrustum, bag);
}
void VoxelTree::searchForColoredNodesRecursion(VoxelNode* node, const ViewFrustum& viewFrustum, VoxelNodeBag& bag) {
// 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;
}
const int MAX_CHILDREN = 8;
VoxelNode* inViewChildren[MAX_CHILDREN];
float distancesToChildren[MAX_CHILDREN];
int positionOfChildren[MAX_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 < MAX_CHILDREN; i++) {
VoxelNode* childNode = node->children[i];
bool childExists = (childNode != NULL);
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) {
// 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);
//printf("child[%d] distance=%f\n",i,distance);
inViewCount = insertIntoSortedArrays((void*)childNode, distance, i,
(void**)&inViewChildren, (float*)&distancesToChildren, (int*)&positionOfChildren,
inViewCount, MAX_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];
searchForColoredNodesRecursion(childNode, viewFrustum, bag);
}
}
}
// 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,
VoxelNodeBag& bag) {
// 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)) {
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, bag);
// 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,
VoxelNodeBag& bag) 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...
bag.insert(node);
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, bag);
// 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;
}