mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 19:04:32 +02:00
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:
commit
76b45a79c6
14 changed files with 428 additions and 282 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue