Merge pull request #216 from ZappoMan/render_voxels_optimization

First Cut at View Frustum Culling in Client to Speed up Rendering
This commit is contained in:
Philip Rosedale 2013-05-06 16:48:43 -07:00
commit 76b45a79c6
14 changed files with 428 additions and 282 deletions

View file

@ -15,6 +15,7 @@
#include <fstream> // to load voxels from file
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <OctalCode.h>
#include <pthread.h>
#include "Log.h"
@ -41,7 +42,8 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
4,5,6, 4,6,7 }; // Z+ .
VoxelSystem::VoxelSystem() {
_voxelsInArrays = _voxelsUpdated = 0;
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
_alwaysRenderFullVBO = true;
_tree = new VoxelTree();
pthread_mutex_init(&_bufferWriteLock, NULL);
}
@ -58,7 +60,7 @@ VoxelSystem::~VoxelSystem() {
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
_tree->loadVoxelsFile(fileName, wantColorRandomizer);
copyWrittenDataToReadArrays();
setupNewVoxelsForDrawing();
}
void VoxelSystem::createSphere(float r,float xc, float yc, float zc, float s, bool solid, bool wantColorRandomizer) {
@ -98,24 +100,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
switch(command) {
case PACKET_HEADER_VOXEL_DATA:
{
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "readBitstreamToTree()");
// ask the VoxelTree to read the bitstream into the tree
_tree->readBitstreamToTree(voxelData, numBytes - 1);
if (_renderWarningsOn && _tree->getNodesChangedFromBitstream()) {
printLog("readBitstreamToTree()... getNodesChangedFromBitstream=%ld _tree->isDirty()=%s \n",
_tree->getNodesChangedFromBitstream(), (_tree->isDirty() ? "yes" : "no") );
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! readBitstreamToTree() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! readBitstreamToTree() took %lf milliseconds\n",elapsedmsec);
}
}
}
break;
case PACKET_HEADER_ERASE_VOXEL:
@ -138,7 +125,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
if (0==strcmp(command,(char*)"erase all")) {
printLog("got Z message == erase all\n");
_tree->eraseAllVoxels();
_voxelsInArrays = 0; // better way to do this??
_voxelsInReadArrays = _voxelsInWriteArrays = 0; // better way to do this??
}
if (0==strcmp(command,(char*)"add scene")) {
printLog("got Z message == add scene - NOT SUPPORTED ON INTERFACE\n");
@ -153,16 +140,29 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
}
void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
double start = usecTimestampNow();
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished);
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000.0;
if (sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed,SIXTY_FPS_IN_MILLISECONDS)) {
return; // bail early, it hasn't been long enough since the last time we ran
}
double sinceLastViewCulling = (start - _lastViewCulling) / 1000.0;
// If the view frustum has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= VIEW_CULLING_RATE_IN_MILLISECONDS) && hasViewChanged()) {
_lastViewCulling = start;
removeOutOfView();
}
if (_tree->isDirty()) {
PerformanceWarning warn(_renderWarningsOn, "calling... newTreeToArrays()");
_callsToTreesToArrays++;
if (_alwaysRenderFullVBO) {
_voxelsInWriteArrays = 0; // reset our VBO
}
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
} else {
@ -172,78 +172,88 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
_voxelsDirty=true;
}
if (_voxelsDirty) {
// copy the newly written data to the arrays designated for reading
copyWrittenDataToReadArrays();
}
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
copyWrittenDataToReadArrays();
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! newTreeToArrays() took %lf seconds %ld voxels updated\n", elapsedsec, _voxelsUpdated);
} else {
printLog("WARNING! newTreeToArrays() took %lf milliseconds %ld voxels updated\n", elapsedmsec, _voxelsUpdated);
}
}
_setupNewVoxelsForDrawingLastFinished = end;
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
}
void VoxelSystem::copyWrittenDataToReadArrays() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()"); // would like to include _voxelsInArrays, _voxelsUpdated
if (_voxelsDirty && _voxelsUpdated) {
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock);
int bytesOfVertices = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
int bytesOfColors = (_voxelsInArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices);
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
_voxelsInReadArrays = _voxelsInWriteArrays;
pthread_mutex_unlock(&_bufferWriteLock);
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! copyWrittenDataToReadArrays() took %lf seconds for %ld voxels %ld updated\n",
elapsedsec, _voxelsInArrays, _voxelsUpdated);
} else {
printLog("WARNING! copyWrittenDataToReadArrays() took %lf milliseconds for %ld voxels %ld updated\n",
elapsedmsec, _voxelsInArrays, _voxelsUpdated);
}
}
}
int VoxelSystem::newTreeToArrays(VoxelNode* node) {
assert(_viewFrustum); // you must set up _viewFrustum before calling this
int voxelsUpdated = 0;
float distanceToNode = node->distanceToCamera(*_viewFrustum);
float boundary = boundaryDistanceForRenderLevel(*node->octalCode + 1);
float childBoundary = boundaryDistanceForRenderLevel(*node->octalCode + 2);
bool inBoundary = (distanceToNode <= boundary);
bool inChildBoundary = (distanceToNode <= childBoundary);
bool shouldRender = node->isColored() && ((node->isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary));
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(*_viewFrustum);
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);
}
node->setShouldRender(shouldRender);
// let children figure out their renderness
for (int i = 0; i < 8; i++) {
if (node->children[i]) {
voxelsUpdated += newTreeToArrays(node->children[i]);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (node->getChildAtIndex(i)) {
voxelsUpdated += newTreeToArrays(node->getChildAtIndex(i));
}
}
if (_alwaysRenderFullVBO) {
voxelsUpdated += newway__updateNodeInArray(node);
} else {
voxelsUpdated += oldway__updateNodeInArray(node);
}
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated;
}
int VoxelSystem::newway__updateNodeInArray(VoxelNode* node) {
if (node->getShouldRender()) {
glm::vec3 startVertex = node->getCorner();
float voxelScale = node->getScale();
glBufferIndex nodeIndex = _voxelsInWriteArrays;
// populate the array with points for the 8 vertices
// and RGB color for each added vertex
for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) {
GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL);
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
*(writeColorsAt +j) = node->getColor()[j % 3];
}
_voxelsInWriteArrays++; // our know vertices in the arrays
return 1; // rendered
}
return 0; // not-rendered
}
int VoxelSystem::oldway__updateNodeInArray(VoxelNode* node) {
// Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays... for us
if (node->isDirty() && (shouldRender || node->isKnownBufferIndex())) {
if (node->isDirty() && (node->getShouldRender() || node->isKnownBufferIndex())) {
glm::vec3 startVertex;
float voxelScale = 0;
// If we're should render, use our legit location and scale,
if (node->getShouldRender()) {
copyFirstVertexForCode(node->octalCode, (float*)&startVertex);
voxelScale = (1 / powf(2, *node->octalCode));
startVertex = node->getCorner();
voxelScale = node->getScale();
} else {
// if we shouldn't render then set out location to some infinitely distant location,
// and our scale as infinitely small
@ -256,7 +266,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
if (node->isKnownBufferIndex()) {
nodeIndex = node->getBufferIndex();
} else {
nodeIndex = _voxelsInArrays;
nodeIndex = _voxelsInWriteArrays;
}
_voxelDirtyArray[nodeIndex] = true;
@ -271,12 +281,11 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
}
if (!node->isKnownBufferIndex()) {
node->setBufferIndex(nodeIndex);
_voxelsInArrays++; // our know vertices in the arrays
_voxelsInWriteArrays++; // our know vertices in the arrays
}
voxelsUpdated++;
return 1; // updated!
}
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
return voxelsUpdated;
return 0; // not-updated
}
VoxelSystem* VoxelSystem::clone() const {
@ -290,10 +299,13 @@ void VoxelSystem::init() {
_callsToTreesToArrays = 0;
_setupNewVoxelsForDrawingLastFinished = 0;
_setupNewVoxelsForDrawingLastElapsed = 0;
_lastViewCulling = 0;
// When we change voxels representations in the arrays, we'll update this
_voxelsDirty = false;
_voxelsInArrays = 0;
_voxelsInWriteArrays = 0;
_voxelsInReadArrays = 0;
_unusedArraySpace = 0;
// we will track individual dirty sections with this array of bools
_voxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
@ -362,59 +374,60 @@ void VoxelSystem::init() {
}
void VoxelSystem::updateVBOs() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "updateVBOs()"); // would like to include _callsToTreesToArrays
if (_voxelsDirty) {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInArrays; i++) {
if (!inSegment) {
if (_voxelDirtyArray[i]) {
segmentStart = i;
inSegment = true;
_voxelDirtyArray[i] = false; // consider us clean!
}
} else {
if (!_voxelDirtyArray[i] || (i == (_voxelsInArrays - 1)) ) {
segmentEnd = i;
inSegment = false;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
if (_alwaysRenderFullVBO) {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = _voxelsInWriteArrays;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
} else {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
if (!inSegment) {
if (_voxelDirtyArray[i]) {
segmentStart = i;
inSegment = true;
_voxelDirtyArray[i] = false; // consider us clean!
}
} else {
if (!_voxelDirtyArray[i] || (i == (_voxelsInWriteArrays - 1)) ) {
segmentEnd = i;
inSegment = false;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
}
}
}
}
_voxelsDirty = false;
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! updateVBOs() took %lf seconds after %d calls to newTreeToArrays()\n",
elapsedsec, _callsToTreesToArrays);
} else {
printLog("WARNING! updateVBOs() took %lf milliseconds after %d calls to newTreeToArrays()\n",
elapsedmsec, _callsToTreesToArrays);
}
} else {
printLog("WARNING! updateVBOs() called after %d calls to newTreeToArrays()\n",_callsToTreesToArrays);
}
}
_callsToTreesToArrays = 0; // clear it
}
void VoxelSystem::render() {
double start = usecTimestampNow();
PerformanceWarning warn(_renderWarningsOn, "render()");
glPushMatrix();
updateVBOs();
// tell OpenGL where to find vertex and color information
@ -434,7 +447,7 @@ void VoxelSystem::render() {
// draw the number of voxels we have
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
glDrawElements(GL_TRIANGLES, 36 * _voxelsInArrays, GL_UNSIGNED_INT, 0);
glDrawElements(GL_TRIANGLES, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
// deactivate vertex and color arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);
@ -447,23 +460,13 @@ void VoxelSystem::render() {
// scale back down to 1 so heads aren't massive
glPopMatrix();
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printLog("WARNING! render() took %lf seconds\n",elapsedsec);
} else {
printLog("WARNING! render() took %lf milliseconds\n",elapsedmsec);
}
}
}
int VoxelSystem::_nodeCount = 0;
void VoxelSystem::killLocalVoxels() {
_tree->eraseAllVoxels();
_voxelsInArrays = 0; // better way to do this??
_voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this??
//setupNewVoxelsForDrawing();
}
@ -584,4 +587,33 @@ void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) {
setupNewVoxelsForDrawing();
}
// "Remove" voxels from the tree that are not in view. We don't actually delete them,
// we remove them from the tree and place them into a holding area for later deletion
bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) {
VoxelSystem* thisVoxelSystem = (VoxelSystem*) extraData;
_nodeCount++;
// Need to operate on our child nodes, so we can remove them
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
if (childNode && !childNode->isInView(*thisVoxelSystem->_viewFrustum)) {
node->removeChildAtIndex(i);
thisVoxelSystem->_removedVoxels.insert(childNode);
}
}
return true; // keep going!
}
bool VoxelSystem::hasViewChanged() {
bool result = false; // assume the best
if (_viewFrustum && !_lastKnowViewFrustum.matches(_viewFrustum)) {
result = true;
_lastKnowViewFrustum = *_viewFrustum; // save last known
}
return result;
}
void VoxelSystem::removeOutOfView() {
PerformanceWarning warn(_renderWarningsOn, "removeOutOfView()"); // would like to include removedCount, _nodeCount, _removedVoxels.count()
_nodeCount = 0;
_tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)this);
}

View file

@ -37,7 +37,7 @@ public:
void render();
unsigned long getVoxelsUpdated() const {return _voxelsUpdated;};
unsigned long getVoxelsRendered() const {return _voxelsInArrays;};
unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;};
void setViewerAvatar(Avatar *newViewerAvatar) { _viewerAvatar = newViewerAvatar; };
void setCamera(Camera* newCamera) { _camera = newCamera; };
@ -61,9 +61,13 @@ public:
void killLocalVoxels();
void setRenderPipelineWarnings(bool on) { _renderWarningsOn = on; };
bool getRenderPipelineWarnings() const { return _renderWarningsOn; };
void removeOutOfView();
bool hasViewChanged();
private:
int _callsToTreesToArrays;
VoxelNodeBag _removedVoxels;
bool _renderWarningsOn;
// Operation functions for tree recursion methods
@ -74,6 +78,10 @@ private:
static bool falseColorizeInViewOperation(VoxelNode* node, void* extraData);
static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData);
static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData);
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
int newway__updateNodeInArray(VoxelNode* node);
int oldway__updateNodeInArray(VoxelNode* node);
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
static float _maxDistance;
@ -88,11 +96,15 @@ private:
GLubyte* _writeColorsArray;
bool* _voxelDirtyArray;
unsigned long _voxelsUpdated;
unsigned long _voxelsInArrays;
unsigned long _voxelsInWriteArrays;
unsigned long _voxelsInReadArrays;
unsigned long _unusedArraySpace;
bool _alwaysRenderFullVBO;
double _setupNewVoxelsForDrawingLastElapsed;
double _setupNewVoxelsForDrawingLastFinished;
double _lastViewCulling;
GLuint _vboVerticesID;
GLuint _vboNormalsID;
@ -101,6 +113,7 @@ private:
pthread_mutex_t _bufferWriteLock;
ViewFrustum* _viewFrustum;
ViewFrustum _lastKnowViewFrustum;
int newTreeToArrays(VoxelNode *currentNode);
void setupNewVoxelsForDrawing();

View file

@ -103,3 +103,20 @@ int PerfStat::DumpStats(char** array) {
return lineCount;
}
// Destructor handles recording all of our stats
PerformanceWarning::~PerformanceWarning() {
double end = usecTimestampNow();
double elapsedmsec = (end - _start) / 1000.0;
if (_renderWarningsOn && elapsedmsec > 1) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - _start) / 1000000.0;
printLog("WARNING! %s took %lf seconds\n", _message, elapsedsec);
} else {
printLog("WARNING! %s took %lf milliseconds\n", _message, elapsedmsec);
}
}
};

View file

@ -13,6 +13,7 @@
#define __hifi__PerfStat__
#include <stdint.h>
#include "SharedUtil.h"
#ifdef _WIN32
#define snprintf _snprintf
@ -81,5 +82,19 @@ public:
typedef std::map<std::string,PerfStatHistory,std::less<std::string> >::iterator PerfStatMapItr;
class PerformanceWarning {
private:
double _start;
const char* _message;
bool _renderWarningsOn;
public:
PerformanceWarning(bool renderWarnings, const char* message) :
_start(usecTimestampNow()),
_message(message),
_renderWarningsOn(renderWarnings) { };
~PerformanceWarning();
};
#endif /* defined(__hifi__PerfStat__) */

View file

@ -14,8 +14,9 @@
void AABox::scale(float scale) {
_corner = _corner*scale;
_size = _size*scale;
_corner = _corner * scale;
_size = _size * scale;
_center = _center * scale;
}
@ -36,6 +37,7 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) {
_size.z = -size.z;
_corner.z -= _size.z;
}
_center = _corner + (_size * 0.5f);
}
glm::vec3 AABox::getVertexP(const glm::vec3 &normal) const {
@ -101,15 +103,15 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct
}
// check each axis
float axisDistance;
if (findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z) ||
findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z)) ||
(findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z) ||
findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z)) ||
(findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) &&
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x)) {
isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) {
distance = axisDistance;
return true;
}

