From b2b67d2b3dac6f20dbd079606520874677220559 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Jul 2013 01:18:54 -0700 Subject: [PATCH 01/27] split multiple avatars into multiple packets if needed --- avatar-mixer/src/main.cpp | 62 ++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index f8bc36f6e6..b2e28fe484 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -36,7 +36,7 @@ const int AVATAR_LISTEN_PORT = 55444; -unsigned char *addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) { +unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) { currentPosition += packNodeId(currentPosition, nodeToAdd->getNodeID()); AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData(); @@ -51,6 +51,47 @@ void attachAvatarDataToNode(Node* newNode) { } } +unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; +unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; +void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { + unsigned char* broadcastPacket = (unsigned char*)&::broadcastPacketBuffer[0]; + int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA); + unsigned char* currentBufferPosition = NULL; + currentBufferPosition = broadcastPacket + numHeaderBytes; + int packetLength = currentBufferPosition - broadcastPacket; + int packetsSent = 0; + + // send back a packet with other active node data to this node + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) { + unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node); + int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer; + + if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) { + memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); + packetLength += avatarDataLength; + currentBufferPosition += avatarDataLength; + } else { + packetsSent++; + //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); + nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); + + // reset the packet + currentBufferPosition = broadcastPacket + numHeaderBytes; + packetLength = currentBufferPosition - broadcastPacket; + + // copy the avatar that didn't fit into the next packet + memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength); + packetLength += avatarDataLength; + currentBufferPosition += avatarDataLength; + } + } + } + packetsSent++; + //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); + nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); +} + int main(int argc, const char* argv[]) { NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT); @@ -67,14 +108,11 @@ int main(int argc, const char* argv[]) { nodeList->startSilentNodeRemovalThread(); - sockaddr *nodeAddress = new sockaddr; - unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; + sockaddr* nodeAddress = new sockaddr; ssize_t receivedBytes = 0; - unsigned char *broadcastPacket = new unsigned char[MAX_PACKET_SIZE]; - int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA); + unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; - unsigned char* currentBufferPosition = NULL; uint16_t nodeID = 0; Node* avatarNode = NULL; @@ -104,17 +142,7 @@ int main(int argc, const char* argv[]) { // parse positional data from an node nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes); case PACKET_TYPE_INJECT_AUDIO: - currentBufferPosition = broadcastPacket + numHeaderBytes; - - // send back a packet with other active node data to this node - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) { - currentBufferPosition = addNodeToBroadcastPacket(currentBufferPosition, &*node); - } - } - - nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); - + broadcastAvatarData(nodeList, nodeAddress); break; case PACKET_TYPE_AVATAR_VOXEL_URL: case PACKET_TYPE_AVATAR_FACE_VIDEO: From bacb4f96e37e68492c82c027888ff28521001166 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Jul 2013 09:59:51 -0700 Subject: [PATCH 02/27] added notes --- avatar-mixer/src/main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index b2e28fe484..8383c992b2 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -51,6 +51,13 @@ void attachAvatarDataToNode(Node* newNode) { } } +// NOTE: some additional optimizations to consider. +// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present +// if the avatar is not in view or in the keyhole. +// 2) after culling for view frustum, sort order the avatars by distance, send the closest ones first. +// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to +// determine which avatars are included in the packet stream +// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { From 212fddbd346f4ecd52c19f44e4c0f742a8e4f114 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jul 2013 12:29:17 -0700 Subject: [PATCH 03/27] CR feedback --- avatar-mixer/src/main.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index 8383c992b2..0677ffbc7e 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -58,13 +58,12 @@ void attachAvatarDataToNode(Node* newNode) { // 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to // determine which avatars are included in the packet stream // 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). -unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; -unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { - unsigned char* broadcastPacket = (unsigned char*)&::broadcastPacketBuffer[0]; + static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; + static unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; + unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0]; int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA); - unsigned char* currentBufferPosition = NULL; - currentBufferPosition = broadcastPacket + numHeaderBytes; + unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes; int packetLength = currentBufferPosition - broadcastPacket; int packetsSent = 0; From 919f2a4a939df264e11a4ef82ed7827c759683b6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jul 2013 12:56:00 -0700 Subject: [PATCH 04/27] fix voxel-server crash --- libraries/voxels/src/VoxelTree.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b26a70f34b..fe67a50dee 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1071,6 +1071,9 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag, EncodeBitstreamParams& params, int& currentEncodeLevel) const { + // you can't call this without a valid node + assert(node); + // How many bytes have we written so far at this level; int bytesAtThisLevel = 0; @@ -1228,6 +1231,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // track stats + // must check childNode here, because it could be we got here with no childNode if (params.stats && childNode) { params.stats->traversed(childNode); } @@ -1243,7 +1247,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum))); if (!childIsInView) { - if (params.stats) { + // must check childNode here, because it could be we got here because there was no childNode + if (params.stats && childNode) { params.stats->skippedOutOfView(childNode); } } else { @@ -1253,6 +1258,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust); if (!(distance < boundaryDistance)) { + // don't need to check childNode here, because we can't get here with no childNode if (params.stats) { params.stats->skippedDistance(childNode); } @@ -1306,9 +1312,11 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // track some stats if (params.stats) { + // don't need to check childNode here, because we can't get here with no childNode if (!shouldRender && childNode->isLeaf()) { params.stats->skippedDistance(childNode); } + // don't need to check childNode here, because we can't get here with no childNode if (childIsOccluded) { params.stats->skippedOccluded(childNode); } @@ -1339,6 +1347,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp inViewWithColorCount++; } else { // otherwise just track stats of the items we discarded + // don't need to check childNode here, because we can't get here with no childNode if (params.stats) { if (childWasInView) { params.stats->skippedWasInView(childNode); @@ -1368,6 +1377,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color + // don't need to check childNode here, because we can't get here with no childNode if (params.stats) { params.stats->colorSent(childNode); } @@ -1407,6 +1417,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } else { bag.insert(node); + // don't need to check node here, because we can't get here with no node if (params.stats) { params.stats->didntFit(node); } From 79648ce656a43f4ad3735d83e4d589dd463946bf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Jul 2013 15:22:23 -0700 Subject: [PATCH 05/27] fix bug with dragonfly leaving trailing bits --- libraries/voxels/src/VoxelTree.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index fe67a50dee..7bf3c66888 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1485,11 +1485,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; From 0c95dc4adf2538897a75517686a8d7b72545f95d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jul 2013 11:12:48 -0700 Subject: [PATCH 06/27] Fixed voxel trails from animations - removed stageForDeletion behavior of VoxelNode - replaced with VoxelNodeDeletionHook strategy - VoxelSystem now cleans up previously used VBO index slots via hook --- interface/src/VoxelSystem.cpp | 66 +++++++++++++++------------ interface/src/VoxelSystem.h | 7 +++ libraries/voxels/src/VoxelNode.cpp | 23 +++------- libraries/voxels/src/VoxelNode.h | 7 +-- libraries/voxels/src/VoxelNodeBag.cpp | 1 - libraries/voxels/src/VoxelTree.cpp | 20 +++----- libraries/voxels/src/VoxelTree.h | 7 +-- 7 files changed, 59 insertions(+), 72 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index f219e38dc6..0ea6d2d351 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -58,6 +58,32 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : _tree = new VoxelTree(); pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); + + _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this); +} + +void VoxelSystem::voxelNodeDeleteHook(VoxelNode* node, void* extraData) { + VoxelSystem* theSystem = (VoxelSystem*)extraData; + + if (node->isKnownBufferIndex()) { + theSystem->freeBufferIndex(node->getBufferIndex()); + } +} + +void VoxelSystem::freeBufferIndex(glBufferIndex index) { + _freeIdexes.push_back(index); +} + +void VoxelSystem::clearFreeBufferIndexes() { + for (int i = 0; i < _freeIdexes.size(); i++) { + glBufferIndex nodeIndex = _freeIdexes[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); + } + _freeIdexes.clear(); } VoxelSystem::~VoxelSystem() { @@ -70,6 +96,8 @@ VoxelSystem::~VoxelSystem() { delete _tree; pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); + + VoxelNode::removeDeleteHook(_hookID); } 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 @@ -239,6 +270,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } void VoxelSystem::cleanupRemovedVoxels() { + //printf("cleanupRemovedVoxels...\n"); PerformanceWarning warn(_renderWarningsOn, "cleanupRemovedVoxels()"); if (!_removedVoxels.isEmpty()) { while (!_removedVoxels.isEmpty()) { @@ -323,7 +355,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 +371,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; } @@ -1111,7 +1136,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 +1192,6 @@ struct FalseColorizeOccludedArgs { long nonLeaves; long nonLeavesOutOfView; long nonLeavesOccluded; - long stagedForDeletion; }; struct FalseColorizeSubTreeOperationArgs { @@ -1189,12 +1213,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 +1293,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 +1305,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 +1326,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 +1414,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 +1422,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, diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 411ae2f81f..cb12eee029 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -187,6 +187,13 @@ private: static ProgramObject* _perlinModulateProgram; static GLuint _permutationNormalTextureID; + + int _hookID; + std::vector _freeIdexes; + + static void voxelNodeDeleteHook(VoxelNode* node, void* extraData); + void freeBufferIndex(glBufferIndex index); + void clearFreeBufferIndexes(); }; #endif diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 34eb2b2261..4a0f017326 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -50,7 +50,6 @@ void VoxelNode::init(unsigned char * octalCode) { _glBufferIndex = GLBUFFER_INDEX_UNKNOWN; _isDirty = true; _shouldRender = false; - _isStagedForDeletion = false; markWithChangedTime(); calculateAABox(); } @@ -159,28 +158,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 +179,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 +268,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; diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index c84ce3c7d6..1efab362de 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -38,7 +38,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); @@ -82,10 +82,6 @@ public: 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); @@ -127,7 +123,6 @@ private: bool _isDirty; uint64_t _lastChanged; bool _shouldRender; - bool _isStagedForDeletion; AABox _box; unsigned char* _octalCode; VoxelNode* _children[8]; diff --git a/libraries/voxels/src/VoxelNodeBag.cpp b/libraries/voxels/src/VoxelNodeBag.cpp index 4f355116e4..bc515c9e5a 100644 --- a/libraries/voxels/src/VoxelNodeBag.cpp +++ b/libraries/voxels/src/VoxelNodeBag.cpp @@ -125,7 +125,6 @@ void VoxelNodeBag::remove(VoxelNode* node) { _elementsInUse--; } } - 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() diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 7bf3c66888..87def2cc23 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -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; diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 199942605e..5cda791531 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -27,8 +27,6 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; #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 @@ -113,12 +111,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); From 4cca7e25724c2293b797a0819ceefb32e9c9851e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Jul 2013 11:44:19 -0700 Subject: [PATCH 07/27] Fixed issue with freeing up abandoned VBO slots when not moving with animation --- interface/src/VoxelSystem.cpp | 16 ++++++++++++++-- interface/src/VoxelSystem.h | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 0ea6d2d351..8282b3d8e0 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -60,6 +60,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : pthread_mutex_init(&_treeLock, NULL); _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this); + _abandonedVBOSlots = 0; } void VoxelSystem::voxelNodeDeleteHook(VoxelNode* node, void* extraData) { @@ -82,6 +83,7 @@ void VoxelSystem::clearFreeBufferIndexes() { _writeVoxelDirtyArray[nodeIndex] = true; nodeColor color = { 0, 0, 0, 0}; updateNodeInArrays(nodeIndex, startVertex, voxelScale, color); + _abandonedVBOSlots++; } _freeIdexes.clear(); } @@ -243,6 +245,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() @@ -270,14 +276,20 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } void VoxelSystem::cleanupRemovedVoxels() { - //printf("cleanupRemovedVoxels...\n"); 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() { @@ -418,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. @@ -470,7 +483,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]; diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index cb12eee029..4a6d30c78b 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -155,7 +155,7 @@ private: unsigned long _voxelsUpdated; unsigned long _voxelsInReadArrays; unsigned long _voxelsInWriteArrays; - unsigned long _unusedArraySpace; + unsigned long _abandonedVBOSlots; bool _writeRenderFullVBO; bool _readRenderFullVBO; From dd2655825a88aaa122c75ff696c1ffdca83aefc6 Mon Sep 17 00:00:00 2001 From: Mark Peng Date: Fri, 26 Jul 2013 16:08:17 -0700 Subject: [PATCH 08/27] Fix first person camera positioning to correspond to eyelevel. This makes sure the lookatVectors don't seem to point upwards when looking at you. --- interface/src/Application.cpp | 2 +- interface/src/avatar/Avatar.cpp | 6 +++++- interface/src/avatar/Avatar.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c56a58ce90..8135e656ed 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -407,7 +407,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) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index c2a2d5aadd..1d6d0ff57c 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -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) { // diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index ec0d5a2ce5..fc1d7e204d 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -171,6 +171,7 @@ public: glm::vec3 getGravity () const { return _gravity; } glm::vec3 getUprightHeadPosition() const; + glm::vec3 getUprightEyeLevelPosition() const; AvatarVoxelSystem* getVoxels() { return &_voxels; } From 5812747ef8d7bf55091777fa8eebb274f67e95a2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 10:06:03 -0700 Subject: [PATCH 09/27] Double the bitrate to see if we can reduce the blockiness. --- interface/src/Webcam.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index e37ca6c70e..814081301b 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -500,8 +500,9 @@ void FrameGrabber::grabFrame() { // initialize encoder context vpx_codec_enc_cfg_t codecConfig; vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0); - codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * combinedFaceHeight * codecConfig.rc_target_bitrate / - codecConfig.g_w / codecConfig.g_h; + const int QUALITY_MULTIPLIER = 2; + codecConfig.rc_target_bitrate = QUALITY_MULTIPLIER * ENCODED_FACE_WIDTH * combinedFaceHeight * + codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; codecConfig.g_w = ENCODED_FACE_WIDTH; codecConfig.g_h = combinedFaceHeight; vpx_codec_enc_init(&_codec, vpx_codec_vp8_cx(), &codecConfig, 0); From 20360ad3e906aaf1e8bcf9092f351f0ade04913e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 10:41:45 -0700 Subject: [PATCH 10/27] Make particle system update/render toggleable, fix GCC compile warnings. --- interface/src/Application.cpp | 14 +++++++------- interface/src/Application.h | 1 + libraries/shared/src/PerfStat.cpp | 2 +- libraries/voxels/src/VoxelSceneStats.cpp | 12 ++++++------ 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c56a58ce90..825b9801d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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 @@ -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); } } @@ -2716,7 +2716,7 @@ void Application::displaySide(Camera& whichCamera) { } } - if (TESTING_PARTICLE_SYSTEM) { + if (_renderParticleSystemOn->isChecked()) { if (_particleSystemInitialized) { _particleSystem.render(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index bee9bb8b20..80ebd34250 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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 diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index 86985eb038..70bcaba009 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -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 diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp index b226886a25..9dd696eaa6 100644 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ b/libraries/voxels/src/VoxelSceneStats.cpp @@ -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); From d01f694d4914848e54e9fda8ef793c68af74dcae Mon Sep 17 00:00:00 2001 From: Mark Peng Date: Mon, 29 Jul 2013 11:52:29 -0700 Subject: [PATCH 11/27] Remove extra semicolon in avatar.cpp --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1d6d0ff57c..0bf2c94b10 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -373,7 +373,7 @@ glm::vec3 Avatar::getUprightHeadPosition() const { 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);; + 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) { From bfd7f3a6c9738e087afa554415a05f653569cf80 Mon Sep 17 00:00:00 2001 From: Mark Peng Date: Mon, 29 Jul 2013 12:25:01 -0700 Subject: [PATCH 12/27] Remove rendering of your own avatar's lookatVectors. This fixes the behavior where if you are moving and jumping, you will occasionally see your own lookatVectors on your screen. --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c8be70b0e4..f331ee9407 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2709,7 +2709,6 @@ 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); From d6428a81459f1875e0385f6ffc80a4749b08b095 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 29 Jul 2013 13:03:42 -0700 Subject: [PATCH 13/27] removed unneeded cast --- interface/src/VoxelSystem.cpp | 2 +- libraries/voxels/src/VoxelNodeBag.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 8282b3d8e0..c1da906c3e 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -59,7 +59,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); - _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this); + _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, this); _abandonedVBOSlots = 0; } diff --git a/libraries/voxels/src/VoxelNodeBag.cpp b/libraries/voxels/src/VoxelNodeBag.cpp index bc515c9e5a..713f4de1dd 100644 --- a/libraries/voxels/src/VoxelNodeBag.cpp +++ b/libraries/voxels/src/VoxelNodeBag.cpp @@ -13,7 +13,7 @@ VoxelNodeBag::VoxelNodeBag() : _bagElements(NULL), _elementsInUse(0), _sizeOfElementsArray(0) { - _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this); + _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, this); }; VoxelNodeBag::~VoxelNodeBag() { From 97aaa738c357a712f40b54578a84bb4653fd4a0a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 29 Jul 2013 13:07:12 -0700 Subject: [PATCH 14/27] fixed typo --- interface/src/VoxelSystem.cpp | 8 ++++---- interface/src/VoxelSystem.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index c1da906c3e..740defd110 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -72,12 +72,12 @@ void VoxelSystem::voxelNodeDeleteHook(VoxelNode* node, void* extraData) { } void VoxelSystem::freeBufferIndex(glBufferIndex index) { - _freeIdexes.push_back(index); + _freeIndexes.push_back(index); } void VoxelSystem::clearFreeBufferIndexes() { - for (int i = 0; i < _freeIdexes.size(); i++) { - glBufferIndex nodeIndex = _freeIdexes[i]; + 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; @@ -85,7 +85,7 @@ void VoxelSystem::clearFreeBufferIndexes() { updateNodeInArrays(nodeIndex, startVertex, voxelScale, color); _abandonedVBOSlots++; } - _freeIdexes.clear(); + _freeIndexes.clear(); } VoxelSystem::~VoxelSystem() { diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 4a6d30c78b..1e47cda52a 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -189,7 +189,7 @@ private: static GLuint _permutationNormalTextureID; int _hookID; - std::vector _freeIdexes; + std::vector _freeIndexes; static void voxelNodeDeleteHook(VoxelNode* node, void* extraData); void freeBufferIndex(glBufferIndex index); From 03a75ab295a35010c553d3be4d711d79cfa3fa1a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 29 Jul 2013 13:08:05 -0700 Subject: [PATCH 15/27] fixed spacing --- interface/src/VoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 740defd110..af3f7ce5c2 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -81,7 +81,7 @@ void VoxelSystem::clearFreeBufferIndexes() { glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX); float voxelScale = 0; _writeVoxelDirtyArray[nodeIndex] = true; - nodeColor color = { 0, 0, 0, 0}; + nodeColor color = {0, 0, 0, 0}; updateNodeInArrays(nodeIndex, startVertex, voxelScale, color); _abandonedVBOSlots++; } From e20f163e0a6015dad18251d263c38a436994dea0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 29 Jul 2013 13:34:58 -0700 Subject: [PATCH 16/27] changed some defines to consts --- libraries/voxels/src/VoxelTree.h | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 5cda791531..88394bd825 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -22,21 +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 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: From 9662b956c7027f8aa4a606fc3636c71bdd54413a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 29 Jul 2013 16:00:55 -0600 Subject: [PATCH 17/27] Fixing cmake path on seed's groovy --- jenkins/jobs.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jenkins/jobs.groovy b/jenkins/jobs.groovy index 114e4389ed..5acf313a9a 100644 --- a/jenkins/jobs.groovy +++ b/jenkins/jobs.groovy @@ -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) } -} \ No newline at end of file +} From 1a73485e36400c76c446115b3a871bbb6d742533 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 29 Jul 2013 15:30:30 -0700 Subject: [PATCH 18/27] Improved VoxelNode delete callback based on CR feedback - changed hooks to use a virtual base class approach - switched account of hooks to use a vector instead of home grown solution - added support for VoxelNode to know what VoxelSystem it belongs to --- interface/src/VoxelSystem.cpp | 15 +++++----- interface/src/VoxelSystem.h | 5 ++-- libraries/voxels/src/VoxelNode.cpp | 43 ++++++++------------------- libraries/voxels/src/VoxelNode.h | 29 ++++++++++-------- libraries/voxels/src/VoxelNodeBag.cpp | 11 +++---- libraries/voxels/src/VoxelNodeBag.h | 4 ++- 6 files changed, 48 insertions(+), 59 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index af3f7ce5c2..2d1bfb577c 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -59,15 +59,13 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); - _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, this); + VoxelNode::addDeleteHook(this); _abandonedVBOSlots = 0; } -void VoxelSystem::voxelNodeDeleteHook(VoxelNode* node, void* extraData) { - VoxelSystem* theSystem = (VoxelSystem*)extraData; - - if (node->isKnownBufferIndex()) { - theSystem->freeBufferIndex(node->getBufferIndex()); +void VoxelSystem::nodeDeleted(VoxelNode* node) { + if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) { + freeBufferIndex(node->getBufferIndex()); } } @@ -99,7 +97,7 @@ VoxelSystem::~VoxelSystem() { pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); - VoxelNode::removeDeleteHook(_hookID); + VoxelNode::removeDeleteHook(this); } void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { @@ -401,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 @@ -440,6 +440,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { } else { nodeIndex = _voxelsInWriteArrays; node->setBufferIndex(nodeIndex); + node->setVoxelSystem(this); _voxelsInWriteArrays++; } _writeVoxelDirtyArray[nodeIndex] = true; diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 1e47cda52a..ca390a5a20 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -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; @@ -191,7 +193,6 @@ private: int _hookID; std::vector _freeIndexes; - static void voxelNodeDeleteHook(VoxelNode* node, void* extraData); void freeBufferIndex(glBufferIndex index); void clearFreeBufferIndexes(); }; diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 4a0f017326..8a76d9dfe8 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -48,6 +48,7 @@ void VoxelNode::init(unsigned char * octalCode) { _subtreeLeafNodeCount = 0; // that's me _glBufferIndex = GLBUFFER_INDEX_UNKNOWN; + _voxelSystem = NULL; _isDirty = true; _shouldRender = false; markWithChangedTime(); @@ -399,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 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); } } diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 1efab362de..38e9627848 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -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: @@ -77,6 +78,9 @@ 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); @@ -101,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; }; @@ -120,6 +124,7 @@ private: bool _falseColored; #endif glBufferIndex _glBufferIndex; + VoxelSystem* _voxelSystem; bool _isDirty; uint64_t _lastChanged; bool _shouldRender; @@ -130,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 _hooks; }; #endif /* defined(__hifi__VoxelNode__) */ diff --git a/libraries/voxels/src/VoxelNodeBag.cpp b/libraries/voxels/src/VoxelNodeBag.cpp index 713f4de1dd..6b69edd32c 100644 --- a/libraries/voxels/src/VoxelNodeBag.cpp +++ b/libraries/voxels/src/VoxelNodeBag.cpp @@ -13,11 +13,11 @@ VoxelNodeBag::VoxelNodeBag() : _bagElements(NULL), _elementsInUse(0), _sizeOfElementsArray(0) { - _hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, this); + VoxelNode::addDeleteHook(this); }; VoxelNodeBag::~VoxelNodeBag() { - VoxelNode::removeDeleteHook(_hookID); + VoxelNode::removeDeleteHook(this); deleteAll(); } @@ -125,9 +125,10 @@ void VoxelNodeBag::remove(VoxelNode* node) { _elementsInUse--; } } -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() } diff --git a/libraries/voxels/src/VoxelNodeBag.h b/libraries/voxels/src/VoxelNodeBag.h index 27bd4e5b60..a29e7678c9 100644 --- a/libraries/voxels/src/VoxelNodeBag.h +++ b/libraries/voxels/src/VoxelNodeBag.h @@ -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; From de52342a142cb90e74d786f9deef04b51d6d4094 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 16:04:15 -0700 Subject: [PATCH 19/27] Working on depth data processing. --- interface/src/Webcam.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 814081301b..8d90cd1255 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -596,10 +596,14 @@ void FrameGrabber::grabFrame() { _faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_8UC1); warpAffine(_grayDepthFrame, _faceDepth, transform, _faceDepth.size()); - uchar* dest = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; + uchar* dline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; + uchar* src = _faceDepth.ptr(); for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { - memcpy(dest, _faceDepth.ptr(i), ENCODED_FACE_WIDTH); - dest += vpxImage.stride[0]; + uchar* dest = dline; + for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { + *dest++ = *src++; + } + dline += vpxImage.stride[0]; } } From 4ae4623315784053a5e6dbffb29365e10891b852 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 16:12:11 -0700 Subject: [PATCH 20/27] These casts should fix the warnings on OS X. --- libraries/shared/src/PerfStat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index 70bcaba009..dde460dbb0 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -61,8 +61,8 @@ PerfStat::~PerfStat() { if (wantDebugOut) { 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 + (long)(end.tv_usec-start.tv_usec), (long)start.tv_usec, (long)end.tv_usec, + (long)(end.tv_sec-start.tv_sec), (long)start.tv_sec, (long)end.tv_sec ); } }; From 9c0e29b5e64deeaca53a361de06a1b10c18745e3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 18:16:10 -0700 Subject: [PATCH 21/27] Use only valid values to compute mean, and use the post-warp data to avoid breaking on head tilt (because we were using the bounds of the rotated rect, which included too much background). --- interface/src/Webcam.cpp | 62 ++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 8d90cd1255..a28bf87d10 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -480,19 +480,6 @@ void FrameGrabber::grabFrame() { _searchWindow = Rect(clip(faceBounds.tl(), imageBounds), clip(faceBounds.br(), imageBounds)); } -#ifdef HAVE_OPENNI - if (_depthGenerator.IsValid()) { - // convert from 11 to 8 bits, centered about the mean face depth (if possible) - if (_searchWindow.area() > 0) { - const double DEPTH_OFFSET_SMOOTHING = 0.95; - const double EIGHT_BIT_MIDPOINT = 128.0; - double meanOffset = EIGHT_BIT_MIDPOINT - mean(depth(_searchWindow))[0]; - _depthOffset = (_depthOffset == 0.0) ? meanOffset : glm::mix(meanOffset, _depthOffset, DEPTH_OFFSET_SMOOTHING); - } - depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, _depthOffset); - } -#endif - const int ENCODED_FACE_WIDTH = 128; const int ENCODED_FACE_HEIGHT = 128; int combinedFaceHeight = ENCODED_FACE_HEIGHT * (depth.empty() ? 1 : 2); @@ -591,19 +578,52 @@ void FrameGrabber::grabFrame() { uline += vpxImage.stride[2]; } - // if we have depth data, warp that and just copy it in if (!depth.empty()) { - _faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_8UC1); - warpAffine(_grayDepthFrame, _faceDepth, transform, _faceDepth.size()); + // warp the face depth without interpolation (because it will contain invalid zero values) + _faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_16UC1); + warpAffine(depth, _faceDepth, transform, _faceDepth.size(), INTER_NEAREST); - uchar* dline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; - uchar* src = _faceDepth.ptr(); + // find the mean of the valid values + qint64 depthTotal = 0; + qint64 depthSamples = 0; + ushort* src = _faceDepth.ptr(); for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { - uchar* dest = dline; for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { - *dest++ = *src++; + ushort depth = *src++; + if (depth != 0) { + depthTotal += depth; + depthSamples++; + } } - dline += vpxImage.stride[0]; + } + double mean = (depthSamples == 0) ? 0.0 : depthTotal / (double)depthSamples; + + // update the depth offset based on the mean + const double DEPTH_OFFSET_SMOOTHING = 0.95; + const double EIGHT_BIT_MIDPOINT = 128.0; + double meanOffset = EIGHT_BIT_MIDPOINT - mean; + _depthOffset = (_depthOffset == 0.0) ? meanOffset : glm::mix(meanOffset, _depthOffset, DEPTH_OFFSET_SMOOTHING); + + // convert from 11 to 8 bits for preview/local display + depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, _depthOffset); + + // likewise for the encoded representation + uchar* yline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; + src = _faceDepth.ptr(); + const char UNKNOWN_DEPTH = 0; + const char MAXIMUM_DEPTH = 255; + for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { + uchar* ydest = yline; + for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { + ushort depth = *src++; + if (depth == UNKNOWN_DEPTH) { + *ydest++ = MAXIMUM_DEPTH; + + } else { + *ydest++ = saturate_cast(depth + _depthOffset); + } + } + yline += vpxImage.stride[0]; } } From 3b4a4daec37cc213a81cfa38c50434808e5b186c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 29 Jul 2013 18:26:51 -0700 Subject: [PATCH 22/27] Ignore maximum values when computing the depth. --- interface/src/Webcam.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index a28bf87d10..2efb12aed2 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -587,10 +587,12 @@ void FrameGrabber::grabFrame() { qint64 depthTotal = 0; qint64 depthSamples = 0; ushort* src = _faceDepth.ptr(); + const ushort ELEVEN_BIT_MINIMUM = 0; + const ushort ELEVEN_BIT_MAXIMUM = 2047; for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { ushort depth = *src++; - if (depth != 0) { + if (depth != ELEVEN_BIT_MINIMUM && depth != ELEVEN_BIT_MAXIMUM) { depthTotal += depth; depthSamples++; } @@ -610,14 +612,13 @@ void FrameGrabber::grabFrame() { // likewise for the encoded representation uchar* yline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; src = _faceDepth.ptr(); - const char UNKNOWN_DEPTH = 0; - const char MAXIMUM_DEPTH = 255; + const char EIGHT_BIT_MAXIMUM = 255; for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { uchar* ydest = yline; for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { ushort depth = *src++; - if (depth == UNKNOWN_DEPTH) { - *ydest++ = MAXIMUM_DEPTH; + if (depth == ELEVEN_BIT_MINIMUM) { + *ydest++ = EIGHT_BIT_MAXIMUM; } else { *ydest++ = saturate_cast(depth + _depthOffset); From 3c437076a7e5d30c60b9c01c5337fee164e3a387 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Jul 2013 11:07:33 -0700 Subject: [PATCH 23/27] When we have a mean face depth, use that rather than the face size to estimate the z coordinate. --- interface/src/Webcam.cpp | 47 +++++++++++++++++++++++++--------------- interface/src/Webcam.h | 5 +++-- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 2efb12aed2..84654b8f96 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -57,8 +57,11 @@ void Webcam::setEnabled(bool enabled) { } } +const float UNINITIALIZED_FACE_DEPTH = 0.0f; + void Webcam::reset() { _initialFaceRect = RotatedRect(); + _initialFaceDepth = UNINITIALIZED_FACE_DEPTH; if (_enabled) { // send a message to the grabber @@ -149,7 +152,8 @@ Webcam::~Webcam() { delete _grabber; } -void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const RotatedRect& faceRect, const JointVector& joints) { +void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float meanFaceDepth, + const RotatedRect& faceRect, const JointVector& joints) { IplImage colorImage = color; glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3); if (_colorTextureID == 0) { @@ -232,22 +236,28 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const Rota const float ROTATION_SMOOTHING = 0.95f; _estimatedRotation.z = glm::mix(_faceRect.angle, _estimatedRotation.z, ROTATION_SMOOTHING); - // determine position based on translation and scaling of the face rect + // determine position based on translation and scaling of the face rect/mean face depth if (_initialFaceRect.size.area() == 0) { _initialFaceRect = _faceRect; _estimatedPosition = glm::vec3(); + _initialFaceDepth = meanFaceDepth; } else { - float proportion = sqrtf(_initialFaceRect.size.area() / (float)_faceRect.size.area()); - const float DISTANCE_TO_CAMERA = 0.333f; + float proportion, z; + if (meanFaceDepth == UNINITIALIZED_FACE_DEPTH) { + proportion = sqrtf(_initialFaceRect.size.area() / (float)_faceRect.size.area()); + const float INITIAL_DISTANCE_TO_CAMERA = 0.333f; + z = INITIAL_DISTANCE_TO_CAMERA * proportion - INITIAL_DISTANCE_TO_CAMERA; + + } else { + z = (meanFaceDepth - _initialFaceDepth) / 1000.0f; + proportion = meanFaceDepth / _initialFaceDepth; + } const float POSITION_SCALE = 0.5f; - float z = DISTANCE_TO_CAMERA * proportion - DISTANCE_TO_CAMERA; - glm::vec3 position = glm::vec3( + _estimatedPosition = glm::vec3( (_faceRect.center.x - _initialFaceRect.center.x) * proportion * POSITION_SCALE / _textureSize.width, (_faceRect.center.y - _initialFaceRect.center.y) * proportion * POSITION_SCALE / _textureSize.width, z); - const float POSITION_SMOOTHING = 0.95f; - _estimatedPosition = glm::mix(position, _estimatedPosition, POSITION_SMOOTHING); } } @@ -259,7 +269,7 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const Rota } FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0), - _depthOffset(0.0), _codec(), _frameCount(0) { + _smoothedMeanFaceDepth(UNINITIALIZED_FACE_DEPTH), _codec(), _frameCount(0) { } FrameGrabber::~FrameGrabber() { @@ -598,16 +608,17 @@ void FrameGrabber::grabFrame() { } } } - double mean = (depthSamples == 0) ? 0.0 : depthTotal / (double)depthSamples; + float mean = (depthSamples == 0) ? UNINITIALIZED_FACE_DEPTH : depthTotal / (float)depthSamples; - // update the depth offset based on the mean - const double DEPTH_OFFSET_SMOOTHING = 0.95; - const double EIGHT_BIT_MIDPOINT = 128.0; - double meanOffset = EIGHT_BIT_MIDPOINT - mean; - _depthOffset = (_depthOffset == 0.0) ? meanOffset : glm::mix(meanOffset, _depthOffset, DEPTH_OFFSET_SMOOTHING); + // smooth the mean over time + const float DEPTH_OFFSET_SMOOTHING = 0.95f; + _smoothedMeanFaceDepth = (_smoothedMeanFaceDepth == UNINITIALIZED_FACE_DEPTH) ? mean : + glm::mix(mean, _smoothedMeanFaceDepth, DEPTH_OFFSET_SMOOTHING); // convert from 11 to 8 bits for preview/local display - depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, _depthOffset); + const double EIGHT_BIT_MIDPOINT = 128.0; + double depthOffset = EIGHT_BIT_MIDPOINT - _smoothedMeanFaceDepth; + depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset); // likewise for the encoded representation uchar* yline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; @@ -621,7 +632,7 @@ void FrameGrabber::grabFrame() { *ydest++ = EIGHT_BIT_MAXIMUM; } else { - *ydest++ = saturate_cast(depth + _depthOffset); + *ydest++ = saturate_cast(depth + depthOffset); } } yline += vpxImage.stride[0]; @@ -646,7 +657,7 @@ void FrameGrabber::grabFrame() { } QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", - Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), + Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(float, _smoothedMeanFaceDepth), Q_ARG(cv::RotatedRect, _smoothedFaceRect), Q_ARG(JointVector, joints)); } diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 260eda0897..16cf3a33a1 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -62,7 +62,7 @@ public: public slots: void setEnabled(bool enabled); - void setFrame(const cv::Mat& color, int format, const cv::Mat& depth, + void setFrame(const cv::Mat& color, int format, const cv::Mat& depth, float meanFaceDepth, const cv::RotatedRect& faceRect, const JointVector& joints); private: @@ -77,6 +77,7 @@ private: cv::Size2f _textureSize; cv::RotatedRect _faceRect; cv::RotatedRect _initialFaceRect; + float _initialFaceDepth; JointVector _joints; uint64_t _startTimestamp; @@ -117,7 +118,7 @@ private: cv::Mat _backProject; cv::Rect _searchWindow; cv::Mat _grayDepthFrame; - double _depthOffset; + float _smoothedMeanFaceDepth; vpx_codec_ctx_t _codec; int _frameCount; From f8ba1c4be12599a42553ab8d2dc7a99e9db57bce Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Jul 2013 13:42:29 -0700 Subject: [PATCH 24/27] Use the V channel to provide masking information, indicating invalid depths. --- interface/src/Webcam.cpp | 43 ++++++++++++++++++++++------------- interface/src/avatar/Face.cpp | 29 +++++++++++++++++++---- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 84654b8f96..b73537f6ae 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -569,9 +569,9 @@ void FrameGrabber::grabFrame() { ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8; ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest[ENCODED_FACE_WIDTH] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] * + ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] * Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest[ENCODED_FACE_WIDTH + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] * + ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] * Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8; ydest += 2; @@ -616,26 +616,37 @@ void FrameGrabber::grabFrame() { glm::mix(mean, _smoothedMeanFaceDepth, DEPTH_OFFSET_SMOOTHING); // convert from 11 to 8 bits for preview/local display - const double EIGHT_BIT_MIDPOINT = 128.0; + const uchar EIGHT_BIT_MIDPOINT = 128; double depthOffset = EIGHT_BIT_MIDPOINT - _smoothedMeanFaceDepth; depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset); // likewise for the encoded representation - uchar* yline = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; - src = _faceDepth.ptr(); - const char EIGHT_BIT_MAXIMUM = 255; - for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) { + uchar* yline = vpxImage.planes[0] + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; + uchar* vline = vpxImage.planes[1] + vpxImage.stride[1] * (ENCODED_FACE_HEIGHT / 2); + const uchar EIGHT_BIT_MAXIMUM = 255; + for (int i = 0; i < ENCODED_FACE_HEIGHT; i += 2) { uchar* ydest = yline; - for (int j = 0; j < ENCODED_FACE_WIDTH; j++) { - ushort depth = *src++; - if (depth == ELEVEN_BIT_MINIMUM) { - *ydest++ = EIGHT_BIT_MAXIMUM; - - } else { - *ydest++ = saturate_cast(depth + depthOffset); - } + uchar* vdest = vline; + for (int j = 0; j < ENCODED_FACE_WIDTH; j += 2) { + ushort tl = *_faceDepth.ptr(i, j); + ushort tr = *_faceDepth.ptr(i, j + 1); + ushort bl = *_faceDepth.ptr(i + 1, j); + ushort br = *_faceDepth.ptr(i + 1, j + 1); + + uchar mask = EIGHT_BIT_MIDPOINT; + + ydest[0] = (tl == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(tl + depthOffset); + ydest[1] = (tr == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(tr + depthOffset); + ydest[vpxImage.stride[0]] = (bl == ELEVEN_BIT_MINIMUM) ? + (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(bl + depthOffset); + ydest[vpxImage.stride[0] + 1] = (br == ELEVEN_BIT_MINIMUM) ? + (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(br + depthOffset); + ydest += 2; + + *vdest++ = mask; } - yline += vpxImage.stride[0]; + yline += vpxImage.stride[0] * 2; + vline += vpxImage.stride[1]; } } diff --git a/interface/src/avatar/Face.cpp b/interface/src/avatar/Face.cpp index d8a8d6c2a6..5d6c4984bd 100644 --- a/interface/src/avatar/Face.cpp +++ b/interface/src/avatar/Face.cpp @@ -147,10 +147,31 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { if (image->d_h > imageHeight) { // if the height is greater than the width, we have depth data depth.create(imageHeight, image->d_w, CV_8UC1); - uchar* src = image->planes[0] + image->stride[0] * imageHeight; - for (int i = 0; i < imageHeight; i++) { - memcpy(depth.ptr(i), src, image->d_w); - src += image->stride[0]; + uchar* yline = image->planes[0] + image->stride[0] * imageHeight; + uchar* vline = image->planes[1] + image->stride[1] * (imageHeight / 2); + const uchar EIGHT_BIT_MAXIMUM = 255; + const uchar MASK_THRESHOLD = 192; + for (int i = 0; i < imageHeight; i += 2) { + uchar* ysrc = yline; + uchar* vsrc = vline; + for (int j = 0; j < image->d_w; j += 2) { + + if (*vsrc++ >= MASK_THRESHOLD) { + *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM; + + } else { + *depth.ptr(i, j) = ysrc[0]; + *depth.ptr(i, j + 1) = ysrc[1]; + *depth.ptr(i + 1, j) = ysrc[image->stride[0]]; + *depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1]; + } + ysrc += 2; + } + yline += image->stride[0] * 2; + vline += image->stride[1]; } } QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color), From c787781efbc572442946d4f95da6ce375d9f63dc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Jul 2013 15:11:32 -0700 Subject: [PATCH 25/27] Send color and depth as separate streams (rather than one on top of the other) so that we can control their bitrates separately. --- interface/src/Webcam.cpp | 88 +++++++++++++++--------- interface/src/Webcam.h | 3 +- interface/src/avatar/Face.cpp | 93 +++++++++++++++----------- interface/src/avatar/Face.h | 3 +- libraries/shared/src/PacketHeaders.cpp | 7 +- 5 files changed, 120 insertions(+), 74 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index b73537f6ae..2a58d51dae 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -269,7 +269,7 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean } FrameGrabber::FrameGrabber() : _initialized(false), _capture(0), _searchWindow(0, 0, 0, 0), - _smoothedMeanFaceDepth(UNINITIALIZED_FACE_DEPTH), _codec(), _frameCount(0) { + _smoothedMeanFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) { } FrameGrabber::~FrameGrabber() { @@ -377,9 +377,13 @@ void FrameGrabber::shutdown() { cvReleaseCapture(&_capture); _capture = 0; } - if (_codec.name != 0) { - vpx_codec_destroy(&_codec); - _codec.name = 0; + if (_colorCodec.name != 0) { + vpx_codec_destroy(&_colorCodec); + _colorCodec.name = 0; + } + if (_depthCodec.name != 0) { + vpx_codec_destroy(&_depthCodec); + _depthCodec.name = 0; } _initialized = false; @@ -492,17 +496,19 @@ void FrameGrabber::grabFrame() { const int ENCODED_FACE_WIDTH = 128; const int ENCODED_FACE_HEIGHT = 128; - int combinedFaceHeight = ENCODED_FACE_HEIGHT * (depth.empty() ? 1 : 2); - if (_codec.name == 0) { - // initialize encoder context + if (_colorCodec.name == 0) { + // initialize encoder context(s) vpx_codec_enc_cfg_t codecConfig; vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0); - const int QUALITY_MULTIPLIER = 2; - codecConfig.rc_target_bitrate = QUALITY_MULTIPLIER * ENCODED_FACE_WIDTH * combinedFaceHeight * + codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; codecConfig.g_w = ENCODED_FACE_WIDTH; - codecConfig.g_h = combinedFaceHeight; - vpx_codec_enc_init(&_codec, vpx_codec_vp8_cx(), &codecConfig, 0); + codecConfig.g_h = ENCODED_FACE_HEIGHT; + vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0); + + if (!depth.empty()) { + vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0); + } } // correct for 180 degree rotations @@ -539,9 +545,9 @@ void FrameGrabber::grabFrame() { const int ENCODED_BITS_PER_VU = 2; const int ENCODED_BITS_PER_PIXEL = ENCODED_BITS_PER_Y + 2 * ENCODED_BITS_PER_VU; const int BITS_PER_BYTE = 8; - _encodedFace.fill(128, ENCODED_FACE_WIDTH * combinedFaceHeight * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE); + _encodedFace.resize(ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE); vpx_image_t vpxImage; - vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, ENCODED_FACE_WIDTH, combinedFaceHeight, 1, (unsigned char*)_encodedFace.data()); + vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, 1, (unsigned char*)_encodedFace.data()); uchar* yline = vpxImage.planes[0]; uchar* vline = vpxImage.planes[1]; uchar* uline = vpxImage.planes[2]; @@ -588,6 +594,24 @@ void FrameGrabber::grabFrame() { uline += vpxImage.stride[2]; } + // encode the frame + vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME); + + // start the payload off with the aspect ratio + QByteArray payload(sizeof(float), 0); + *(float*)payload.data() = _smoothedFaceRect.size.width / _smoothedFaceRect.size.height; + + // extract the encoded frame + vpx_codec_iter_t iterator = 0; + const vpx_codec_cx_pkt_t* packet; + while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) { + if (packet->kind == VPX_CODEC_CX_FRAME_PKT) { + // prepend the length, which will indicate whether there's a depth frame too + payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz)); + payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz); + } + } + if (!depth.empty()) { // warp the face depth without interpolation (because it will contain invalid zero values) _faceDepth.create(ENCODED_FACE_WIDTH, ENCODED_FACE_HEIGHT, CV_16UC1); @@ -621,12 +645,14 @@ void FrameGrabber::grabFrame() { depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset); // likewise for the encoded representation - uchar* yline = vpxImage.planes[0] + vpxImage.stride[0] * ENCODED_FACE_HEIGHT; - uchar* vline = vpxImage.planes[1] + vpxImage.stride[1] * (ENCODED_FACE_HEIGHT / 2); + uchar* yline = vpxImage.planes[0]; + uchar* vline = vpxImage.planes[1]; + uchar* uline = vpxImage.planes[2]; const uchar EIGHT_BIT_MAXIMUM = 255; for (int i = 0; i < ENCODED_FACE_HEIGHT; i += 2) { uchar* ydest = yline; uchar* vdest = vline; + uchar* udest = uline; for (int j = 0; j < ENCODED_FACE_WIDTH; j += 2) { ushort tl = *_faceDepth.ptr(i, j); ushort tr = *_faceDepth.ptr(i, j + 1); @@ -644,28 +670,28 @@ void FrameGrabber::grabFrame() { ydest += 2; *vdest++ = mask; + *udest++ = EIGHT_BIT_MIDPOINT; } yline += vpxImage.stride[0] * 2; vline += vpxImage.stride[1]; + uline += vpxImage.stride[2]; + } + + // encode the frame + vpx_codec_encode(&_depthCodec, &vpxImage, _frameCount, 1, 0, VPX_DL_REALTIME); + + // extract the encoded frame + vpx_codec_iter_t iterator = 0; + const vpx_codec_cx_pkt_t* packet; + while ((packet = vpx_codec_get_cx_data(&_depthCodec, &iterator)) != 0) { + if (packet->kind == VPX_CODEC_CX_FRAME_PKT) { + payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz); + } } } - // encode the frame - vpx_codec_encode(&_codec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME); - - // extract the encoded frame - vpx_codec_iter_t iterator = 0; - const vpx_codec_cx_pkt_t* packet; - while ((packet = vpx_codec_get_cx_data(&_codec, &iterator)) != 0) { - if (packet->kind == VPX_CODEC_CX_FRAME_PKT) { - // prepend the aspect ratio - QByteArray payload(sizeof(float), 0); - *(float*)payload.data() = _smoothedFaceRect.size.width / _smoothedFaceRect.size.height; - payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz); - QMetaObject::invokeMethod(Application::getInstance(), "sendAvatarFaceVideoMessage", Q_ARG(int, _frameCount), - Q_ARG(QByteArray, payload)); - } - } + QMetaObject::invokeMethod(Application::getInstance(), "sendAvatarFaceVideoMessage", + Q_ARG(int, _frameCount), Q_ARG(QByteArray, payload)); QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", Q_ARG(cv::Mat, color), Q_ARG(int, format), Q_ARG(cv::Mat, _grayDepthFrame), Q_ARG(float, _smoothedMeanFaceDepth), diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index 16cf3a33a1..6c6d250897 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -120,7 +120,8 @@ private: cv::Mat _grayDepthFrame; float _smoothedMeanFaceDepth; - vpx_codec_ctx_t _codec; + vpx_codec_ctx_t _colorCodec; + vpx_codec_ctx_t _depthCodec; int _frameCount; cv::Mat _faceColor; cv::Mat _faceDepth; diff --git a/interface/src/avatar/Face.cpp b/interface/src/avatar/Face.cpp index 5d6c4984bd..f25426a5be 100644 --- a/interface/src/avatar/Face.cpp +++ b/interface/src/avatar/Face.cpp @@ -30,19 +30,25 @@ GLuint Face::_vboID; GLuint Face::_iboID; Face::Face(Head* owningHead) : _owningHead(owningHead), _renderMode(MESH), - _colorTextureID(0), _depthTextureID(0), _codec(), _frameCount(0) { + _colorTextureID(0), _depthTextureID(0), _colorCodec(), _depthCodec(), _frameCount(0) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); } Face::~Face() { - if (_codec.name != 0) { - vpx_codec_destroy(&_codec); + if (_colorCodec.name != 0) { + vpx_codec_destroy(&_colorCodec); - // delete our textures, since we know that we own them + // delete our texture, since we know that we own it if (_colorTextureID != 0) { glDeleteTextures(1, &_colorTextureID); } + + } + if (_depthCodec.name != 0) { + vpx_codec_destroy(&_depthCodec); + + // delete our texture, since we know that we own it if (_depthTextureID != 0) { glDeleteTextures(1, &_depthTextureID); } @@ -55,9 +61,9 @@ void Face::setTextureRect(const cv::RotatedRect& textureRect) { } int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { - if (_codec.name == 0) { + if (_colorCodec.name == 0) { // initialize decoder context - vpx_codec_dec_init(&_codec, vpx_codec_vp8_dx(), 0, 0); + vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0); } // skip the header unsigned char* packetPosition = packetData; @@ -85,14 +91,14 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { if ((_frameBytesRemaining -= payloadSize) <= 0) { float aspectRatio = *(const float*)_arrivingFrame.constData(); - vpx_codec_decode(&_codec, (const uint8_t*)_arrivingFrame.constData() + sizeof(float), - _arrivingFrame.size() - sizeof(float), 0, 0); + size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float)); + const uint8_t* colorData = (const uint8_t*)(_arrivingFrame.constData() + sizeof(float) + sizeof(size_t)); + vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0); vpx_codec_iter_t iterator = 0; vpx_image_t* image; - while ((image = vpx_codec_get_frame(&_codec, &iterator)) != 0) { + while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) { // convert from YV12 to RGB - const int imageHeight = image->d_w; - Mat color(imageHeight, image->d_w, CV_8UC3); + Mat color(image->d_h, image->d_w, CV_8UC3); uchar* yline = image->planes[0]; uchar* vline = image->planes[1]; uchar* uline = image->planes[2]; @@ -100,7 +106,7 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { const int GREEN_V_WEIGHT = (int)(0.714 * 256); const int GREEN_U_WEIGHT = (int)(0.344 * 256); const int BLUE_U_WEIGHT = (int)(1.773 * 256); - for (int i = 0; i < imageHeight; i += 2) { + for (int i = 0; i < image->d_h; i += 2) { uchar* ysrc = yline; uchar* vsrc = vline; uchar* usrc = uline; @@ -144,34 +150,45 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { uline += image->stride[2]; } Mat depth; - if (image->d_h > imageHeight) { - // if the height is greater than the width, we have depth data - depth.create(imageHeight, image->d_w, CV_8UC1); - uchar* yline = image->planes[0] + image->stride[0] * imageHeight; - uchar* vline = image->planes[1] + image->stride[1] * (imageHeight / 2); - const uchar EIGHT_BIT_MAXIMUM = 255; - const uchar MASK_THRESHOLD = 192; - for (int i = 0; i < imageHeight; i += 2) { - uchar* ysrc = yline; - uchar* vsrc = vline; - for (int j = 0; j < image->d_w; j += 2) { - - if (*vsrc++ >= MASK_THRESHOLD) { - *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM; - - } else { - *depth.ptr(i, j) = ysrc[0]; - *depth.ptr(i, j + 1) = ysrc[1]; - *depth.ptr(i + 1, j) = ysrc[image->stride[0]]; - *depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1]; + + const uint8_t* depthData = colorData + colorSize; + int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData()); + if (depthSize > 0) { + if (_depthCodec.name == 0) { + // initialize decoder context + vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0); + } + vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0); + vpx_codec_iter_t iterator = 0; + vpx_image_t* image; + while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) { + depth.create(image->d_h, image->d_w, CV_8UC1); + uchar* yline = image->planes[0]; + uchar* vline = image->planes[1]; + const uchar EIGHT_BIT_MAXIMUM = 255; + const uchar MASK_THRESHOLD = 192; + for (int i = 0; i < image->d_h; i += 2) { + uchar* ysrc = yline; + uchar* vsrc = vline; + for (int j = 0; j < image->d_w; j += 2) { + + if (*vsrc++ >= MASK_THRESHOLD) { + *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM; + + } else { + *depth.ptr(i, j) = ysrc[0]; + *depth.ptr(i, j + 1) = ysrc[1]; + *depth.ptr(i + 1, j) = ysrc[image->stride[0]]; + *depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1]; + } + ysrc += 2; } - ysrc += 2; + yline += image->stride[0] * 2; + vline += image->stride[1]; } - yline += image->stride[0] * 2; - vline += image->stride[1]; } } QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color), diff --git a/interface/src/avatar/Face.h b/interface/src/avatar/Face.h index 1f9d41a1b3..d4812fecfb 100644 --- a/interface/src/avatar/Face.h +++ b/interface/src/avatar/Face.h @@ -57,7 +57,8 @@ private: cv::RotatedRect _textureRect; float _aspectRatio; - vpx_codec_ctx_t _codec; + vpx_codec_ctx_t _colorCodec; + vpx_codec_ctx_t _depthCodec; QByteArray _arrivingFrame; int _frameCount; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 292c4bbc0a..2e7b95c7f7 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -18,14 +18,15 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO: case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO: return 1; - break; case PACKET_TYPE_HEAD_DATA: return 2; - break; + + case PACKET_TYPE_AVATAR_FACE_VIDEO: + return 1; + default: return 0; - break; } } From 96bd7dbe25016b77c530a7e3d67c0ad2ac7500a9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Jul 2013 15:30:34 -0700 Subject: [PATCH 26/27] Removed the magic from a number. --- interface/src/Webcam.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 2a58d51dae..b39f11aa67 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -152,6 +152,8 @@ Webcam::~Webcam() { delete _grabber; } +const float METERS_PER_MM = 1.0f / 1000.0f; + void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float meanFaceDepth, const RotatedRect& faceRect, const JointVector& joints) { IplImage colorImage = color; @@ -250,7 +252,7 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float mean z = INITIAL_DISTANCE_TO_CAMERA * proportion - INITIAL_DISTANCE_TO_CAMERA; } else { - z = (meanFaceDepth - _initialFaceDepth) / 1000.0f; + z = (meanFaceDepth - _initialFaceDepth) * METERS_PER_MM; proportion = meanFaceDepth / _initialFaceDepth; } const float POSITION_SCALE = 0.5f; @@ -437,7 +439,6 @@ void FrameGrabber::grabFrame() { _userID, (XnSkeletonJoint)parentJoint, parentOrientation); rotation = glm::inverse(xnToGLM(parentOrientation.orientation)) * rotation; } - const float METERS_PER_MM = 1.0f / 1000.0f; joints[avatarJoint] = Joint(xnToGLM(transform.position.position, true) * METERS_PER_MM, rotation, xnToGLM(projected)); } From 353d674ad95a34a6b515c827dce33d1d4b6b926a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 30 Jul 2013 15:56:47 -0700 Subject: [PATCH 27/27] Masking tweak. --- interface/src/Webcam.cpp | 12 +++++++----- interface/src/avatar/Face.cpp | 3 +-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index b39f11aa67..d4fa015ba0 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -508,6 +508,8 @@ void FrameGrabber::grabFrame() { vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0); if (!depth.empty()) { + int DEPTH_BITRATE_MULTIPLIER = 2; + codecConfig.rc_target_bitrate *= 2; vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0); } } @@ -660,14 +662,14 @@ void FrameGrabber::grabFrame() { ushort bl = *_faceDepth.ptr(i + 1, j); ushort br = *_faceDepth.ptr(i + 1, j + 1); - uchar mask = EIGHT_BIT_MIDPOINT; + uchar mask = EIGHT_BIT_MAXIMUM; - ydest[0] = (tl == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(tl + depthOffset); - ydest[1] = (tr == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(tr + depthOffset); + ydest[0] = (tl == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast(tl + depthOffset); + ydest[1] = (tr == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast(tr + depthOffset); ydest[vpxImage.stride[0]] = (bl == ELEVEN_BIT_MINIMUM) ? - (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(bl + depthOffset); + (mask = EIGHT_BIT_MIDPOINT) : saturate_cast(bl + depthOffset); ydest[vpxImage.stride[0] + 1] = (br == ELEVEN_BIT_MINIMUM) ? - (mask = EIGHT_BIT_MAXIMUM) : saturate_cast(br + depthOffset); + (mask = EIGHT_BIT_MIDPOINT) : saturate_cast(br + depthOffset); ydest += 2; *vdest++ = mask; diff --git a/interface/src/avatar/Face.cpp b/interface/src/avatar/Face.cpp index f25426a5be..ff31241c54 100644 --- a/interface/src/avatar/Face.cpp +++ b/interface/src/avatar/Face.cpp @@ -171,8 +171,7 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { uchar* ysrc = yline; uchar* vsrc = vline; for (int j = 0; j < image->d_w; j += 2) { - - if (*vsrc++ >= MASK_THRESHOLD) { + if (*vsrc++ < MASK_THRESHOLD) { *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM;