Merge branch 'master' of https://github.com/worklist/hifi into multiple_voxel_servers

This commit is contained in:
ZappoMan 2013-07-29 15:54:52 -07:00
commit c5ab321bd9
15 changed files with 159 additions and 162 deletions

View file

@ -75,8 +75,6 @@ using namespace std;
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
static char STAR_CACHE_FILE[] = "cachedStars.txt";
static const bool TESTING_PARTICLE_SYSTEM = true;
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins in the world
@ -407,7 +405,7 @@ void Application::paintGL() {
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
_myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition());
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
@ -1497,11 +1495,11 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
args->packetsSent, args->bytesSent, elapsed, usecToSleep);
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
usleep(usecToSleep);
} else {
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
args->packetsSent, args->bytesSent, elapsed);
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
}
args->lastSendTime = now;
}
@ -1686,7 +1684,7 @@ void Application::pasteVoxels() {
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
qDebug("sending packet: %d\n", ++args.packetsSent);
args.bytesSent += args.bufferInUse;
qDebug("total bytes sent: %lld\n", args.bytesSent);
qDebug("total bytes sent: %lld\n", (long long int)args.bytesSent);
}
if (calculatedOctCode) {
@ -1751,6 +1749,8 @@ void Application::initMenu() {
_renderLookatOn->setChecked(false);
(_renderLookatIndicatorOn = renderMenu->addAction("Lookat Indicator"))->setCheckable(true);
_renderLookatIndicatorOn->setChecked(true);
(_renderParticleSystemOn = renderMenu->addAction("Particle System"))->setCheckable(true);
_renderParticleSystemOn->setChecked(true);
(_manualFirstPerson = renderMenu->addAction(
"First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P))->setCheckable(true);
(_manualThirdPerson = renderMenu->addAction(
@ -2260,7 +2260,7 @@ void Application::update(float deltaTime) {
_audio.eventuallyAnalyzePing();
#endif
if (TESTING_PARTICLE_SYSTEM) {
if (_renderParticleSystemOn->isChecked()) {
updateParticleSystem(deltaTime);
}
}
@ -2709,14 +2709,13 @@ void Application::displaySide(Camera& whichCamera) {
_myAvatar.getHead().setLookAtPosition(_myCamera.getPosition());
}
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
if (_renderLookatIndicatorOn->isChecked() && _isLookingAtOtherAvatar) {
renderLookatIndicator(_lookatOtherPosition, whichCamera);
}
}
if (TESTING_PARTICLE_SYSTEM) {
if (_renderParticleSystemOn->isChecked()) {
if (_particleSystemInitialized) {
_particleSystem.render();
}

View file

@ -267,6 +267,7 @@ private:
QAction* _renderFrameTimerOn; // Whether to show onscreen text overlay with stats
QAction* _renderLookatOn; // Whether to show lookat vectors from avatar eyes if looking at something
QAction* _renderLookatIndicatorOn;
QAction* _renderParticleSystemOn;
QAction* _manualFirstPerson; // Whether to force first-person mode
QAction* _manualThirdPerson; // Whether to force third-person mode
QAction* _logOn; // Whether to show on-screen log

View file

@ -58,6 +58,32 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
_tree = new VoxelTree();
pthread_mutex_init(&_bufferWriteLock, NULL);
pthread_mutex_init(&_treeLock, NULL);
VoxelNode::addDeleteHook(this);
_abandonedVBOSlots = 0;
}
void VoxelSystem::nodeDeleted(VoxelNode* node) {
if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {
freeBufferIndex(node->getBufferIndex());
}
}
void VoxelSystem::freeBufferIndex(glBufferIndex index) {
_freeIndexes.push_back(index);
}
void VoxelSystem::clearFreeBufferIndexes() {
for (int i = 0; i < _freeIndexes.size(); i++) {
glBufferIndex nodeIndex = _freeIndexes[i];
glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
float voxelScale = 0;
_writeVoxelDirtyArray[nodeIndex] = true;
nodeColor color = {0, 0, 0, 0};
updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
_abandonedVBOSlots++;
}
_freeIndexes.clear();
}
VoxelSystem::~VoxelSystem() {
@ -70,6 +96,8 @@ VoxelSystem::~VoxelSystem() {
delete _tree;
pthread_mutex_destroy(&_bufferWriteLock);
pthread_mutex_destroy(&_treeLock);
VoxelNode::removeDeleteHook(this);
}
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
@ -173,6 +201,9 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
uint64_t start = usecTimestampNow();
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
// clear up the VBOs for any nodes that have been recently deleted.
clearFreeBufferIndexes();
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
@ -182,7 +213,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
&& !isViewChanging() && hasViewChanged()) {
&& !isViewChanging()) {
_lastViewCulling = start;
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
@ -212,6 +243,10 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
}
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
if (_writeRenderFullVBO) {
_abandonedVBOSlots = 0; // reset the count of our abandoned slots
}
// since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are
// ok again, until/unless we call removeOutOfView()
@ -240,12 +275,19 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
void VoxelSystem::cleanupRemovedVoxels() {
PerformanceWarning warn(_renderWarningsOn, "cleanupRemovedVoxels()");
// This handles cleanup of voxels that were culled as part of our regular out of view culling operation
if (!_removedVoxels.isEmpty()) {
while (!_removedVoxels.isEmpty()) {
delete _removedVoxels.extract();
}
_writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
}
// we also might have VBO slots that have been abandoned, if too many of our VBO slots
// are abandonded we want to rerender our full VBOs
const float TOO_MANY_ABANDONED_RATIO = 0.25f;
if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) {
_writeRenderFullVBO = true;
}
}
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
@ -323,7 +365,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
bool shouldRender = false; // assume we don't need to render it
// if it's colored, we might need to render it!
shouldRender = node->calculateShouldRender(Application::getInstance()->getViewFrustum());
node->setShouldRender(shouldRender && !node->isStagedForDeletion());
node->setShouldRender(shouldRender);
// let children figure out their renderness
if (!node->isLeaf()) {
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
@ -339,13 +381,6 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
}
node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
// If the node has been asked to be deleted, but we've gotten to here, after updateNodeInArraysXXX()
// then it means our VBOs are "clean" and our vertices have been removed or not added. So we can now
// safely remove the node from the tree and actually delete it.
if (node->isStagedForDeletion()) {
_tree->deleteVoxelCodeFromTree(node->getOctalCode());
}
return voxelsUpdated;
}
@ -364,11 +399,13 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
// and RGB color for each added vertex
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
node->setBufferIndex(nodeIndex);
node->setVoxelSystem(this);
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
_voxelsInWriteArrays++; // our know vertices in the arrays
return 1; // rendered
} else {
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
node->setVoxelSystem(NULL);
}
return 0; // not-rendered
@ -393,6 +430,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
// and our scale as infinitely small
startVertex[0] = startVertex[1] = startVertex[2] = FLT_MAX;
voxelScale = 0;
_abandonedVBOSlots++;
}
// If this node has not yet been written to the array, then add it to the end of the array.
@ -402,6 +440,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
} else {
nodeIndex = _voxelsInWriteArrays;
node->setBufferIndex(nodeIndex);
node->setVoxelSystem(this);
_voxelsInWriteArrays++;
}
_writeVoxelDirtyArray[nodeIndex] = true;
@ -445,7 +484,6 @@ void VoxelSystem::init() {
_voxelsDirty = false;
_voxelsInWriteArrays = 0;
_voxelsInReadArrays = 0;
_unusedArraySpace = 0;
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[_maxVoxels];
@ -1111,7 +1149,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) {
pthread_mutex_lock(&_treeLock);
_tree->deleteVoxelAt(x, y, z, s, true);
_tree->deleteVoxelAt(x, y, z, s);
// redraw!
setupNewVoxelsForDrawing(); // do we even need to do this? Or will the next network receive kick in?
@ -1167,7 +1205,6 @@ struct FalseColorizeOccludedArgs {
long nonLeaves;
long nonLeavesOutOfView;
long nonLeavesOccluded;
long stagedForDeletion;
};
struct FalseColorizeSubTreeOperationArgs {
@ -1189,12 +1226,6 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
args->totalVoxels++;
// if this node is staged for deletion, then just return
if (node->isStagedForDeletion()) {
args->stagedForDeletion++;
return true;
}
// If we are a parent, let's see if we're completely occluded.
if (!node->isLeaf()) {
args->nonLeaves++;
@ -1275,7 +1306,6 @@ void VoxelSystem::falseColorizeOccluded() {
args.outOfView = 0;
args.subtreeVoxelsSkipped = 0;
args.nonLeaves = 0;
args.stagedForDeletion = 0;
args.nonLeavesOutOfView = 0;
args.nonLeavesOccluded = 0;
args.tree = _tree;
@ -1288,11 +1318,10 @@ void VoxelSystem::falseColorizeOccluded() {
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,
@ -1310,12 +1339,6 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraD
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
args->totalVoxels++;
// if this node is staged for deletion, then just return
if (node->isStagedForDeletion()) {
args->stagedForDeletion++;
return true;
}
// If we are a parent, let's see if we're completely occluded.
if (!node->isLeaf()) {
args->nonLeaves++;
@ -1404,7 +1427,6 @@ void VoxelSystem::falseColorizeOccludedV2() {
args.outOfView = 0;
args.subtreeVoxelsSkipped = 0;
args.nonLeaves = 0;
args.stagedForDeletion = 0;
args.nonLeavesOutOfView = 0;
args.nonLeavesOccluded = 0;
args.tree = _tree;
@ -1413,11 +1435,10 @@ void VoxelSystem::falseColorizeOccludedV2() {
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args);
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
position.x, position.y,
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
args.stagedForDeletion,
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
VoxelProjectedPolygon::pointInside_calls,
VoxelProjectedPolygon::occludes_calls,

View file

@ -28,7 +28,7 @@ class ProgramObject;
const int NUM_CHILDREN = 8;
class VoxelSystem : public NodeData {
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook {
public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
~VoxelSystem();
@ -92,6 +92,8 @@ public:
CoverageMapV2 myCoverageMapV2;
CoverageMap myCoverageMap;
virtual void nodeDeleted(VoxelNode* node);
protected:
float _treeScale;
@ -155,7 +157,7 @@ private:
unsigned long _voxelsUpdated;
unsigned long _voxelsInReadArrays;
unsigned long _voxelsInWriteArrays;
unsigned long _unusedArraySpace;
unsigned long _abandonedVBOSlots;
bool _writeRenderFullVBO;
bool _readRenderFullVBO;
@ -187,6 +189,12 @@ private:
static ProgramObject* _perlinModulateProgram;
static GLuint _permutationNormalTextureID;
int _hookID;
std::vector<glBufferIndex> _freeIndexes;
void freeBufferIndex(glBufferIndex index);
void clearFreeBufferIndexes();
};
#endif

View file

@ -370,7 +370,11 @@ glm::vec3 Avatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
glm::vec3 Avatar::getUprightEyeLevelPosition() const {
const float EYE_UP_OFFSET = 0.36f;
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
return _position + up * _scale * BODY_BALL_RADIUS_HEAD_BASE * EYE_UP_OFFSET + glm::vec3(0.0f, _pelvisToHeadLength, 0.0f);
}
void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) {
//

View file

@ -171,6 +171,7 @@ public:
glm::vec3 getGravity () const { return _gravity; }
glm::vec3 getUprightHeadPosition() const;
glm::vec3 getUprightEyeLevelPosition() const;
AvatarVoxelSystem* getVoxels() { return &_voxels; }

View file

@ -90,7 +90,7 @@ static Closure cmakeBuild(srcDir, instCommand) {
installCommand instCommand
preloadScript ''
cmakeArgs ''
projectCmakePath '/usr/bin/cmake'
projectCmakePath '/usr/local/bin/cmake'
cleanBuild 'false'
cleanInstallDir 'false'
builderImpl ''
@ -138,4 +138,4 @@ parameterizedJob.with {
(project / publishers / 'hudson.plugins.postbuildtask.PostbuildTask' /
tasks / 'hudson.plugins.postbuildtask.TaskProperties' / script).setValue(curlCommand)
}
}
}

View file

@ -59,7 +59,7 @@ PerfStat::~PerfStat() {
}
if (wantDebugOut) {
qDebug("PerfStats: %s elapsed:%f average:%lf count:%ld total:%lf ut:%d us:%d ue:%d t:%ld s:%ld e:%ld\n",
qDebug("PerfStats: %s elapsed:%f average:%lf count:%ld total:%lf ut:%ld us:%ld ue:%ld t:%ld s:%ld e:%ld\n",
this->group.c_str(),elapsed,average,count,totalTime,
(end.tv_usec-start.tv_usec),start.tv_usec,end.tv_usec,
(end.tv_sec-start.tv_sec),start.tv_sec,end.tv_sec

View file

@ -48,9 +48,9 @@ void VoxelNode::init(unsigned char * octalCode) {
_subtreeLeafNodeCount = 0; // that's me
_glBufferIndex = GLBUFFER_INDEX_UNKNOWN;
_voxelSystem = NULL;
_isDirty = true;
_shouldRender = false;
_isStagedForDeletion = false;
markWithChangedTime();
calculateAABox();
}
@ -159,28 +159,18 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
}
// handles staging or deletion of all deep children
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion) {
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex) {
VoxelNode* childToDelete = getChildAtIndex(childIndex);
if (childToDelete) {
// If the child is not a leaf, then call ourselves recursively on all the children
if (!childToDelete->isLeaf()) {
// delete all it's children
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
childToDelete->safeDeepDeleteChildAtIndex(i, stagedForDeletion);
childToDelete->safeDeepDeleteChildAtIndex(i);
}
}
// if this node has a BufferIndex then we need to stage it for deletion
// instead of actually deleting it from the tree
if (childToDelete->isKnownBufferIndex()) {
stagedForDeletion = true;
}
if (stagedForDeletion) {
childToDelete->stageForDeletion();
_isDirty = true;
} else {
deleteChildAtIndex(childIndex);
_isDirty = true;
}
deleteChildAtIndex(childIndex);
_isDirty = true;
markWithChangedTime();
}
}
@ -190,7 +180,7 @@ void VoxelNode::setColorFromAverageOfChildren() {
int colorArray[4] = {0,0,0,0};
float density = 0.0f;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (_children[i] && !_children[i]->isStagedForDeletion() && _children[i]->isColored()) {
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
}
@ -279,7 +269,7 @@ bool VoxelNode::collapseIdenticalLeaves() {
int red,green,blue;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
// if no child, child isn't a leaf, or child doesn't have a color
if (!_children[i] || _children[i]->isStagedForDeletion() || !_children[i]->isLeaf() || !_children[i]->isColored()) {
if (!_children[i] || !_children[i]->isLeaf() || !_children[i]->isColored()) {
allChildrenMatch=false;
//qDebug("SADNESS child missing or not colored! i=%d\n",i);
break;
@ -410,43 +400,23 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
return distance;
}
VoxelNodeDeleteHook VoxelNode::_hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
void* VoxelNode::_hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
int VoxelNode::_hooksInUse = 0;
std::vector<VoxelNodeDeleteHook*> VoxelNode::_hooks;
int VoxelNode::addDeleteHook(VoxelNodeDeleteHook hook, void* extraData) {
// If first use, initialize the _hooks array
if (_hooksInUse == 0) {
memset(_hooks, 0, sizeof(_hooks));
memset(_hooksExtraData, 0, sizeof(_hooksExtraData));
}
// find first available slot
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
if (!_hooks[i]) {
_hooks[i] = hook;
_hooksExtraData[i] = extraData;
_hooksInUse++;
return i;
}
}
// if we got here, then we're out of room in our hooks, return error
return VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE;
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
_hooks.push_back(hook);
}
void VoxelNode::removeDeleteHook(int hookID) {
if (_hooks[hookID]) {
_hooks[hookID] = NULL;
_hooksExtraData[hookID] = NULL;
_hooksInUse--;
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
for (int i = 0; i < _hooks.size(); i++) {
if (_hooks[i] == hook) {
_hooks.erase(_hooks.begin() + i);
return;
}
}
}
void VoxelNode::notifyDeleteHooks() {
if (_hooksInUse > 0) {
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
if (_hooks[i]) {
_hooks[i](this, _hooksExtraData[i]);
}
}
for (int i = 0; i < _hooks.size(); i++) {
_hooks[i]->nodeDeleted(this);
}
}

View file

@ -14,18 +14,19 @@
#include "ViewFrustum.h"
#include "VoxelConstants.h"
class VoxelTree; // forward delclaration
class VoxelNode; // forward delclaration
class VoxelTree; // forward declaration
class VoxelNode; // forward declaration
class VoxelSystem; // forward declaration
typedef unsigned char colorPart;
typedef unsigned char nodeColor[4];
typedef unsigned char rgbColor[3];
// Callback function, for delete hook
typedef void (*VoxelNodeDeleteHook)(VoxelNode* node, void* extraData);
const int VOXEL_NODE_MAX_DELETE_HOOKS = 100;
const int VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE = -1;
// Callers who want delete hook callbacks should implement this class
class VoxelNodeDeleteHook {
public:
virtual void nodeDeleted(VoxelNode* node) = 0;
};
class VoxelNode {
public:
@ -38,7 +39,7 @@ public:
void deleteChildAtIndex(int childIndex);
VoxelNode* removeChildAtIndex(int childIndex);
VoxelNode* addChildAtIndex(int childIndex);
void safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion); // handles staging or deletion of all descendents
void safeDeepDeleteChildAtIndex(int childIndex); // handles deletion of all descendents
void setColorFromAverageOfChildren();
void setRandomColor(int minimumBrightness);
@ -77,15 +78,14 @@ public:
glBufferIndex getBufferIndex() const { return _glBufferIndex; };
bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); };
void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; };
VoxelSystem* getVoxelSystem() const { return _voxelSystem; };
void setVoxelSystem(VoxelSystem* voxelSystem) { _voxelSystem = voxelSystem; };
// Used by VoxelSystem for rendering in/out of view and LOD
void setShouldRender(bool shouldRender);
bool getShouldRender() const { return _shouldRender; }
// Used by VoxelSystem to mark a node as to be deleted on next render pass
void stageForDeletion() { _isStagedForDeletion = true; _isDirty = true; };
bool isStagedForDeletion() const { return _isStagedForDeletion; }
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
void setFalseColor(colorPart red, colorPart green, colorPart blue);
void setFalseColored(bool isFalseColored);
@ -105,8 +105,8 @@ public:
const nodeColor& getColor() const { return _trueColor; };
#endif
static int addDeleteHook(VoxelNodeDeleteHook hook, void* extraData = NULL);
static void removeDeleteHook(int hookID);
static void addDeleteHook(VoxelNodeDeleteHook* hook);
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
void recalculateSubTreeNodeCount();
unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; };
@ -124,10 +124,10 @@ private:
bool _falseColored;
#endif
glBufferIndex _glBufferIndex;
VoxelSystem* _voxelSystem;
bool _isDirty;
uint64_t _lastChanged;
bool _shouldRender;
bool _isStagedForDeletion;
AABox _box;
unsigned char* _octalCode;
VoxelNode* _children[8];
@ -135,10 +135,8 @@ private:
unsigned long _subtreeNodeCount;
unsigned long _subtreeLeafNodeCount;
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
static VoxelNodeDeleteHook _hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
static void* _hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
static int _hooksInUse;
static std::vector<VoxelNodeDeleteHook*> _hooks;
};
#endif /* defined(__hifi__VoxelNode__) */

View file

@ -13,11 +13,11 @@ VoxelNodeBag::VoxelNodeBag() :
_bagElements(NULL),
_elementsInUse(0),
_sizeOfElementsArray(0) {
_hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this);
VoxelNode::addDeleteHook(this);
};
VoxelNodeBag::~VoxelNodeBag() {
VoxelNode::removeDeleteHook(_hookID);
VoxelNode::removeDeleteHook(this);
deleteAll();
}
@ -126,9 +126,9 @@ void VoxelNodeBag::remove(VoxelNode* node) {
}
}
void VoxelNodeBag::voxelNodeDeleteHook(VoxelNode* node, void* extraData) {
VoxelNodeBag* theBag = (VoxelNodeBag*)extraData;
theBag->remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
void VoxelNodeBag::nodeDeleted(VoxelNode* node) {
remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
}

View file

@ -16,7 +16,7 @@
#include "VoxelNode.h"
class VoxelNodeBag {
class VoxelNodeBag : public VoxelNodeDeleteHook {
public:
VoxelNodeBag();
@ -34,6 +34,8 @@ public:
static void voxelNodeDeleteHook(VoxelNode* node, void* extraData);
virtual void nodeDeleted(VoxelNode* node);
private:
VoxelNode** _bagElements;

View file

@ -378,10 +378,10 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl
void VoxelSceneStats::printDebugDetails() {
qDebug("\n------------------------------\n");
qDebug("VoxelSceneStats:\n");
qDebug(" start : %llu \n", _start);
qDebug(" end : %llu \n", _end);
qDebug(" elapsed : %llu \n", _elapsed);
qDebug(" encoding : %llu \n", _totalEncodeTime);
qDebug(" start : %llu \n", (long long unsigned int)_start);
qDebug(" end : %llu \n", (long long unsigned int)_end);
qDebug(" elapsed : %llu \n", (long long unsigned int)_elapsed);
qDebug(" encoding : %llu \n", (long long unsigned int)_totalEncodeTime);
qDebug("\n");
qDebug(" full scene: %s\n", debug::valueOf(_isFullScene));
qDebug(" moving: %s\n", debug::valueOf(_isMoving));
@ -457,12 +457,12 @@ char* VoxelSceneStats::getItemValue(int item) {
calcAverageFPS = (float)USECS_PER_SECOND / (float)elapsedAverage;
sprintf(_itemValueBuffer, "%llu usecs (%d fps) Average: %.0f usecs (%d fps)",
_elapsed, calcFPS, elapsedAverage, calcAverageFPS);
(long long unsigned int)_elapsed, calcFPS, elapsedAverage, calcAverageFPS);
break;
}
case ITEM_ENCODE:
calcFPS = (float)USECS_PER_SECOND / (float)_totalEncodeTime;
sprintf(_itemValueBuffer, "%llu usecs (%d fps)", _totalEncodeTime, calcFPS);
sprintf(_itemValueBuffer, "%llu usecs (%d fps)", (long long unsigned int)_totalEncodeTime, calcFPS);
break;
case ITEM_PACKETS: {
float elapsedSecs = ((float)_elapsed / (float)USECS_PER_SECOND);

View file

@ -311,8 +311,7 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData,
// now also check the childrenInTreeMask, if the mask is missing the bit, then it means we need to delete this child
// subtree/node, because it shouldn't actually exist in the tree.
if (!oneAtBit(childrenInTreeMask, i) && destinationNode->getChildAtIndex(i)) {
bool stagedForDeletion = false; // assume staging is not needed
destinationNode->safeDeepDeleteChildAtIndex(i, stagedForDeletion);
destinationNode->safeDeepDeleteChildAtIndex(i);
_isDirty = true; // by definition!
}
}
@ -366,15 +365,14 @@ void VoxelTree::readBitstreamToTree(unsigned char * bitstream, unsigned long int
this->voxelsBytesReadStats.updateAverage(bufferSizeBytes);
}
void VoxelTree::deleteVoxelAt(float x, float y, float z, float s, bool stage) {
void VoxelTree::deleteVoxelAt(float x, float y, float z, float s) {
unsigned char* octalCode = pointToVoxel(x,y,z,s,0,0,0);
deleteVoxelCodeFromTree(octalCode, stage);
deleteVoxelCodeFromTree(octalCode);
delete[] octalCode; // cleanup memory
}
class DeleteVoxelCodeFromTreeArgs {
public:
bool stage;
bool collapseEmptyTrees;
unsigned char* codeBuffer;
int lengthOfCode;
@ -384,11 +382,10 @@ public:
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the node from the tree.
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage, bool collapseEmptyTrees) {
void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees) {
// recurse the tree while decoding the codeBuffer, once you find the node in question, recurse
// back and implement color reaveraging, and marking of lastChanged
DeleteVoxelCodeFromTreeArgs args;
args.stage = stage;
args.collapseEmptyTrees = collapseEmptyTrees;
args.codeBuffer = codeBuffer;
args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer);
@ -408,7 +405,6 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat
// matches, then we've reached our target node.
if (lengthOfNodeCode == args->lengthOfCode) {
// we've reached our target, depending on how we're called we may be able to operate on it
// if we're in "stage" mode, then we can could have the node staged, otherwise we can't really delete
// it here, we need to recurse up, and delete it there. So we handle these cases the same to keep
// the logic consistent.
args->deleteLastChild = true;
@ -468,11 +464,7 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat
// If the lower level determined it needs to be deleted, then we should delete now.
if (args->deleteLastChild) {
if (args->stage) {
childNode->stageForDeletion();
} else {
node->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this node
}
node->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this node
// track our tree dirtiness
_isDirty = true;
@ -602,7 +594,7 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe
int codeLength = numberOfThreeBitSectionsInCode(voxelCode);
int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA;
deleteVoxelCodeFromTree(voxelCode, ACTUALLY_DELETE, COLLAPSE_EMPTY_TREE);
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE);
voxelCode+=voxelDataSize;
atByte+=voxelDataSize;
@ -1485,11 +1477,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
if (params.includeColor && !params.includeExistsBits && childTreeBytesOut == 2) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
// If we've asked for existBits, this is also true, except that the tree will output 3 bytes
// NOTE: does this introduce a problem with detecting deletion??
if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
// We used to try to collapse trees that didn't contain any data, but this does appear to create a problem
// in detecting node deletion. So, I've commented this out but left it in here as a warning to anyone else
// about not attempting to add this optimization back in, without solving the node deletion case.
// We need to send these bitMasks in case the exists in tree bitmask is indicating the deletion of a tree
//if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
// childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
//}
bytesAtThisLevel += childTreeBytesOut;
availableBytes -= childTreeBytesOut;

View file

@ -22,23 +22,23 @@
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
#define NO_EXISTS_BITS false
#define WANT_EXISTS_BITS true
#define NO_COLOR false
#define WANT_COLOR true
#define IGNORE_VIEW_FRUSTUM NULL
#define JUST_STAGE_DELETION true
#define ACTUALLY_DELETE false
#define COLLAPSE_EMPTY_TREE true
#define DONT_COLLAPSE false
#define NO_OCCLUSION_CULLING false
#define WANT_OCCLUSION_CULLING true
#define IGNORE_COVERAGE_MAP NULL
#define DONT_CHOP 0
#define NO_BOUNDARY_ADJUST 0
#define LOW_RES_MOVING_ADJUST 1
#define IGNORE_LAST_SENT 0
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
const bool NO_COLOR = false;
const bool WANT_COLOR = true;
const bool COLLAPSE_EMPTY_TREE = true;
const bool DONT_COLLAPSE = false;
const bool NO_OCCLUSION_CULLING = false;
const bool WANT_OCCLUSION_CULLING = true;
const int DONT_CHOP = 0;
const int NO_BOUNDARY_ADJUST = 0;
const int LOW_RES_MOVING_ADJUST = 1;
const uint64_t IGNORE_LAST_SENT = 0;
#define IGNORE_SCENE_STATS NULL
#define IGNORE_VIEW_FRUSTUM NULL
#define IGNORE_COVERAGE_MAP NULL
class EncodeBitstreamParams {
public:
@ -113,12 +113,11 @@ public:
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
VoxelNode* destinationNode = NULL);
void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false);
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage = ACTUALLY_DELETE,
bool collapseEmptyTrees = DONT_COLLAPSE);
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void printTreeForDebugging(VoxelNode* startNode);
void reaverageVoxelColors(VoxelNode* startNode);
void deleteVoxelAt(float x, float y, float z, float s, bool stage = false);
void deleteVoxelAt(float x, float y, float z, float s);
VoxelNode* getVoxelAt(float x, float y, float z, float s) const;
void createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);