View file

@ -34,12 +34,14 @@ public:
const glm::vec3& getCorner() const { return _corner; };
const glm::vec3& getSize() const { return _size; };
const glm::vec3& getCenter() const { return _center; };
bool contains(const glm::vec3& point) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
private:
glm::vec3 _corner;
glm::vec3 _center;
glm::vec3 _size;
};

View file

@ -224,6 +224,53 @@ int ViewFrustum::boxInFrustum(const AABox& box) const {
}
return(result);
}
bool ViewFrustum::matches(const ViewFrustum& compareTo) const {
bool debug = false;
bool result = compareTo._position == _position &&
compareTo._direction == _direction &&
compareTo._up == _up &&
compareTo._right == _right &&
compareTo._fieldOfView == _fieldOfView &&
compareTo._aspectRatio == _aspectRatio &&
compareTo._nearClip == _nearClip &&
compareTo._farClip == _farClip;
if (!result && debug) {
printLog("ViewFrustum::matches()... result=%s\n", (result ? "yes" : "no"));
printLog("%s -- compareTo._position=%f,%f,%f _position=%f,%f,%f\n",
(compareTo._position == _position ? "MATCHES " : "NO MATCH"),
compareTo._position.x, compareTo._position.y, compareTo._position.z,
_position.x, _position.y, _position.z );
printLog("%s -- compareTo._direction=%f,%f,%f _direction=%f,%f,%f\n",
(compareTo._direction == _direction ? "MATCHES " : "NO MATCH"),
compareTo._direction.x, compareTo._direction.y, compareTo._direction.z,
_direction.x, _direction.y, _direction.z );
printLog("%s -- compareTo._up=%f,%f,%f _up=%f,%f,%f\n",
(compareTo._up == _up ? "MATCHES " : "NO MATCH"),
compareTo._up.x, compareTo._up.y, compareTo._up.z,
_up.x, _up.y, _up.z );
printLog("%s -- compareTo._right=%f,%f,%f _right=%f,%f,%f\n",
(compareTo._right == _right ? "MATCHES " : "NO MATCH"),
compareTo._right.x, compareTo._right.y, compareTo._right.z,
_right.x, _right.y, _right.z );
printLog("%s -- compareTo._fieldOfView=%f _fieldOfView=%f\n",
(compareTo._fieldOfView == _fieldOfView ? "MATCHES " : "NO MATCH"),
compareTo._fieldOfView, _fieldOfView);
printLog("%s -- compareTo._aspectRatio=%f _aspectRatio=%f\n",
(compareTo._aspectRatio == _aspectRatio ? "MATCHES " : "NO MATCH"),
compareTo._aspectRatio, _aspectRatio);
printLog("%s -- compareTo._nearClip=%f _nearClip=%f\n",
(compareTo._nearClip == _nearClip ? "MATCHES " : "NO MATCH"),
compareTo._nearClip, _nearClip);
printLog("%s -- compareTo._farClip=%f _farClip=%f\n",
(compareTo._farClip == _farClip ? "MATCHES " : "NO MATCH"),
compareTo._farClip, _farClip);
}
return result;
}
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);

View file

@ -98,6 +98,9 @@ public:
int sphereInFrustum(const glm::vec3& center, float radius) const;
int boxInFrustum(const AABox& box) const;
// some frustum comparisons
bool matches(const ViewFrustum& compareTo) const;
bool matches(const ViewFrustum* compareTo) const { return matches(*compareTo); };
void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const;
};

View file

@ -14,6 +14,8 @@
#include <limits.h>
const int NUMBER_OF_CHILDREN = 8;
const int MAX_VOXEL_PACKET_SIZE = 1492;
const int MAX_TREE_SLICE_BYTES = 26;
const int TREE_SCALE = 10;
@ -27,4 +29,5 @@ typedef unsigned long int glBufferIndex;
const glBufferIndex GLBUFFER_INDEX_UNKNOWN = ULONG_MAX;
const double SIXTY_FPS_IN_MILLISECONDS = 1000.0/60;
const double VIEW_CULLING_RATE_IN_MILLISECONDS = 1000.0; // once a second is fine
#endif

View file

