mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:44:01 +02:00
Merge branch 'master' of github.com:worklist/hifi into qt5
This commit is contained in:
commit
9e63b78a69
21 changed files with 425 additions and 265 deletions
|
@ -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,53 @@ 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).
|
||||
void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) {
|
||||
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 = 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 +114,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 +148,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:
|
||||
|
|
|
@ -75,8 +75,6 @@ using namespace std;
|
|||
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
||||
|
||||
static const bool TESTING_PARTICLE_SYSTEM = true;
|
||||
|
||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||
|
||||
const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins in the world
|
||||
|
@ -407,7 +405,7 @@ void Application::paintGL() {
|
|||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
|
||||
_myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition());
|
||||
_myCamera.setTargetPosition(_myAvatar.getUprightEyeLevelPosition());
|
||||
_myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation());
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
|
@ -1497,11 +1495,11 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
|
|||
int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed;
|
||||
if (usecToSleep > 0) {
|
||||
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, sleeping for %d usecs!\n",
|
||||
args->packetsSent, args->bytesSent, elapsed, usecToSleep);
|
||||
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed, usecToSleep);
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
qDebug("sendVoxelsOperation: packet: %d bytes:%lld elapsed %lld usecs, no need to sleep!\n",
|
||||
args->packetsSent, args->bytesSent, elapsed);
|
||||
args->packetsSent, (long long int)args->bytesSent, (long long int)elapsed);
|
||||
}
|
||||
args->lastSendTime = now;
|
||||
}
|
||||
|
@ -1686,7 +1684,7 @@ void Application::pasteVoxels() {
|
|||
controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1);
|
||||
qDebug("sending packet: %d\n", ++args.packetsSent);
|
||||
args.bytesSent += args.bufferInUse;
|
||||
qDebug("total bytes sent: %lld\n", args.bytesSent);
|
||||
qDebug("total bytes sent: %lld\n", (long long int)args.bytesSent);
|
||||
}
|
||||
|
||||
if (calculatedOctCode) {
|
||||
|
@ -1751,6 +1749,8 @@ void Application::initMenu() {
|
|||
_renderLookatOn->setChecked(false);
|
||||
(_renderLookatIndicatorOn = renderMenu->addAction("Lookat Indicator"))->setCheckable(true);
|
||||
_renderLookatIndicatorOn->setChecked(true);
|
||||
(_renderParticleSystemOn = renderMenu->addAction("Particle System"))->setCheckable(true);
|
||||
_renderParticleSystemOn->setChecked(true);
|
||||
(_manualFirstPerson = renderMenu->addAction(
|
||||
"First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P))->setCheckable(true);
|
||||
(_manualThirdPerson = renderMenu->addAction(
|
||||
|
@ -2263,7 +2263,7 @@ void Application::update(float deltaTime) {
|
|||
_audio.eventuallyAnalyzePing();
|
||||
#endif
|
||||
|
||||
if (TESTING_PARTICLE_SYSTEM) {
|
||||
if (_renderParticleSystemOn->isChecked()) {
|
||||
updateParticleSystem(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -2712,14 +2712,13 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
_myAvatar.getHead().setLookAtPosition(_myCamera.getPosition());
|
||||
}
|
||||
_myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked());
|
||||
_myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked());
|
||||
|
||||
if (_renderLookatIndicatorOn->isChecked() && _isLookingAtOtherAvatar) {
|
||||
renderLookatIndicator(_lookatOtherPosition, whichCamera);
|
||||
}
|
||||
}
|
||||
|
||||
if (TESTING_PARTICLE_SYSTEM) {
|
||||
if (_renderParticleSystemOn->isChecked()) {
|
||||
if (_particleSystemInitialized) {
|
||||
_particleSystem.render();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -58,6 +58,32 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) :
|
|||
_tree = new VoxelTree();
|
||||
pthread_mutex_init(&_bufferWriteLock, NULL);
|
||||
pthread_mutex_init(&_treeLock, NULL);
|
||||
|
||||
VoxelNode::addDeleteHook(this);
|
||||
_abandonedVBOSlots = 0;
|
||||
}
|
||||
|
||||
void VoxelSystem::nodeDeleted(VoxelNode* node) {
|
||||
if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {
|
||||
freeBufferIndex(node->getBufferIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::freeBufferIndex(glBufferIndex index) {
|
||||
_freeIndexes.push_back(index);
|
||||
}
|
||||
|
||||
void VoxelSystem::clearFreeBufferIndexes() {
|
||||
for (int i = 0; i < _freeIndexes.size(); i++) {
|
||||
glBufferIndex nodeIndex = _freeIndexes[i];
|
||||
glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
float voxelScale = 0;
|
||||
_writeVoxelDirtyArray[nodeIndex] = true;
|
||||
nodeColor color = {0, 0, 0, 0};
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, color);
|
||||
_abandonedVBOSlots++;
|
||||
}
|
||||
_freeIndexes.clear();
|
||||
}
|
||||
|
||||
VoxelSystem::~VoxelSystem() {
|
||||
|
@ -70,6 +96,8 @@ VoxelSystem::~VoxelSystem() {
|
|||
delete _tree;
|
||||
pthread_mutex_destroy(&_bufferWriteLock);
|
||||
pthread_mutex_destroy(&_treeLock);
|
||||
|
||||
VoxelNode::removeDeleteHook(this);
|
||||
}
|
||||
|
||||
void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) {
|
||||
|
@ -173,6 +201,9 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
|
||||
uint64_t start = usecTimestampNow();
|
||||
uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;
|
||||
|
||||
// clear up the VBOs for any nodes that have been recently deleted.
|
||||
clearFreeBufferIndexes();
|
||||
|
||||
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
|
||||
if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
|
||||
|
@ -182,7 +213,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
|
||||
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
|
||||
if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
|
||||
&& !isViewChanging() && hasViewChanged()) {
|
||||
&& !isViewChanging()) {
|
||||
_lastViewCulling = start;
|
||||
|
||||
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
|
||||
|
@ -212,6 +243,10 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
}
|
||||
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
|
||||
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
|
||||
|
||||
if (_writeRenderFullVBO) {
|
||||
_abandonedVBOSlots = 0; // reset the count of our abandoned slots
|
||||
}
|
||||
|
||||
// since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are
|
||||
// ok again, until/unless we call removeOutOfView()
|
||||
|
@ -240,12 +275,19 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
|
||||
void VoxelSystem::cleanupRemovedVoxels() {
|
||||
PerformanceWarning warn(_renderWarningsOn, "cleanupRemovedVoxels()");
|
||||
// This handles cleanup of voxels that were culled as part of our regular out of view culling operation
|
||||
if (!_removedVoxels.isEmpty()) {
|
||||
while (!_removedVoxels.isEmpty()) {
|
||||
delete _removedVoxels.extract();
|
||||
}
|
||||
_writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
|
||||
}
|
||||
// we also might have VBO slots that have been abandoned, if too many of our VBO slots
|
||||
// are abandonded we want to rerender our full VBOs
|
||||
const float TOO_MANY_ABANDONED_RATIO = 0.25f;
|
||||
if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) {
|
||||
_writeRenderFullVBO = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
|
||||
|
@ -323,7 +365,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
bool shouldRender = false; // assume we don't need to render it
|
||||
// if it's colored, we might need to render it!
|
||||
shouldRender = node->calculateShouldRender(Application::getInstance()->getViewFrustum());
|
||||
node->setShouldRender(shouldRender && !node->isStagedForDeletion());
|
||||
node->setShouldRender(shouldRender);
|
||||
// let children figure out their renderness
|
||||
if (!node->isLeaf()) {
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
|
@ -339,13 +381,6 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
}
|
||||
node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
|
||||
|
||||
// If the node has been asked to be deleted, but we've gotten to here, after updateNodeInArraysXXX()
|
||||
// then it means our VBOs are "clean" and our vertices have been removed or not added. So we can now
|
||||
// safely remove the node from the tree and actually delete it.
|
||||
if (node->isStagedForDeletion()) {
|
||||
_tree->deleteVoxelCodeFromTree(node->getOctalCode());
|
||||
}
|
||||
|
||||
return voxelsUpdated;
|
||||
}
|
||||
|
||||
|
@ -364,11 +399,13 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
|||
// and RGB color for each added vertex
|
||||
updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());
|
||||
node->setBufferIndex(nodeIndex);
|
||||
node->setVoxelSystem(this);
|
||||
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
|
||||
_voxelsInWriteArrays++; // our know vertices in the arrays
|
||||
return 1; // rendered
|
||||
} else {
|
||||
node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
|
||||
node->setVoxelSystem(NULL);
|
||||
}
|
||||
|
||||
return 0; // not-rendered
|
||||
|
@ -393,6 +430,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
|||
// and our scale as infinitely small
|
||||
startVertex[0] = startVertex[1] = startVertex[2] = FLT_MAX;
|
||||
voxelScale = 0;
|
||||
_abandonedVBOSlots++;
|
||||
}
|
||||
|
||||
// If this node has not yet been written to the array, then add it to the end of the array.
|
||||
|
@ -402,6 +440,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
|||
} else {
|
||||
nodeIndex = _voxelsInWriteArrays;
|
||||
node->setBufferIndex(nodeIndex);
|
||||
node->setVoxelSystem(this);
|
||||
_voxelsInWriteArrays++;
|
||||
}
|
||||
_writeVoxelDirtyArray[nodeIndex] = true;
|
||||
|
@ -445,7 +484,6 @@ void VoxelSystem::init() {
|
|||
_voxelsDirty = false;
|
||||
_voxelsInWriteArrays = 0;
|
||||
_voxelsInReadArrays = 0;
|
||||
_unusedArraySpace = 0;
|
||||
|
||||
// we will track individual dirty sections with these arrays of bools
|
||||
_writeVoxelDirtyArray = new bool[_maxVoxels];
|
||||
|
@ -1111,7 +1149,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
|
|||
void VoxelSystem::deleteVoxelAt(float x, float y, float z, float s) {
|
||||
pthread_mutex_lock(&_treeLock);
|
||||
|
||||
_tree->deleteVoxelAt(x, y, z, s, true);
|
||||
_tree->deleteVoxelAt(x, y, z, s);
|
||||
|
||||
// redraw!
|
||||
setupNewVoxelsForDrawing(); // do we even need to do this? Or will the next network receive kick in?
|
||||
|
@ -1167,7 +1205,6 @@ struct FalseColorizeOccludedArgs {
|
|||
long nonLeaves;
|
||||
long nonLeavesOutOfView;
|
||||
long nonLeavesOccluded;
|
||||
long stagedForDeletion;
|
||||
};
|
||||
|
||||
struct FalseColorizeSubTreeOperationArgs {
|
||||
|
@ -1189,12 +1226,6 @@ bool VoxelSystem::falseColorizeOccludedOperation(VoxelNode* node, void* extraDat
|
|||
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
|
||||
args->totalVoxels++;
|
||||
|
||||
// if this node is staged for deletion, then just return
|
||||
if (node->isStagedForDeletion()) {
|
||||
args->stagedForDeletion++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a parent, let's see if we're completely occluded.
|
||||
if (!node->isLeaf()) {
|
||||
args->nonLeaves++;
|
||||
|
@ -1275,7 +1306,6 @@ void VoxelSystem::falseColorizeOccluded() {
|
|||
args.outOfView = 0;
|
||||
args.subtreeVoxelsSkipped = 0;
|
||||
args.nonLeaves = 0;
|
||||
args.stagedForDeletion = 0;
|
||||
args.nonLeavesOutOfView = 0;
|
||||
args.nonLeavesOccluded = 0;
|
||||
args.tree = _tree;
|
||||
|
@ -1288,11 +1318,10 @@ void VoxelSystem::falseColorizeOccluded() {
|
|||
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedOperation, position, (void*)&args);
|
||||
|
||||
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
qDebug("falseColorizeOccluded()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
position.x, position.y,
|
||||
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||
args.stagedForDeletion,
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
|
||||
VoxelProjectedPolygon::pointInside_calls,
|
||||
VoxelProjectedPolygon::occludes_calls,
|
||||
|
@ -1310,12 +1339,6 @@ bool VoxelSystem::falseColorizeOccludedV2Operation(VoxelNode* node, void* extraD
|
|||
FalseColorizeOccludedArgs* args = (FalseColorizeOccludedArgs*) extraData;
|
||||
args->totalVoxels++;
|
||||
|
||||
// if this node is staged for deletion, then just return
|
||||
if (node->isStagedForDeletion()) {
|
||||
args->stagedForDeletion++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are a parent, let's see if we're completely occluded.
|
||||
if (!node->isLeaf()) {
|
||||
args->nonLeaves++;
|
||||
|
@ -1404,7 +1427,6 @@ void VoxelSystem::falseColorizeOccludedV2() {
|
|||
args.outOfView = 0;
|
||||
args.subtreeVoxelsSkipped = 0;
|
||||
args.nonLeaves = 0;
|
||||
args.stagedForDeletion = 0;
|
||||
args.nonLeavesOutOfView = 0;
|
||||
args.nonLeavesOccluded = 0;
|
||||
args.tree = _tree;
|
||||
|
@ -1413,11 +1435,10 @@ void VoxelSystem::falseColorizeOccludedV2() {
|
|||
|
||||
_tree->recurseTreeWithOperationDistanceSorted(falseColorizeOccludedV2Operation, position, (void*)&args);
|
||||
|
||||
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n stagedForDeletion=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
qDebug("falseColorizeOccludedV2()\n position=(%f,%f)\n total=%ld\n colored=%ld\n occluded=%ld\n notOccluded=%ld\n outOfView=%ld\n subtreeVoxelsSkipped=%ld\n nonLeaves=%ld\n nonLeavesOutOfView=%ld\n nonLeavesOccluded=%ld\n pointInside_calls=%ld\n occludes_calls=%ld\n intersects_calls=%ld\n",
|
||||
position.x, position.y,
|
||||
args.totalVoxels, args.coloredVoxels, args.occludedVoxels,
|
||||
args.notOccludedVoxels, args.outOfView, args.subtreeVoxelsSkipped,
|
||||
args.stagedForDeletion,
|
||||
args.nonLeaves, args.nonLeavesOutOfView, args.nonLeavesOccluded,
|
||||
VoxelProjectedPolygon::pointInside_calls,
|
||||
VoxelProjectedPolygon::occludes_calls,
|
||||
|
|
|
@ -28,7 +28,7 @@ class ProgramObject;
|
|||
|
||||
const int NUM_CHILDREN = 8;
|
||||
|
||||
class VoxelSystem : public NodeData {
|
||||
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook {
|
||||
public:
|
||||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
@ -92,6 +92,8 @@ public:
|
|||
|
||||
CoverageMapV2 myCoverageMapV2;
|
||||
CoverageMap myCoverageMap;
|
||||
|
||||
virtual void nodeDeleted(VoxelNode* node);
|
||||
|
||||
protected:
|
||||
float _treeScale;
|
||||
|
@ -155,7 +157,7 @@ private:
|
|||
unsigned long _voxelsUpdated;
|
||||
unsigned long _voxelsInReadArrays;
|
||||
unsigned long _voxelsInWriteArrays;
|
||||
unsigned long _unusedArraySpace;
|
||||
unsigned long _abandonedVBOSlots;
|
||||
|
||||
bool _writeRenderFullVBO;
|
||||
bool _readRenderFullVBO;
|
||||
|
@ -187,6 +189,12 @@ private:
|
|||
|
||||
static ProgramObject* _perlinModulateProgram;
|
||||
static GLuint _permutationNormalTextureID;
|
||||
|
||||
int _hookID;
|
||||
std::vector<glBufferIndex> _freeIndexes;
|
||||
|
||||
void freeBufferIndex(glBufferIndex index);
|
||||
void clearFreeBufferIndexes();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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,10 @@ Webcam::~Webcam() {
|
|||
delete _grabber;
|
||||
}
|
||||
|
||||
void Webcam::setFrame(const Mat& color, int format, const Mat& depth, const RotatedRect& faceRect, const JointVector& joints) {
|
||||
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;
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3);
|
||||
if (_colorTextureID == 0) {
|
||||
|
@ -232,22 +238,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) * METERS_PER_MM;
|
||||
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 +271,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), _colorCodec(), _depthCodec(), _frameCount(0) {
|
||||
}
|
||||
|
||||
FrameGrabber::~FrameGrabber() {
|
||||
|
@ -367,9 +379,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;
|
||||
|
||||
|
@ -423,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));
|
||||
}
|
||||
|
@ -480,31 +495,23 @@ 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);
|
||||
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);
|
||||
codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * combinedFaceHeight * codecConfig.rc_target_bitrate /
|
||||
codecConfig.g_w / codecConfig.g_h;
|
||||
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()) {
|
||||
int DEPTH_BITRATE_MULTIPLIER = 2;
|
||||
codecConfig.rc_target_bitrate *= 2;
|
||||
vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// correct for 180 degree rotations
|
||||
|
@ -541,9 +548,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];
|
||||
|
@ -571,9 +578,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;
|
||||
|
||||
|
@ -590,37 +597,107 @@ 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());
|
||||
|
||||
uchar* dest = (uchar*)_encodedFace.data() + vpxImage.stride[0] * ENCODED_FACE_HEIGHT;
|
||||
for (int i = 0; i < ENCODED_FACE_HEIGHT; i++) {
|
||||
memcpy(dest, _faceDepth.ptr(i), ENCODED_FACE_WIDTH);
|
||||
dest += vpxImage.stride[0];
|
||||
}
|
||||
}
|
||||
|
||||
// encode the frame
|
||||
vpx_codec_encode(&_codec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME);
|
||||
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(&_codec, &iterator)) != 0) {
|
||||
while ((packet = vpx_codec_get_cx_data(&_colorCodec, &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;
|
||||
// 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);
|
||||
QMetaObject::invokeMethod(Application::getInstance(), "sendAvatarFaceVideoMessage", Q_ARG(int, _frameCount),
|
||||
Q_ARG(QByteArray, payload));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
warpAffine(depth, _faceDepth, transform, _faceDepth.size(), INTER_NEAREST);
|
||||
|
||||
// find the mean of the valid values
|
||||
qint64 depthTotal = 0;
|
||||
qint64 depthSamples = 0;
|
||||
ushort* src = _faceDepth.ptr<ushort>();
|
||||
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 != ELEVEN_BIT_MINIMUM && depth != ELEVEN_BIT_MAXIMUM) {
|
||||
depthTotal += depth;
|
||||
depthSamples++;
|
||||
}
|
||||
}
|
||||
}
|
||||
float mean = (depthSamples == 0) ? UNINITIALIZED_FACE_DEPTH : depthTotal / (float)depthSamples;
|
||||
|
||||
// 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
|
||||
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 = 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<ushort>(i, j);
|
||||
ushort tr = *_faceDepth.ptr<ushort>(i, j + 1);
|
||||
ushort bl = *_faceDepth.ptr<ushort>(i + 1, j);
|
||||
ushort br = *_faceDepth.ptr<ushort>(i + 1, j + 1);
|
||||
|
||||
uchar mask = EIGHT_BIT_MAXIMUM;
|
||||
|
||||
ydest[0] = (tl == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(tl + depthOffset);
|
||||
ydest[1] = (tr == ELEVEN_BIT_MINIMUM) ? (mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(tr + depthOffset);
|
||||
ydest[vpxImage.stride[0]] = (bl == ELEVEN_BIT_MINIMUM) ?
|
||||
(mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(bl + depthOffset);
|
||||
ydest[vpxImage.stride[0] + 1] = (br == ELEVEN_BIT_MINIMUM) ?
|
||||
(mask = EIGHT_BIT_MIDPOINT) : saturate_cast<uchar>(br + depthOffset);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,9 +118,10 @@ private:
|
|||
cv::Mat _backProject;
|
||||
cv::Rect _searchWindow;
|
||||
cv::Mat _grayDepthFrame;
|
||||
double _depthOffset;
|
||||
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;
|
||||
|
|
|
@ -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) {
|
||||
//
|
||||
|
|
|
@ -171,6 +171,7 @@ public:
|
|||
glm::vec3 getGravity () const { return _gravity; }
|
||||
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
glm::vec3 getUprightEyeLevelPosition() const;
|
||||
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
|
|
|
@ -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,13 +150,44 @@ 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* 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];
|
||||
|
||||
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;
|
||||
}
|
||||
yline += image->stride[0] * 2;
|
||||
vline += image->stride[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,10 +59,10 @@ 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
|
||||
(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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -48,9 +48,9 @@ void VoxelNode::init(unsigned char * octalCode) {
|
|||
_subtreeLeafNodeCount = 0; // that's me
|
||||
|
||||
_glBufferIndex = GLBUFFER_INDEX_UNKNOWN;
|
||||
_voxelSystem = NULL;
|
||||
_isDirty = true;
|
||||
_shouldRender = false;
|
||||
_isStagedForDeletion = false;
|
||||
markWithChangedTime();
|
||||
calculateAABox();
|
||||
}
|
||||
|
@ -159,28 +159,18 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
|
|||
}
|
||||
|
||||
// handles staging or deletion of all deep children
|
||||
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion) {
|
||||
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex) {
|
||||
VoxelNode* childToDelete = getChildAtIndex(childIndex);
|
||||
if (childToDelete) {
|
||||
// If the child is not a leaf, then call ourselves recursively on all the children
|
||||
if (!childToDelete->isLeaf()) {
|
||||
// delete all it's children
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
childToDelete->safeDeepDeleteChildAtIndex(i, stagedForDeletion);
|
||||
childToDelete->safeDeepDeleteChildAtIndex(i);
|
||||
}
|
||||
}
|
||||
// if this node has a BufferIndex then we need to stage it for deletion
|
||||
// instead of actually deleting it from the tree
|
||||
if (childToDelete->isKnownBufferIndex()) {
|
||||
stagedForDeletion = true;
|
||||
}
|
||||
if (stagedForDeletion) {
|
||||
childToDelete->stageForDeletion();
|
||||
_isDirty = true;
|
||||
} else {
|
||||
deleteChildAtIndex(childIndex);
|
||||
_isDirty = true;
|
||||
}
|
||||
deleteChildAtIndex(childIndex);
|
||||
_isDirty = true;
|
||||
markWithChangedTime();
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +180,7 @@ void VoxelNode::setColorFromAverageOfChildren() {
|
|||
int colorArray[4] = {0,0,0,0};
|
||||
float density = 0.0f;
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
if (_children[i] && !_children[i]->isStagedForDeletion() && _children[i]->isColored()) {
|
||||
if (_children[i] && _children[i]->isColored()) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
colorArray[j] += _children[i]->getTrueColor()[j]; // color averaging should always be based on true colors
|
||||
}
|
||||
|
@ -279,7 +269,7 @@ bool VoxelNode::collapseIdenticalLeaves() {
|
|||
int red,green,blue;
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
// if no child, child isn't a leaf, or child doesn't have a color
|
||||
if (!_children[i] || _children[i]->isStagedForDeletion() || !_children[i]->isLeaf() || !_children[i]->isColored()) {
|
||||
if (!_children[i] || !_children[i]->isLeaf() || !_children[i]->isColored()) {
|
||||
allChildrenMatch=false;
|
||||
//qDebug("SADNESS child missing or not colored! i=%d\n",i);
|
||||
break;
|
||||
|
@ -410,43 +400,23 @@ float VoxelNode::distanceToPoint(const glm::vec3& point) const {
|
|||
return distance;
|
||||
}
|
||||
|
||||
VoxelNodeDeleteHook VoxelNode::_hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||
void* VoxelNode::_hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||
int VoxelNode::_hooksInUse = 0;
|
||||
std::vector<VoxelNodeDeleteHook*> VoxelNode::_hooks;
|
||||
|
||||
int VoxelNode::addDeleteHook(VoxelNodeDeleteHook hook, void* extraData) {
|
||||
// If first use, initialize the _hooks array
|
||||
if (_hooksInUse == 0) {
|
||||
memset(_hooks, 0, sizeof(_hooks));
|
||||
memset(_hooksExtraData, 0, sizeof(_hooksExtraData));
|
||||
}
|
||||
// find first available slot
|
||||
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
|
||||
if (!_hooks[i]) {
|
||||
_hooks[i] = hook;
|
||||
_hooksExtraData[i] = extraData;
|
||||
_hooksInUse++;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// if we got here, then we're out of room in our hooks, return error
|
||||
return VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE;
|
||||
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
|
||||
_hooks.push_back(hook);
|
||||
}
|
||||
|
||||
void VoxelNode::removeDeleteHook(int hookID) {
|
||||
if (_hooks[hookID]) {
|
||||
_hooks[hookID] = NULL;
|
||||
_hooksExtraData[hookID] = NULL;
|
||||
_hooksInUse--;
|
||||
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
if (_hooks[i] == hook) {
|
||||
_hooks.erase(_hooks.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelNode::notifyDeleteHooks() {
|
||||
if (_hooksInUse > 0) {
|
||||
for (int i = 0; i < VOXEL_NODE_MAX_DELETE_HOOKS; i++) {
|
||||
if (_hooks[i]) {
|
||||
_hooks[i](this, _hooksExtraData[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _hooks.size(); i++) {
|
||||
_hooks[i]->nodeDeleted(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,18 +14,19 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
class VoxelTree; // forward delclaration
|
||||
class VoxelNode; // forward delclaration
|
||||
class VoxelTree; // forward declaration
|
||||
class VoxelNode; // forward declaration
|
||||
class VoxelSystem; // forward declaration
|
||||
|
||||
typedef unsigned char colorPart;
|
||||
typedef unsigned char nodeColor[4];
|
||||
typedef unsigned char rgbColor[3];
|
||||
|
||||
// Callback function, for delete hook
|
||||
typedef void (*VoxelNodeDeleteHook)(VoxelNode* node, void* extraData);
|
||||
const int VOXEL_NODE_MAX_DELETE_HOOKS = 100;
|
||||
const int VOXEL_NODE_NO_MORE_HOOKS_AVAILABLE = -1;
|
||||
|
||||
// Callers who want delete hook callbacks should implement this class
|
||||
class VoxelNodeDeleteHook {
|
||||
public:
|
||||
virtual void nodeDeleted(VoxelNode* node) = 0;
|
||||
};
|
||||
|
||||
class VoxelNode {
|
||||
public:
|
||||
|
@ -38,7 +39,7 @@ public:
|
|||
void deleteChildAtIndex(int childIndex);
|
||||
VoxelNode* removeChildAtIndex(int childIndex);
|
||||
VoxelNode* addChildAtIndex(int childIndex);
|
||||
void safeDeepDeleteChildAtIndex(int childIndex, bool& stagedForDeletion); // handles staging or deletion of all descendents
|
||||
void safeDeepDeleteChildAtIndex(int childIndex); // handles deletion of all descendents
|
||||
|
||||
void setColorFromAverageOfChildren();
|
||||
void setRandomColor(int minimumBrightness);
|
||||
|
@ -77,15 +78,14 @@ public:
|
|||
glBufferIndex getBufferIndex() const { return _glBufferIndex; };
|
||||
bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); };
|
||||
void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; };
|
||||
VoxelSystem* getVoxelSystem() const { return _voxelSystem; };
|
||||
void setVoxelSystem(VoxelSystem* voxelSystem) { _voxelSystem = voxelSystem; };
|
||||
|
||||
|
||||
// Used by VoxelSystem for rendering in/out of view and LOD
|
||||
void setShouldRender(bool shouldRender);
|
||||
bool getShouldRender() const { return _shouldRender; }
|
||||
|
||||
// Used by VoxelSystem to mark a node as to be deleted on next render pass
|
||||
void stageForDeletion() { _isStagedForDeletion = true; _isDirty = true; };
|
||||
bool isStagedForDeletion() const { return _isStagedForDeletion; }
|
||||
|
||||
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
|
||||
void setFalseColor(colorPart red, colorPart green, colorPart blue);
|
||||
void setFalseColored(bool isFalseColored);
|
||||
|
@ -105,8 +105,8 @@ public:
|
|||
const nodeColor& getColor() const { return _trueColor; };
|
||||
#endif
|
||||
|
||||
static int addDeleteHook(VoxelNodeDeleteHook hook, void* extraData = NULL);
|
||||
static void removeDeleteHook(int hookID);
|
||||
static void addDeleteHook(VoxelNodeDeleteHook* hook);
|
||||
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
|
||||
|
||||
void recalculateSubTreeNodeCount();
|
||||
unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; };
|
||||
|
@ -124,10 +124,10 @@ private:
|
|||
bool _falseColored;
|
||||
#endif
|
||||
glBufferIndex _glBufferIndex;
|
||||
VoxelSystem* _voxelSystem;
|
||||
bool _isDirty;
|
||||
uint64_t _lastChanged;
|
||||
bool _shouldRender;
|
||||
bool _isStagedForDeletion;
|
||||
AABox _box;
|
||||
unsigned char* _octalCode;
|
||||
VoxelNode* _children[8];
|
||||
|
@ -135,10 +135,8 @@ private:
|
|||
unsigned long _subtreeNodeCount;
|
||||
unsigned long _subtreeLeafNodeCount;
|
||||
float _density; // If leaf: density = 1, if internal node: 0-1 density of voxels inside
|
||||
|
||||
static VoxelNodeDeleteHook _hooks[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||
static void* _hooksExtraData[VOXEL_NODE_MAX_DELETE_HOOKS];
|
||||
static int _hooksInUse;
|
||||
|
||||
static std::vector<VoxelNodeDeleteHook*> _hooks;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelNode__) */
|
||||
|
|
|
@ -13,11 +13,11 @@ VoxelNodeBag::VoxelNodeBag() :
|
|||
_bagElements(NULL),
|
||||
_elementsInUse(0),
|
||||
_sizeOfElementsArray(0) {
|
||||
_hookID = VoxelNode::addDeleteHook(voxelNodeDeleteHook, (void*)this);
|
||||
VoxelNode::addDeleteHook(this);
|
||||
};
|
||||
|
||||
VoxelNodeBag::~VoxelNodeBag() {
|
||||
VoxelNode::removeDeleteHook(_hookID);
|
||||
VoxelNode::removeDeleteHook(this);
|
||||
deleteAll();
|
||||
}
|
||||
|
||||
|
@ -126,9 +126,9 @@ void VoxelNodeBag::remove(VoxelNode* node) {
|
|||
}
|
||||
}
|
||||
|
||||
void VoxelNodeBag::voxelNodeDeleteHook(VoxelNode* node, void* extraData) {
|
||||
VoxelNodeBag* theBag = (VoxelNodeBag*)extraData;
|
||||
theBag->remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
|
||||
|
||||
void VoxelNodeBag::nodeDeleted(VoxelNode* node) {
|
||||
remove(node); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -1071,6 +1063,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 +1223,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 +1239,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 +1250,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 +1304,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 +1339,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 +1369,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 +1409,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);
|
||||
}
|
||||
|
@ -1474,11 +1477,13 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
if (params.includeColor && !params.includeExistsBits && childTreeBytesOut == 2) {
|
||||
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
||||
}
|
||||
// If we've asked for existBits, this is also true, except that the tree will output 3 bytes
|
||||
// NOTE: does this introduce a problem with detecting deletion??
|
||||
if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
|
||||
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
||||
}
|
||||
// We used to try to collapse trees that didn't contain any data, but this does appear to create a problem
|
||||
// in detecting node deletion. So, I've commented this out but left it in here as a warning to anyone else
|
||||
// about not attempting to add this optimization back in, without solving the node deletion case.
|
||||
// We need to send these bitMasks in case the exists in tree bitmask is indicating the deletion of a tree
|
||||
//if (params.includeColor && params.includeExistsBits && childTreeBytesOut == 3) {
|
||||
// childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
|
||||
//}
|
||||
|
||||
bytesAtThisLevel += childTreeBytesOut;
|
||||
availableBytes -= childTreeBytesOut;
|
||||
|
|
|
@ -22,23 +22,23 @@
|
|||
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
|
||||
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||
|
||||
#define NO_EXISTS_BITS false
|
||||
#define WANT_EXISTS_BITS true
|
||||
#define NO_COLOR false
|
||||
#define WANT_COLOR true
|
||||
#define IGNORE_VIEW_FRUSTUM NULL
|
||||
#define JUST_STAGE_DELETION true
|
||||
#define ACTUALLY_DELETE false
|
||||
#define COLLAPSE_EMPTY_TREE true
|
||||
#define DONT_COLLAPSE false
|
||||
#define NO_OCCLUSION_CULLING false
|
||||
#define WANT_OCCLUSION_CULLING true
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
#define DONT_CHOP 0
|
||||
#define NO_BOUNDARY_ADJUST 0
|
||||
#define LOW_RES_MOVING_ADJUST 1
|
||||
#define IGNORE_LAST_SENT 0
|
||||
const bool NO_EXISTS_BITS = false;
|
||||
const bool WANT_EXISTS_BITS = true;
|
||||
const bool NO_COLOR = false;
|
||||
const bool WANT_COLOR = true;
|
||||
const bool COLLAPSE_EMPTY_TREE = true;
|
||||
const bool DONT_COLLAPSE = false;
|
||||
const bool NO_OCCLUSION_CULLING = false;
|
||||
const bool WANT_OCCLUSION_CULLING = true;
|
||||
|
||||
const int DONT_CHOP = 0;
|
||||
const int NO_BOUNDARY_ADJUST = 0;
|
||||
const int LOW_RES_MOVING_ADJUST = 1;
|
||||
const uint64_t IGNORE_LAST_SENT = 0;
|
||||
|
||||
#define IGNORE_SCENE_STATS NULL
|
||||
#define IGNORE_VIEW_FRUSTUM NULL
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
|
||||
class EncodeBitstreamParams {
|
||||
public:
|
||||
|
@ -113,12 +113,11 @@ public:
|
|||
bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS,
|
||||
VoxelNode* destinationNode = NULL);
|
||||
void readCodeColorBufferToTree(unsigned char* codeColorBuffer, bool destructive = false);
|
||||
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool stage = ACTUALLY_DELETE,
|
||||
bool collapseEmptyTrees = DONT_COLLAPSE);
|
||||
void deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
|
||||
void printTreeForDebugging(VoxelNode* startNode);
|
||||
void reaverageVoxelColors(VoxelNode* startNode);
|
||||
|
||||
void deleteVoxelAt(float x, float y, float z, float s, bool stage = false);
|
||||
void deleteVoxelAt(float x, float y, float z, float s);
|
||||
VoxelNode* getVoxelAt(float x, float y, float z, float s) const;
|
||||
void createVoxel(float x, float y, float z, float s,
|
||||
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
|
||||
|
|
Loading…
Reference in a new issue