@ -20,29 +20,41 @@ using voxels_lib::printLog;
// using voxels_lib::printLog;
VoxelNode::VoxelNode() {
octalCode = NULL;
unsigned char* rootCode = new unsigned char[1];
*rootCode = 0;
init(rootCode);
}
VoxelNode::VoxelNode(unsigned char * octalCode) {
init(octalCode);
}
void VoxelNode::init(unsigned char * octalCode) {
_octalCode = octalCode;
#ifdef HAS_FALSE_COLOR
_falseColored = false; // assume true color
#endif
// default pointers to child nodes to NULL
for (int i = 0; i < 8; i++) {
children[i] = NULL;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
_children[i] = NULL;
}
_glBufferIndex = GLBUFFER_INDEX_UNKNOWN;
_isDirty = true;
_shouldRender = false;
calculateAABox();
}
VoxelNode::~VoxelNode() {
delete[] octalCode;
delete[] _octalCode;
// delete all of this node's children
for (int i = 0; i < 8; i++) {
if (children[i]) {
delete children[i];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i]) {
delete _children[i];
}
}
}
@ -55,33 +67,47 @@ void VoxelNode::setShouldRender(bool shouldRender) {
}
}
void VoxelNode::getAABox(AABox& box) const {
void VoxelNode::calculateAABox() {
glm::vec3 corner;
glm::vec3 size;
// copy corner into box
copyFirstVertexForCode(octalCode,(float*)&corner);
copyFirstVertexForCode(_octalCode,(float*)&corner);
// this tells you the "size" of the voxel
float voxelScale = 1 / powf(2, *octalCode);
float voxelScale = 1 / powf(2, *_octalCode);
size = glm::vec3(voxelScale,voxelScale,voxelScale);
box.setBox(corner,size);
_box.setBox(corner,size);
}
void VoxelNode::deleteChildAtIndex(int childIndex) {
if (_children[childIndex]) {
delete _children[childIndex];
_children[childIndex] = NULL;
}
}
// does not delete the node!
VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
VoxelNode* returnedChild = _children[childIndex];
if (_children[childIndex]) {
_children[childIndex] = NULL;
}
return returnedChild;
}
void VoxelNode::addChildAtIndex(int childIndex) {
if (!children[childIndex]) {
children[childIndex] = new VoxelNode();
if (!_children[childIndex]) {
_children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex));
// XXXBHG - When the node is constructed, it should be cleanly set up as
// true colored, but for some reason, not so much. I've added a a basecamp
// to-do to research this. But for now we'll use belt and suspenders and set
// it to not-false-colored here!
children[childIndex]->setFalseColored(false);
_children[childIndex]->setFalseColored(false);
// give this child its octal code
children[childIndex]->octalCode = childOctalCode(octalCode, childIndex);
_isDirty = true;
}
}
@ -89,10 +115,10 @@ void VoxelNode::addChildAtIndex(int childIndex) {
// will average the child colors...
void VoxelNode::setColorFromAverageOfChildren() {
int colorArray[4] = {0,0,0,0};
for (int i = 0; i < 8; i++) {
if (children[i] != NULL && children[i]->isColored()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i] && _children[i]->isColored()) {
for (int j = 0; j < 3; j++) {
colorArray[j] += children[i]->getTrueColor()[j]; // color averaging should always be based on true colors
colorArray[j] += _children[i]->getTrueColor()[j]; // color averaging should always be based on true colors
}
colorArray[3]++;
}
@ -168,19 +194,19 @@ bool VoxelNode::collapseIdenticalLeaves() {
// scan children, verify that they are ALL present and accounted for
bool allChildrenMatch = true; // assume the best (ottimista)
int red,green,blue;
for (int i = 0; i < 8; i++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
// if no child, or child doesn't have a color
if (children[i] == NULL || !children[i]->isColored()) {
if (!_children[i] || !_children[i]->isColored()) {
allChildrenMatch=false;
//printLog("SADNESS child missing or not colored! i=%d\n",i);
break;
} else {
if (i==0) {
red = children[i]->getColor()[0];
green = children[i]->getColor()[1];
blue = children[i]->getColor()[2];
} else if (red != children[i]->getColor()[0] ||
green != children[i]->getColor()[1] || blue != children[i]->getColor()[2]) {
red = _children[i]->getColor()[0];
green = _children[i]->getColor()[1];
blue = _children[i]->getColor()[2];
} else if (red != _children[i]->getColor()[0] ||
green != _children[i]->getColor()[1] || blue != _children[i]->getColor()[2]) {
allChildrenMatch=false;
break;
}
@ -190,9 +216,9 @@ bool VoxelNode::collapseIdenticalLeaves() {
if (allChildrenMatch) {
//printLog("allChildrenMatch: pruning tree\n");
for (int i = 0; i < 8; i++) {
delete children[i]; // delete all the child nodes
children[i]=NULL; // set it to NULL
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
delete _children[i]; // delete all the child nodes
_children[i]=NULL; // set it to NULL
}
nodeColor collapsedColor;
collapsedColor[0]=red;
@ -215,8 +241,8 @@ void VoxelNode::setRandomColor(int minimumBrightness) {
}
bool VoxelNode::isLeaf() const {
for (int i = 0; i < 8; i++) {
if (children[i]) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i]) {
return false;
}
}
@ -224,27 +250,22 @@ bool VoxelNode::isLeaf() const {
}
void VoxelNode::printDebugDetails(const char* label) const {
AABox box;
getAABox(box);
printLog("%s - Voxel at corner=(%f,%f,%f) size=%f octcode=", label,
box.getCorner().x, box.getCorner().y, box.getCorner().z, box.getSize().x);
printOctalCode(octalCode);
_box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getSize().x);
printOctalCode(_octalCode);
}
bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const {
AABox box;
getAABox(box);
AABox box = _box; // use temporary box so we can scale it
box.scale(TREE_SCALE);
bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box));
return inView;
}
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box;
getAABox(box);
box.scale(TREE_SCALE);
float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - (box.getCorner().x + box.getSize().x), 2) +
powf(viewFrustum.getPosition().y - (box.getCorner().y + box.getSize().y), 2) +
powf(viewFrustum.getPosition().z - (box.getCorner().z + box.getSize().z), 2));
glm::vec3 center = _box.getCenter() * (float)TREE_SCALE;
float distanceToVoxelCenter = sqrtf(powf(viewFrustum.getPosition().x - center.x, 2) +
powf(viewFrustum.getPosition().y - center.y, 2) +
powf(viewFrustum.getPosition().z - center.z, 2));
return distanceToVoxelCenter;
}

View file

@ -27,23 +27,38 @@ private:
glBufferIndex _glBufferIndex;
bool _isDirty;
bool _shouldRender;
AABox _box;
unsigned char* _octalCode;
VoxelNode* _children[8];
void calculateAABox();
void init(unsigned char * octalCode);
public:
VoxelNode();
VoxelNode(); // root node constructor
VoxelNode(unsigned char * octalCode); // regular constructor
~VoxelNode();
unsigned char* getOctalCode() const { return _octalCode; };
VoxelNode* getChildAtIndex(int i) const { return _children[i]; };
void deleteChildAtIndex(int childIndex);
VoxelNode* removeChildAtIndex(int childIndex);
void addChildAtIndex(int childIndex);
void setColorFromAverageOfChildren();
void setRandomColor(int minimumBrightness);
bool collapseIdenticalLeaves();
unsigned char *octalCode;
VoxelNode *children[8];
const AABox& getAABox() const { return _box; };
const glm::vec3& getCenter() const { return _box.getCenter(); };
const glm::vec3& getCorner() const { return _box.getCorner(); };
float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ };
int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ };
bool isColored() const { return (_trueColor[3]==1); };
bool isInView(const ViewFrustum& viewFrustum) const;
float distanceToCamera(const ViewFrustum& viewFrustum) const;
bool isLeaf() const;
void getAABox(AABox& box) const;
void printDebugDetails(const char* label) const;
bool isDirty() const { return _isDirty; };
void clearDirtyBit() { _isDirty = false; };

View file

@ -32,18 +32,12 @@ void VoxelNodeBag::insert(VoxelNode* node) {
// Note: change this to binary search... instead of linear!
int insertAt = _elementsInUse;
for (int i = 0; i < _elementsInUse; i++) {
// compare the newNode to the elements already in the bag
OctalCodeComparison comparison = compareOctalCodes(_bagElements[i]->octalCode, node->octalCode);
// If we found a code in the bag that matches, then just return, since the element is already in the bag.
if (comparison == EXACT_MATCH) {
// just compare the pointers... that's good enough
if (_bagElements[i] == node) {
return; // exit early!!
}
}
// if we found a node "greater than" the inserted node, then
// we want to insert our node here.
if (comparison == GREATER_THAN) {
if (_bagElements[i] > node) {
insertAt = i;
break;
}

View file

@ -37,17 +37,14 @@ VoxelTree::VoxelTree() :
voxelsColoredStats(100),
voxelsBytesReadStats(100),
_isDirty(true) {
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];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
delete rootNode->getChildAtIndex(i);
}
}
@ -60,8 +57,8 @@ void VoxelTree::recurseTreeWithOperation(RecurseVoxelTreeOperation operation, vo
// Recurses voxel node with an operation function
void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperation operation, void* extraData) {
if (operation(node, extraData)) {
for (int i = 0; i < sizeof(node->children) / sizeof(node->children[0]); i++) {
VoxelNode* child = node->children[i];
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* child = node->getChildAtIndex(i);
if (child) {
recurseNodeWithOperation(child, operation, extraData);
}
@ -72,11 +69,11 @@ void VoxelTree::recurseNodeWithOperation(VoxelNode* node,RecurseVoxelTreeOperati
VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char * needleCode, VoxelNode** parentOfFoundNode) const {
// find the appropriate branch index based on this ancestorNode
if (*needleCode > 0) {
int branchForNeedle = branchIndexWithDescendant(ancestorNode->octalCode, needleCode);
VoxelNode *childNode = ancestorNode->children[branchForNeedle];
int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode);
VoxelNode *childNode = ancestorNode->getChildAtIndex(branchForNeedle);
if (childNode != NULL) {
if (*childNode->octalCode == *needleCode) {
if (childNode) {
if (*childNode->getOctalCode() == *needleCode) {
// If the caller asked for the parent, then give them that too...
if (parentOfFoundNode) {
@ -101,34 +98,31 @@ VoxelNode * VoxelTree::nodeForOctalCode(VoxelNode *ancestorNode, unsigned char *
// returns the node created!
VoxelNode* VoxelTree::createMissingNode(VoxelNode* lastParentNode, unsigned char* codeToReach) {
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->octalCode, codeToReach);
int indexOfNewChild = branchIndexWithDescendant(lastParentNode->getOctalCode(), codeToReach);
// we could be coming down a branch that was already created, so don't stomp on it.
if (lastParentNode->children[indexOfNewChild] == NULL) {
if (!lastParentNode->getChildAtIndex(indexOfNewChild)) {
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];
if (*lastParentNode->getChildAtIndex(indexOfNewChild)->getOctalCode() == *codeToReach) {
return lastParentNode->getChildAtIndex(indexOfNewChild);
} else {
return createMissingNode(lastParentNode->children[indexOfNewChild], codeToReach);
return createMissingNode(lastParentNode->getChildAtIndex(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++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; 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]) {
if (!destinationNode->getChildAtIndex(i)) {
destinationNode->addChildAtIndex(i);
if (destinationNode->isDirty()) {
_isDirty = true;
@ -142,9 +136,9 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
nodeColor newColor;
memcpy(newColor, nodeData + bytesRead, 3);
newColor[3] = 1;
bool nodeWasDirty = destinationNode->children[i]->isDirty();
destinationNode->children[i]->setColor(newColor);
bool nodeIsDirty = destinationNode->children[i]->isDirty();
bool nodeWasDirty = destinationNode->getChildAtIndex(i)->isDirty();
destinationNode->getChildAtIndex(i)->setColor(newColor);
bool nodeIsDirty = destinationNode->getChildAtIndex(i)->isDirty();
if (nodeIsDirty) {
_isDirty = true;
}
@ -164,11 +158,11 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
int childIndex = 0;
bytesRead++;
while (bytesLeftToRead - bytesRead > 0 && childIndex < 8) {
while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
// check the exists mask to see if we have a child to traverse into
if (oneAtBit(childMask, childIndex)) {
if (!destinationNode->children[childIndex]) {
if (!destinationNode->getChildAtIndex(childIndex)) {
// add a child at that index, if it doesn't exist
bool nodeWasDirty = destinationNode->isDirty();
destinationNode->addChildAtIndex(childIndex);
@ -184,7 +178,7 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode,
}
// tell the child to read the subsequent data
bytesRead += readNodeData(destinationNode->children[childIndex],
bytesRead += readNodeData(destinationNode->getChildAtIndex(childIndex),
nodeData + bytesRead,
bytesLeftToRead - bytesRead);
}
@ -207,7 +201,7 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
while (bitstreamAt < bitstream + bufferSizeBytes) {
VoxelNode* bitstreamRootNode = nodeForOctalCode(rootNode, (unsigned char *)bitstreamAt, NULL);
if (*bitstreamAt != *bitstreamRootNode->octalCode) {
if (*bitstreamAt != *bitstreamRootNode->getOctalCode()) {
// if the octal code returned is not on the same level as
// the code being searched for, we have VoxelNodes to create
@ -251,12 +245,11 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char *codeBuffer) {
// If the node exists...
int lengthInBytes = bytesRequiredForCodeLength(*codeBuffer); // includes octet count, not color!
if (0 == memcmp(nodeToDelete->octalCode,codeBuffer,lengthInBytes)) {
if (0 == memcmp(nodeToDelete->getOctalCode(),codeBuffer,lengthInBytes)) {
if (parentNode) {
int childIndex = branchIndexWithDescendant(parentNode->octalCode, codeBuffer);
int childIndex = branchIndexWithDescendant(parentNode->getOctalCode(), codeBuffer);
delete parentNode->children[childIndex]; // delete the child nodes
parentNode->children[childIndex] = NULL; // set it to NULL
parentNode->deleteChildAtIndex(childIndex);
reaverageVoxelColors(rootNode); // Fix our colors!! Need to call it on rootNode
_isDirty = true;
@ -268,8 +261,6 @@ 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;
_isDirty = true;
}
@ -277,7 +268,7 @@ void VoxelTree::readCodeColorBufferToTree(unsigned char *codeColorBuffer) {
VoxelNode* lastCreatedNode = nodeForOctalCode(rootNode, codeColorBuffer, NULL);
// create the node if it does not exist
if (*lastCreatedNode->octalCode != *codeColorBuffer) {
if (*lastCreatedNode->getOctalCode() != *codeColorBuffer) {
lastCreatedNode = createMissingNode(lastCreatedNode, codeColorBuffer);
_isDirty = true;
}
@ -319,8 +310,8 @@ 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()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i) && startNode->getChildAtIndex(i)->isColored()) {
colorMask += (1 << (7 - i));
}
}
@ -329,20 +320,20 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
outputBits(colorMask);
// output the colors we have
for (int j = 0; j < 8; j++) {
if (startNode->children[j] != NULL && startNode->children[j]->isColored()) {
for (int j = 0; j < NUMBER_OF_CHILDREN; j++) {
if (startNode->getChildAtIndex(j) && startNode->getChildAtIndex(j)->isColored()) {
printLog("color %d : ",j);
for (int c = 0; c < 3; c++) {
outputBits(startNode->children[j]->getTrueColor()[c],false);
outputBits(startNode->getChildAtIndex(j)->getTrueColor()[c],false);
}
startNode->children[j]->printDebugDetails("");
startNode->getChildAtIndex(j)->printDebugDetails("");
}
}
unsigned char childMask = 0;
for (int k = 0; k < 8; k++) {
if (startNode->children[k] != NULL) {
for (int k = 0; k < NUMBER_OF_CHILDREN; k++) {
if (startNode->getChildAtIndex(k)) {
childMask += (1 << (7 - k));
}
}
@ -353,9 +344,9 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
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]);
for (int l = 0; l < NUMBER_OF_CHILDREN; l++) {
if (startNode->getChildAtIndex(l)) {
printTreeForDebugging(startNode->getChildAtIndex(l));
}
}
}
@ -364,9 +355,9 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) {
void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) {
bool hasChildren = false;
for (int i = 0; i < 8; i++) {
if (startNode->children[i] != NULL) {
reaverageVoxelColors(startNode->children[i]);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (startNode->getChildAtIndex(i)) {
reaverageVoxelColors(startNode->getChildAtIndex(i));
hasChildren = true;
}
}
@ -396,8 +387,8 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
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];
lengthInBytes = bytesRequiredForCodeLength(octets) - 1;
unsigned char * voxelData = new unsigned char[lengthInBytes + 1 + 3];
voxelData[0]=octets;
char byte;
@ -437,7 +428,7 @@ void VoxelTree::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
VoxelNode* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
unsigned char* octalCode = pointToVoxel(x,y,z,s,0,0,0);
VoxelNode* node = nodeForOctalCode(rootNode, octalCode, NULL);
if (*node->octalCode != *octalCode) {
if (*node->getOctalCode() != *octalCode) {
node = NULL;
}
delete octalCode; // cleanup memory
@ -564,8 +555,7 @@ public:
bool findRayOperation(VoxelNode* node, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
AABox box;
node->getAABox(box);
AABox box = node->getAABox();
float distance;
if (!box.findRayIntersection(args->origin, args->direction, distance)) {
return false;
@ -610,22 +600,20 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
int thisLevel = currentSearchLevel;
int maxChildLevel = thisLevel;
const int MAX_CHILDREN = 8;
VoxelNode* inViewChildren[MAX_CHILDREN];
float distancesToChildren[MAX_CHILDREN];
int positionOfChildren[MAX_CHILDREN];
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 < 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());
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) {
@ -641,10 +629,10 @@ int VoxelTree::searchForColoredNodesRecursion(int maxSearchLevel, int& currentSe
float distance = childNode->distanceToCamera(viewFrustum);
if (distance < boundaryDistanceForRenderLevel(*childNode->octalCode + 1)) {
if (distance < boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1)) {
inViewCount = insertIntoSortedArrays((void*)childNode, distance, i,
(void**)&inViewChildren, (float*)&distancesToChildren,
(int*)&positionOfChildren, inViewCount, MAX_CHILDREN);
(int*)&positionOfChildren, inViewCount, NUMBER_OF_CHILDREN);
}
}
}
@ -682,8 +670,8 @@ int VoxelTree::encodeTreeBitstream(int maxEncodeLevel, VoxelNode* node, unsigned
}
// write the octal code
int codeLength = bytesRequiredForCodeLength(*node->octalCode);
memcpy(outputBuffer,node->octalCode,codeLength);
int codeLength = bytesRequiredForCodeLength(*node->getOctalCode());
memcpy(outputBuffer,node->getOctalCode(),codeLength);
outputBuffer += codeLength; // move the pointer
bytesWritten += codeLength; // keep track of byte count
@ -729,7 +717,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// caller can pass NULL as viewFrustum if they want everything
if (viewFrustum) {
float distance = node->distanceToCamera(*viewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->octalCode + 1);
float boundaryDistance = boundaryDistanceForRenderLevel(*node->getOctalCode() + 1);
// If we're too far away for our render level, then just return
if (distance >= boundaryDistance) {
@ -747,14 +735,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
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
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN 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;
const int MAX_LEVEL_BYTES = CHILD_COLOR_MASK_BYTES + NUMBER_OF_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];
@ -768,14 +755,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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 childIsInView = (childExists && (!viewFrustum || childNode->isInView(*viewFrustum)));
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childNode = node->getChildAtIndex(i);
bool childIsInView = (childNode && (!viewFrustum || childNode->isInView(*viewFrustum)));
if (childIsInView) {
// Before we determine consider this further, let's see if it's in our LOD scope...
float distance = viewFrustum ? childNode->distanceToCamera(*viewFrustum) : 0;
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->octalCode + 1) : 1;
float boundaryDistance = viewFrustum ? boundaryDistanceForRenderLevel(*childNode->getOctalCode() + 1) : 1;
if (distance < boundaryDistance) {
inViewCount++;
@ -783,13 +769,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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 (!(childExists && childNode->isLeaf())) {
if (!(childNode && childNode->isLeaf())) {
childrenExistBits += (1 << (7 - i));
inViewNotLeafCount++;
}
// track children with actual color
if (childExists && childNode->isColored()) {
if (childNode && childNode->isColored()) {
childrenColoredBits += (1 << (7 - i));
inViewWithColorCount++;
}
@ -801,9 +787,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count
// write the color data...
for (int i = 0; i < MAX_CHILDREN; i++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenColoredBits, i)) {
memcpy(writeToThisLevelBuffer, &node->children[i]->getColor(), BYTES_PER_COLOR);
memcpy(writeToThisLevelBuffer, &node->getChildAtIndex(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
}
@ -841,10 +827,10 @@ int VoxelTree::encodeTreeBitstreamRecursion(int maxEncodeLevel, int& currentEnco
// 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++) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenExistBits, i)) {
VoxelNode* childNode = node->children[i];
VoxelNode* childNode = node->getChildAtIndex(i);
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = encodeTreeBitstreamRecursion(maxEncodeLevel, thisLevel, childNode,

View file

@ -38,7 +38,7 @@ const float DEATH_STAR_RADIUS = 4.0;
const float MAX_CUBE = 0.05f;
const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000;
int PACKETS_PER_CLIENT_PER_INTERVAL = 20;
int PACKETS_PER_CLIENT_PER_INTERVAL = 50;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
@ -153,12 +153,8 @@ void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
for (int i = 0; i < 8; i++) {
if (true) {
// create a new VoxelNode to put here
currentRootNode->children[i] = new VoxelNode();
// give this child it's octal code
currentRootNode->children[i]->octalCode = childOctalCode(currentRootNode->octalCode, i);
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->children[i]);
currentRootNode->addChildAtIndex(i);
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->getChildAtIndex(i));
createdChildren = true;
}
}