From 165342bcc3c8c90f7dab5a40708b3de487d27bf6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 10:35:21 -0700 Subject: [PATCH 01/31] Have the local avatar glow when moving. --- interface/src/OculusManager.cpp | 1 - interface/src/avatar/Avatar.cpp | 16 +++++++++++++++- interface/src/renderer/GlowEffect.cpp | 8 +++++--- interface/src/renderer/GlowEffect.h | 4 ++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/interface/src/OculusManager.cpp b/interface/src/OculusManager.cpp index 046e1684fb..17775b9f9c 100644 --- a/interface/src/OculusManager.cpp +++ b/interface/src/OculusManager.cpp @@ -46,7 +46,6 @@ void OculusManager::updateYawOffset() { } void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { - yaw = pitch = roll = 0.0f; #ifdef __APPLE__ _sensorFusion.GetOrientation().GetEulerAngles(&yaw, &pitch, &roll); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7cd3e4d7c8..91e8d97c82 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1477,7 +1477,17 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { if (isMyAvatar() && Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON) { // Dont display body - } else if (_head.getFace().isFullFrame()) { + return; + } + + // glow when moving + const float MIN_GLOW_SPEED = 0.01f; + bool glowing = _speed > MIN_GLOW_SPEED; + if (glowing) { + Application::getInstance()->getGlowEffect()->begin(); + } + + if (_head.getFace().isFullFrame()) { // Render the full-frame video float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { @@ -1558,6 +1568,10 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { } } _hand.render(lookingInMirror); + + if (glowing) { + Application::getInstance()->getGlowEffect()->end(); + } } diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index e35896c975..782bda2a06 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -15,7 +15,7 @@ #include "ProgramObject.h" #include "RenderUtil.h" -GlowEffect::GlowEffect() : _renderMode(DIFFUSE_ADD_MODE), _isOddFrame(false) { +GlowEffect::GlowEffect() : _renderMode(DIFFUSE_ADD_MODE), _isOddFrame(false), _intensity(0.0f) { } QOpenGLFramebufferObject* GlowEffect::getFreeFramebufferObject() const { @@ -70,12 +70,14 @@ void GlowEffect::prepare() { } void GlowEffect::begin(float intensity) { - glBlendColor(0.0f, 0.0f, 0.0f, intensity); + // store the current intensity and add the new amount + _intensityStack.push(_intensity); + glBlendColor(0.0f, 0.0f, 0.0f, _intensity += intensity); _isEmpty = false; } void GlowEffect::end() { - glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + glBlendColor(0.0f, 0.0f, 0.0f, _intensity = _intensityStack.pop()); } static void maybeBind(QOpenGLFramebufferObject* fbo) { diff --git a/interface/src/renderer/GlowEffect.h b/interface/src/renderer/GlowEffect.h index 2cf1efe9d7..81acd0c108 100644 --- a/interface/src/renderer/GlowEffect.h +++ b/interface/src/renderer/GlowEffect.h @@ -10,6 +10,7 @@ #define __interface__GlowEffect__ #include +#include class QOpenGLFramebufferObject; @@ -63,6 +64,9 @@ private: bool _isEmpty; ///< set when nothing in the scene is currently glowing bool _isOddFrame; ///< controls the alternation between texture targets in diffuse add mode + + float _intensity; + QStack _intensityStack; }; #endif /* defined(__interface__GlowEffect__) */ From 533a8ff31fd93a6c0ab01605d49a93fd8aeb7daf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Aug 2013 11:13:17 -0700 Subject: [PATCH 02/31] moved voxel sending into a GenericThread derived class/object --- voxel-server/src/VoxelSendThread.cpp | 309 ++++++++++++++++++++++++++ voxel-server/src/VoxelSendThread.h | 38 ++++ voxel-server/src/main.cpp | 311 ++------------------------- 3 files changed, 362 insertions(+), 296 deletions(-) create mode 100644 voxel-server/src/VoxelSendThread.cpp create mode 100644 voxel-server/src/VoxelSendThread.h diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp new file mode 100644 index 0000000000..d0c9503be7 --- /dev/null +++ b/voxel-server/src/VoxelSendThread.cpp @@ -0,0 +1,309 @@ +// +// VoxelSendThread.cpp +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded voxel persistance +// + +#include +#include +#include + +#include "VoxelSendThread.h" +#include "VoxelServer.h" + +VoxelSendThread::VoxelSendThread() { +} + +bool VoxelSendThread::process() { + + NodeList* nodeList = NodeList::getInstance(); + timeval lastSendTime; + + gettimeofday(&lastSendTime, NULL); + + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); + + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (::debugVoxelSending) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); + } + } + + // dynamically sleep until we need to fire off the next set of voxels + int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + if (::debugVoxelSending) { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + + return isStillRunning(); // keep running till they terminate us +} + + +void VoxelSendThread::handlePacketSend(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + int& trueBytesSent, int& truePacketsSent) { + // If we've got a stats message ready to send, then see if we can piggyback them together + if (nodeData->stats.isReadyToSend()) { + // Send the stats message to the client + unsigned char* statsMessage = nodeData->stats.getStatsMessage(); + int statsMessageLength = nodeData->stats.getStatsMessageLength(); + + // If the size of the stats message and the voxel message will fit in a packet, then piggyback them + if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { + + // copy voxel message to back of stats message + memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); + statsMessageLength += nodeData->getPacketLength(); + + // actually send it + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + } else { + // not enough room in the packet, send two packets + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + } else { + // just send the voxel packet + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + // remember to track our stats + nodeData->stats.packetSent(nodeData->getPacketLength()); + trueBytesSent += nodeData->getPacketLength(); + truePacketsSent++; + nodeData->resetVoxelPacket(); +} + +/// Version of voxel distributor that sends the deepest LOD level at once +void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + bool viewFrustumChanged) { + + pthread_mutex_lock(&::treeLock); + + int truePacketsSent = 0; + int trueBytesSent = 0; + + // FOR NOW... node tells us if it wants to receive only view frustum deltas + bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); + + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. + bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); + + // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color + // then let's just send that waiting packet. + if (wantColor != nodeData->getCurrentPacketIsColor()) { + + if (nodeData->isPacketWaiting()) { + if (::debugVoxelSending) { + printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + + } else { + if (::debugVoxelSending) { + printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + nodeData->resetVoxelPacket(); + } + } + + if (::debugVoxelSending) { + printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); + } + + const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; + + if (::debugVoxelSending) { + printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + debug::valueOf(nodeData->getViewSent()) + ); + } + + // If the current view frustum has changed OR we have nothing to send, then search against + // the current view frustum for things to send. + if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { + uint64_t now = usecTimestampNow(); + if (::debugVoxelSending) { + printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); + if (nodeData->getLastTimeBagEmpty() > 0) { + float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; + if (viewFrustumChanged) { + printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + } else { + printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + } + printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + debug::valueOf(wantColor)); + } + } + + // if our view has changed, we need to reset these things... + if (viewFrustumChanged) { + if (::dumpVoxelsOnMove) { + nodeData->nodeBag.deleteAll(); + } + nodeData->map.erase(); + } + + if (!viewFrustumChanged && !nodeData->getWantDelta()) { + // only set our last sent time if we weren't resetting due to frustum change + uint64_t now = usecTimestampNow(); + nodeData->setLastTimeBagEmpty(now); + } + + nodeData->stats.sceneCompleted(); + + if (::displayVoxelStats) { + nodeData->stats.printDebugDetails(); + } + + // start tracking our stats + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); + + // If we're starting a full scene, then definitely we want to empty the nodeBag + if (isFullScene) { + nodeData->nodeBag.deleteAll(); + } + nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); + + // This is the start of "resending" the scene. + nodeData->nodeBag.insert(serverTree.rootNode); + } + + // If we have something in our nodeBag, then turn them into packets and send them out... + if (!nodeData->nodeBag.isEmpty()) { + static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static + int bytesWritten = 0; + int packetsSentThisInterval = 0; + uint64_t start = usecTimestampNow(); + + bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); + while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { + // Check to see if we're taking too long, and if so bail early... + uint64_t now = usecTimestampNow(); + long elapsedUsec = (now - start); + long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); + long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); + + if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { + if (::debugVoxelSending) { + printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", + usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, + nodeData->nodeBag.count()); + } + break; + } + + if (!nodeData->nodeBag.isEmpty()) { + VoxelNode* subTree = nodeData->nodeBag.extract(); + bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); + CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; + int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; + + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging(); + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, + wantOcclusionCulling, coverageMap, boundaryLevelAdjust, + nodeData->getLastTimeBagEmpty(), + isFullScene, &nodeData->stats, ::jurisdiction); + + nodeData->stats.encodeStarted(); + bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, + nodeData->nodeBag, params); + nodeData->stats.encodeStopped(); + + if (nodeData->getAvailable() >= bytesWritten) { + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } else { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval++; + nodeData->resetVoxelPacket(); + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } + } else { + if (nodeData->isPacketWaiting()) { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + nodeData->resetVoxelPacket(); + } + packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left + } + } + // send the environment packet + if (shouldSendEnvironments) { + int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); + + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); + } + + nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); + trueBytesSent += envPacketLength; + truePacketsSent++; + } + + uint64_t end = usecTimestampNow(); + int elapsedmsec = (end - start)/1000; + if (elapsedmsec > 100) { + if (elapsedmsec > 1000) { + int elapsedsec = (end - start)/1000000; + printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } else { + printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + } else if (::debugVoxelSending) { + printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all + // the voxels from the current view frustum + if (nodeData->nodeBag.isEmpty()) { + nodeData->updateLastKnownViewFrustum(); + nodeData->setViewSent(true); + if (::debugVoxelSending) { + nodeData->map.printStats(); + } + nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes + } + + } // end if bag wasn't empty, and so we sent stuff... + + pthread_mutex_unlock(&::treeLock); +} + diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h new file mode 100644 index 0000000000..568659982b --- /dev/null +++ b/voxel-server/src/VoxelSendThread.h @@ -0,0 +1,38 @@ +// +// VoxelSendThread.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded object for sending voxels to a client +// + +#ifndef __voxel_server__VoxelSendThread__ +#define __voxel_server__VoxelSendThread__ + +#include +#include +#include +#include +#include "VoxelNodeData.h" + +/// Threaded processor for sending voxel packets to a single client +class VoxelSendThread : public virtual GenericThread { +public: + VoxelSendThread(); +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + + void handlePacketSend(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, + int& trueBytesSent, int& truePacketsSent); + + void deepestLevelVoxelDistributor(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, + bool viewFrustumChanged); + +}; + +#endif // __voxel_server__VoxelSendThread__ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 859eb21294..493c52845f 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -24,6 +24,7 @@ #include #include "VoxelPersistThread.h" +#include "VoxelSendThread.h" #include "VoxelServerPacketProcessor.h" #ifdef _WIN32 @@ -57,299 +58,10 @@ JurisdictionMap* jurisdiction = NULL; JurisdictionSender* jurisdictionSender = NULL; VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; VoxelPersistThread* voxelPersistThread = NULL; +VoxelSendThread* voxelSendThread = NULL; pthread_mutex_t treeLock; -void handlePacketSend(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent) { - // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { - // Send the stats message to the client - unsigned char* statsMessage = nodeData->stats.getStatsMessage(); - int statsMessageLength = nodeData->stats.getStatsMessageLength(); - - // If the size of the stats message and the voxel message will fit in a packet, then piggyback them - if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { - - // copy voxel message to back of stats message - memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); - statsMessageLength += nodeData->getPacketLength(); - - // actually send it - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - } else { - // not enough room in the packet, send two packets - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - } else { - // just send the voxel packet - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - // remember to track our stats - nodeData->stats.packetSent(nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - nodeData->resetVoxelPacket(); -} - -// Version of voxel distributor that sends the deepest LOD level at once -void deepestLevelVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - bool viewFrustumChanged) { - - pthread_mutex_lock(&::treeLock); - - int truePacketsSent = 0; - int trueBytesSent = 0; - - // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); - - // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. - if (wantColor != nodeData->getCurrentPacketIsColor()) { - - if (nodeData->isPacketWaiting()) { - if (::debugVoxelSending) { - printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - - } else { - if (::debugVoxelSending) { - printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - nodeData->resetVoxelPacket(); - } - } - - if (::debugVoxelSending) { - printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - - if (::debugVoxelSending) { - printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - - // If the current view frustum has changed OR we have nothing to send, then search against - // the current view frustum for things to send. - if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); - if (::debugVoxelSending) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } - - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (::dumpVoxelsOnMove) { - nodeData->nodeBag.deleteAll(); - } - nodeData->map.erase(); - } - - if (!viewFrustumChanged && !nodeData->getWantDelta()) { - // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); - nodeData->setLastTimeBagEmpty(now); - } - - nodeData->stats.sceneCompleted(); - - if (::displayVoxelStats) { - nodeData->stats.printDebugDetails(); - } - - // start tracking our stats - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); - - // If we're starting a full scene, then definitely we want to empty the nodeBag - if (isFullScene) { - nodeData->nodeBag.deleteAll(); - } - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); - - // This is the start of "resending" the scene. - nodeData->nodeBag.insert(serverTree.rootNode); - } - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static - int bytesWritten = 0; - int packetsSentThisInterval = 0; - uint64_t start = usecTimestampNow(); - - bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { - // Check to see if we're taking too long, and if so bail early... - uint64_t now = usecTimestampNow(); - long elapsedUsec = (now - start); - long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); - long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); - - if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { - if (::debugVoxelSending) { - printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", - usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, - nodeData->nodeBag.count()); - } - break; - } - - if (!nodeData->nodeBag.isEmpty()) { - VoxelNode* subTree = nodeData->nodeBag.extract(); - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; - - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, - WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, - nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats, ::jurisdiction); - - nodeData->stats.encodeStarted(); - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - nodeData->nodeBag, params); - nodeData->stats.encodeStopped(); - - if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } else { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - packetsSentThisInterval++; - nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } - } else { - if (nodeData->isPacketWaiting()) { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - nodeData->resetVoxelPacket(); - } - packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left - } - } - // send the environment packet - if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); - } - - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); - trueBytesSent += envPacketLength; - truePacketsSent++; - } - - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (::debugVoxelSending) { - printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the voxels from the current view frustum - if (nodeData->nodeBag.isEmpty()) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - if (::debugVoxelSending) { - nodeData->map.printStats(); - } - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes - } - - } // end if bag wasn't empty, and so we sent stuff... - - pthread_mutex_unlock(&::treeLock); -} - -void* distributeVoxelsToListeners(void* args) { - - NodeList* nodeList = NodeList::getInstance(); - timeval lastSendTime; - - while (true) { - gettimeofday(&lastSendTime, NULL); - - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); - - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (::debugVoxelSending) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); - } - } - - // dynamically sleep until we need to fire off the next set of voxels - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); - - if (usecToSleep > 0) { - usleep(usecToSleep); - } else { - if (::debugVoxelSending) { - std::cout << "Last send took too much time, not sleeping!\n"; - } - } - } - - pthread_exit(0); -} - void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { newNode->setLinkedData(new VoxelNodeData(newNode)); @@ -532,9 +244,12 @@ int main(int argc, const char * argv[]) { environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue - - pthread_t sendVoxelThread; - pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL); + + // Create voxel sending thread... + ::voxelSendThread = new VoxelSendThread(); + if (::voxelSendThread) { + ::voxelSendThread->initialize(true); + } sockaddr senderAddress; @@ -597,9 +312,6 @@ int main(int argc, const char * argv[]) { } } - pthread_join(sendVoxelThread, NULL); - pthread_mutex_destroy(&::treeLock); - if (::jurisdiction) { delete ::jurisdiction; } @@ -618,6 +330,13 @@ int main(int argc, const char * argv[]) { ::voxelPersistThread->terminate(); delete ::voxelPersistThread; } + + if (::voxelSendThread) { + ::voxelSendThread->terminate(); + delete ::voxelSendThread; + } + + pthread_mutex_destroy(&::treeLock); return 0; } From f5cb76011029bc6d8266c7a93d5c686769614fd5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Aug 2013 11:16:51 -0700 Subject: [PATCH 03/31] Revert "moved voxel sending into a GenericThread derived class/object" This reverts commit 533a8ff31fd93a6c0ab01605d49a93fd8aeb7daf. --- voxel-server/src/VoxelSendThread.cpp | 309 -------------------------- voxel-server/src/VoxelSendThread.h | 38 ---- voxel-server/src/main.cpp | 311 +++++++++++++++++++++++++-- 3 files changed, 296 insertions(+), 362 deletions(-) delete mode 100644 voxel-server/src/VoxelSendThread.cpp delete mode 100644 voxel-server/src/VoxelSendThread.h diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp deleted file mode 100644 index d0c9503be7..0000000000 --- a/voxel-server/src/VoxelSendThread.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// -// VoxelSendThread.cpp -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded voxel persistance -// - -#include -#include -#include - -#include "VoxelSendThread.h" -#include "VoxelServer.h" - -VoxelSendThread::VoxelSendThread() { -} - -bool VoxelSendThread::process() { - - NodeList* nodeList = NodeList::getInstance(); - timeval lastSendTime; - - gettimeofday(&lastSendTime, NULL); - - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); - - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (::debugVoxelSending) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); - } - } - - // dynamically sleep until we need to fire off the next set of voxels - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); - - if (usecToSleep > 0) { - usleep(usecToSleep); - } else { - if (::debugVoxelSending) { - std::cout << "Last send took too much time, not sleeping!\n"; - } - } - - return isStillRunning(); // keep running till they terminate us -} - - -void VoxelSendThread::handlePacketSend(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent) { - // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { - // Send the stats message to the client - unsigned char* statsMessage = nodeData->stats.getStatsMessage(); - int statsMessageLength = nodeData->stats.getStatsMessageLength(); - - // If the size of the stats message and the voxel message will fit in a packet, then piggyback them - if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { - - // copy voxel message to back of stats message - memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); - statsMessageLength += nodeData->getPacketLength(); - - // actually send it - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - } else { - // not enough room in the packet, send two packets - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - } else { - // just send the voxel packet - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - // remember to track our stats - nodeData->stats.packetSent(nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - nodeData->resetVoxelPacket(); -} - -/// Version of voxel distributor that sends the deepest LOD level at once -void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - bool viewFrustumChanged) { - - pthread_mutex_lock(&::treeLock); - - int truePacketsSent = 0; - int trueBytesSent = 0; - - // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); - - // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. - if (wantColor != nodeData->getCurrentPacketIsColor()) { - - if (nodeData->isPacketWaiting()) { - if (::debugVoxelSending) { - printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - - } else { - if (::debugVoxelSending) { - printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - nodeData->resetVoxelPacket(); - } - } - - if (::debugVoxelSending) { - printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - - if (::debugVoxelSending) { - printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - - // If the current view frustum has changed OR we have nothing to send, then search against - // the current view frustum for things to send. - if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); - if (::debugVoxelSending) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } - - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (::dumpVoxelsOnMove) { - nodeData->nodeBag.deleteAll(); - } - nodeData->map.erase(); - } - - if (!viewFrustumChanged && !nodeData->getWantDelta()) { - // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); - nodeData->setLastTimeBagEmpty(now); - } - - nodeData->stats.sceneCompleted(); - - if (::displayVoxelStats) { - nodeData->stats.printDebugDetails(); - } - - // start tracking our stats - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); - - // If we're starting a full scene, then definitely we want to empty the nodeBag - if (isFullScene) { - nodeData->nodeBag.deleteAll(); - } - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); - - // This is the start of "resending" the scene. - nodeData->nodeBag.insert(serverTree.rootNode); - } - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static - int bytesWritten = 0; - int packetsSentThisInterval = 0; - uint64_t start = usecTimestampNow(); - - bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { - // Check to see if we're taking too long, and if so bail early... - uint64_t now = usecTimestampNow(); - long elapsedUsec = (now - start); - long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); - long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); - - if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { - if (::debugVoxelSending) { - printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", - usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, - nodeData->nodeBag.count()); - } - break; - } - - if (!nodeData->nodeBag.isEmpty()) { - VoxelNode* subTree = nodeData->nodeBag.extract(); - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; - - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, - WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, - nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats, ::jurisdiction); - - nodeData->stats.encodeStarted(); - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - nodeData->nodeBag, params); - nodeData->stats.encodeStopped(); - - if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } else { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - packetsSentThisInterval++; - nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } - } else { - if (nodeData->isPacketWaiting()) { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - nodeData->resetVoxelPacket(); - } - packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left - } - } - // send the environment packet - if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); - } - - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); - trueBytesSent += envPacketLength; - truePacketsSent++; - } - - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (::debugVoxelSending) { - printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the voxels from the current view frustum - if (nodeData->nodeBag.isEmpty()) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - if (::debugVoxelSending) { - nodeData->map.printStats(); - } - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes - } - - } // end if bag wasn't empty, and so we sent stuff... - - pthread_mutex_unlock(&::treeLock); -} - diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h deleted file mode 100644 index 568659982b..0000000000 --- a/voxel-server/src/VoxelSendThread.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// VoxelSendThread.h -// voxel-server -// -// Created by Brad Hefta-Gaub on 8/21/13 -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// Threaded or non-threaded object for sending voxels to a client -// - -#ifndef __voxel_server__VoxelSendThread__ -#define __voxel_server__VoxelSendThread__ - -#include -#include -#include -#include -#include "VoxelNodeData.h" - -/// Threaded processor for sending voxel packets to a single client -class VoxelSendThread : public virtual GenericThread { -public: - VoxelSendThread(); -protected: - /// Implements generic processing behavior for this thread. - virtual bool process(); - -private: - - void handlePacketSend(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent); - - void deepestLevelVoxelDistributor(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, - bool viewFrustumChanged); - -}; - -#endif // __voxel_server__VoxelSendThread__ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 493c52845f..859eb21294 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -24,7 +24,6 @@ #include #include "VoxelPersistThread.h" -#include "VoxelSendThread.h" #include "VoxelServerPacketProcessor.h" #ifdef _WIN32 @@ -58,10 +57,299 @@ JurisdictionMap* jurisdiction = NULL; JurisdictionSender* jurisdictionSender = NULL; VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; VoxelPersistThread* voxelPersistThread = NULL; -VoxelSendThread* voxelSendThread = NULL; pthread_mutex_t treeLock; +void handlePacketSend(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + int& trueBytesSent, int& truePacketsSent) { + // If we've got a stats message ready to send, then see if we can piggyback them together + if (nodeData->stats.isReadyToSend()) { + // Send the stats message to the client + unsigned char* statsMessage = nodeData->stats.getStatsMessage(); + int statsMessageLength = nodeData->stats.getStatsMessageLength(); + + // If the size of the stats message and the voxel message will fit in a packet, then piggyback them + if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { + + // copy voxel message to back of stats message + memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); + statsMessageLength += nodeData->getPacketLength(); + + // actually send it + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + } else { + // not enough room in the packet, send two packets + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + } else { + // just send the voxel packet + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + // remember to track our stats + nodeData->stats.packetSent(nodeData->getPacketLength()); + trueBytesSent += nodeData->getPacketLength(); + truePacketsSent++; + nodeData->resetVoxelPacket(); +} + +// Version of voxel distributor that sends the deepest LOD level at once +void deepestLevelVoxelDistributor(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + bool viewFrustumChanged) { + + pthread_mutex_lock(&::treeLock); + + int truePacketsSent = 0; + int trueBytesSent = 0; + + // FOR NOW... node tells us if it wants to receive only view frustum deltas + bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); + + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. + bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); + + // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color + // then let's just send that waiting packet. + if (wantColor != nodeData->getCurrentPacketIsColor()) { + + if (nodeData->isPacketWaiting()) { + if (::debugVoxelSending) { + printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + + } else { + if (::debugVoxelSending) { + printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + nodeData->resetVoxelPacket(); + } + } + + if (::debugVoxelSending) { + printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); + } + + const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; + + if (::debugVoxelSending) { + printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + debug::valueOf(nodeData->getViewSent()) + ); + } + + // If the current view frustum has changed OR we have nothing to send, then search against + // the current view frustum for things to send. + if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { + uint64_t now = usecTimestampNow(); + if (::debugVoxelSending) { + printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); + if (nodeData->getLastTimeBagEmpty() > 0) { + float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; + if (viewFrustumChanged) { + printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + } else { + printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + } + printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + debug::valueOf(wantColor)); + } + } + + // if our view has changed, we need to reset these things... + if (viewFrustumChanged) { + if (::dumpVoxelsOnMove) { + nodeData->nodeBag.deleteAll(); + } + nodeData->map.erase(); + } + + if (!viewFrustumChanged && !nodeData->getWantDelta()) { + // only set our last sent time if we weren't resetting due to frustum change + uint64_t now = usecTimestampNow(); + nodeData->setLastTimeBagEmpty(now); + } + + nodeData->stats.sceneCompleted(); + + if (::displayVoxelStats) { + nodeData->stats.printDebugDetails(); + } + + // start tracking our stats + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); + + // If we're starting a full scene, then definitely we want to empty the nodeBag + if (isFullScene) { + nodeData->nodeBag.deleteAll(); + } + nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); + + // This is the start of "resending" the scene. + nodeData->nodeBag.insert(serverTree.rootNode); + } + + // If we have something in our nodeBag, then turn them into packets and send them out... + if (!nodeData->nodeBag.isEmpty()) { + static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static + int bytesWritten = 0; + int packetsSentThisInterval = 0; + uint64_t start = usecTimestampNow(); + + bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); + while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { + // Check to see if we're taking too long, and if so bail early... + uint64_t now = usecTimestampNow(); + long elapsedUsec = (now - start); + long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); + long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); + + if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { + if (::debugVoxelSending) { + printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", + usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, + nodeData->nodeBag.count()); + } + break; + } + + if (!nodeData->nodeBag.isEmpty()) { + VoxelNode* subTree = nodeData->nodeBag.extract(); + bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); + CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; + int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; + + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging(); + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, + wantOcclusionCulling, coverageMap, boundaryLevelAdjust, + nodeData->getLastTimeBagEmpty(), + isFullScene, &nodeData->stats, ::jurisdiction); + + nodeData->stats.encodeStarted(); + bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, + nodeData->nodeBag, params); + nodeData->stats.encodeStopped(); + + if (nodeData->getAvailable() >= bytesWritten) { + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } else { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval++; + nodeData->resetVoxelPacket(); + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } + } else { + if (nodeData->isPacketWaiting()) { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + nodeData->resetVoxelPacket(); + } + packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left + } + } + // send the environment packet + if (shouldSendEnvironments) { + int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); + + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); + } + + nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); + trueBytesSent += envPacketLength; + truePacketsSent++; + } + + uint64_t end = usecTimestampNow(); + int elapsedmsec = (end - start)/1000; + if (elapsedmsec > 100) { + if (elapsedmsec > 1000) { + int elapsedsec = (end - start)/1000000; + printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } else { + printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + } else if (::debugVoxelSending) { + printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all + // the voxels from the current view frustum + if (nodeData->nodeBag.isEmpty()) { + nodeData->updateLastKnownViewFrustum(); + nodeData->setViewSent(true); + if (::debugVoxelSending) { + nodeData->map.printStats(); + } + nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes + } + + } // end if bag wasn't empty, and so we sent stuff... + + pthread_mutex_unlock(&::treeLock); +} + +void* distributeVoxelsToListeners(void* args) { + + NodeList* nodeList = NodeList::getInstance(); + timeval lastSendTime; + + while (true) { + gettimeofday(&lastSendTime, NULL); + + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); + + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (::debugVoxelSending) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); + } + } + + // dynamically sleep until we need to fire off the next set of voxels + int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + if (::debugVoxelSending) { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + } + + pthread_exit(0); +} + void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { newNode->setLinkedData(new VoxelNodeData(newNode)); @@ -244,12 +532,9 @@ int main(int argc, const char * argv[]) { environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue - - // Create voxel sending thread... - ::voxelSendThread = new VoxelSendThread(); - if (::voxelSendThread) { - ::voxelSendThread->initialize(true); - } + + pthread_t sendVoxelThread; + pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL); sockaddr senderAddress; @@ -312,6 +597,9 @@ int main(int argc, const char * argv[]) { } } + pthread_join(sendVoxelThread, NULL); + pthread_mutex_destroy(&::treeLock); + if (::jurisdiction) { delete ::jurisdiction; } @@ -330,13 +618,6 @@ int main(int argc, const char * argv[]) { ::voxelPersistThread->terminate(); delete ::voxelPersistThread; } - - if (::voxelSendThread) { - ::voxelSendThread->terminate(); - delete ::voxelSendThread; - } - - pthread_mutex_destroy(&::treeLock); return 0; } From 07a2bedfb00e2e94e54e54309d1d9e7671d08d2b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Aug 2013 11:13:17 -0700 Subject: [PATCH 04/31] moved voxel sending into a GenericThread derived class/object --- voxel-server/src/VoxelSendThread.cpp | 309 ++++++++++++++++++++++++++ voxel-server/src/VoxelSendThread.h | 38 ++++ voxel-server/src/main.cpp | 311 ++------------------------- 3 files changed, 362 insertions(+), 296 deletions(-) create mode 100644 voxel-server/src/VoxelSendThread.cpp create mode 100644 voxel-server/src/VoxelSendThread.h diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp new file mode 100644 index 0000000000..d0c9503be7 --- /dev/null +++ b/voxel-server/src/VoxelSendThread.cpp @@ -0,0 +1,309 @@ +// +// VoxelSendThread.cpp +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded voxel persistance +// + +#include +#include +#include + +#include "VoxelSendThread.h" +#include "VoxelServer.h" + +VoxelSendThread::VoxelSendThread() { +} + +bool VoxelSendThread::process() { + + NodeList* nodeList = NodeList::getInstance(); + timeval lastSendTime; + + gettimeofday(&lastSendTime, NULL); + + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); + + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (::debugVoxelSending) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); + } + } + + // dynamically sleep until we need to fire off the next set of voxels + int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + + if (usecToSleep > 0) { + usleep(usecToSleep); + } else { + if (::debugVoxelSending) { + std::cout << "Last send took too much time, not sleeping!\n"; + } + } + + return isStillRunning(); // keep running till they terminate us +} + + +void VoxelSendThread::handlePacketSend(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + int& trueBytesSent, int& truePacketsSent) { + // If we've got a stats message ready to send, then see if we can piggyback them together + if (nodeData->stats.isReadyToSend()) { + // Send the stats message to the client + unsigned char* statsMessage = nodeData->stats.getStatsMessage(); + int statsMessageLength = nodeData->stats.getStatsMessageLength(); + + // If the size of the stats message and the voxel message will fit in a packet, then piggyback them + if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { + + // copy voxel message to back of stats message + memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); + statsMessageLength += nodeData->getPacketLength(); + + // actually send it + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + } else { + // not enough room in the packet, send two packets + nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + } else { + // just send the voxel packet + nodeList->getNodeSocket()->send(node->getActiveSocket(), + nodeData->getPacket(), nodeData->getPacketLength()); + } + // remember to track our stats + nodeData->stats.packetSent(nodeData->getPacketLength()); + trueBytesSent += nodeData->getPacketLength(); + truePacketsSent++; + nodeData->resetVoxelPacket(); +} + +/// Version of voxel distributor that sends the deepest LOD level at once +void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, + NodeList::iterator& node, + VoxelNodeData* nodeData, + bool viewFrustumChanged) { + + pthread_mutex_lock(&::treeLock); + + int truePacketsSent = 0; + int trueBytesSent = 0; + + // FOR NOW... node tells us if it wants to receive only view frustum deltas + bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); + + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // the clients requested color state. + bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); + + // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color + // then let's just send that waiting packet. + if (wantColor != nodeData->getCurrentPacketIsColor()) { + + if (nodeData->isPacketWaiting()) { + if (::debugVoxelSending) { + printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + + } else { + if (::debugVoxelSending) { + printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); + } + nodeData->resetVoxelPacket(); + } + } + + if (::debugVoxelSending) { + printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); + } + + const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; + + if (::debugVoxelSending) { + printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + debug::valueOf(nodeData->getViewSent()) + ); + } + + // If the current view frustum has changed OR we have nothing to send, then search against + // the current view frustum for things to send. + if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { + uint64_t now = usecTimestampNow(); + if (::debugVoxelSending) { + printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); + if (nodeData->getLastTimeBagEmpty() > 0) { + float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; + if (viewFrustumChanged) { + printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + } else { + printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + } + printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + debug::valueOf(wantColor)); + } + } + + // if our view has changed, we need to reset these things... + if (viewFrustumChanged) { + if (::dumpVoxelsOnMove) { + nodeData->nodeBag.deleteAll(); + } + nodeData->map.erase(); + } + + if (!viewFrustumChanged && !nodeData->getWantDelta()) { + // only set our last sent time if we weren't resetting due to frustum change + uint64_t now = usecTimestampNow(); + nodeData->setLastTimeBagEmpty(now); + } + + nodeData->stats.sceneCompleted(); + + if (::displayVoxelStats) { + nodeData->stats.printDebugDetails(); + } + + // start tracking our stats + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); + + // If we're starting a full scene, then definitely we want to empty the nodeBag + if (isFullScene) { + nodeData->nodeBag.deleteAll(); + } + nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); + + // This is the start of "resending" the scene. + nodeData->nodeBag.insert(serverTree.rootNode); + } + + // If we have something in our nodeBag, then turn them into packets and send them out... + if (!nodeData->nodeBag.isEmpty()) { + static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static + int bytesWritten = 0; + int packetsSentThisInterval = 0; + uint64_t start = usecTimestampNow(); + + bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); + while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { + // Check to see if we're taking too long, and if so bail early... + uint64_t now = usecTimestampNow(); + long elapsedUsec = (now - start); + long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); + long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); + + if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { + if (::debugVoxelSending) { + printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", + usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, + nodeData->nodeBag.count()); + } + break; + } + + if (!nodeData->nodeBag.isEmpty()) { + VoxelNode* subTree = nodeData->nodeBag.extract(); + bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); + CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; + int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; + + bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging(); + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, + wantOcclusionCulling, coverageMap, boundaryLevelAdjust, + nodeData->getLastTimeBagEmpty(), + isFullScene, &nodeData->stats, ::jurisdiction); + + nodeData->stats.encodeStarted(); + bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, + nodeData->nodeBag, params); + nodeData->stats.encodeStopped(); + + if (nodeData->getAvailable() >= bytesWritten) { + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } else { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval++; + nodeData->resetVoxelPacket(); + nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + } + } else { + if (nodeData->isPacketWaiting()) { + handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + nodeData->resetVoxelPacket(); + } + packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left + } + } + // send the environment packet + if (shouldSendEnvironments) { + int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int envPacketLength = numBytesPacketHeader; + int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); + + for (int i = 0; i < environmentsToSend; i++) { + envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); + } + + nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); + trueBytesSent += envPacketLength; + truePacketsSent++; + } + + uint64_t end = usecTimestampNow(); + int elapsedmsec = (end - start)/1000; + if (elapsedmsec > 100) { + if (elapsedmsec > 1000) { + int elapsedsec = (end - start)/1000000; + printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } else { + printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + } else if (::debugVoxelSending) { + printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + } + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all + // the voxels from the current view frustum + if (nodeData->nodeBag.isEmpty()) { + nodeData->updateLastKnownViewFrustum(); + nodeData->setViewSent(true); + if (::debugVoxelSending) { + nodeData->map.printStats(); + } + nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes + } + + } // end if bag wasn't empty, and so we sent stuff... + + pthread_mutex_unlock(&::treeLock); +} + diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h new file mode 100644 index 0000000000..568659982b --- /dev/null +++ b/voxel-server/src/VoxelSendThread.h @@ -0,0 +1,38 @@ +// +// VoxelSendThread.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded object for sending voxels to a client +// + +#ifndef __voxel_server__VoxelSendThread__ +#define __voxel_server__VoxelSendThread__ + +#include +#include +#include +#include +#include "VoxelNodeData.h" + +/// Threaded processor for sending voxel packets to a single client +class VoxelSendThread : public virtual GenericThread { +public: + VoxelSendThread(); +protected: + /// Implements generic processing behavior for this thread. + virtual bool process(); + +private: + + void handlePacketSend(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, + int& trueBytesSent, int& truePacketsSent); + + void deepestLevelVoxelDistributor(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, + bool viewFrustumChanged); + +}; + +#endif // __voxel_server__VoxelSendThread__ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 859eb21294..493c52845f 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -24,6 +24,7 @@ #include #include "VoxelPersistThread.h" +#include "VoxelSendThread.h" #include "VoxelServerPacketProcessor.h" #ifdef _WIN32 @@ -57,299 +58,10 @@ JurisdictionMap* jurisdiction = NULL; JurisdictionSender* jurisdictionSender = NULL; VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; VoxelPersistThread* voxelPersistThread = NULL; +VoxelSendThread* voxelSendThread = NULL; pthread_mutex_t treeLock; -void handlePacketSend(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent) { - // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { - // Send the stats message to the client - unsigned char* statsMessage = nodeData->stats.getStatsMessage(); - int statsMessageLength = nodeData->stats.getStatsMessageLength(); - - // If the size of the stats message and the voxel message will fit in a packet, then piggyback them - if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) { - - // copy voxel message to back of stats message - memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); - statsMessageLength += nodeData->getPacketLength(); - - // actually send it - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - } else { - // not enough room in the packet, send two packets - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - } else { - // just send the voxel packet - nodeList->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); - } - // remember to track our stats - nodeData->stats.packetSent(nodeData->getPacketLength()); - trueBytesSent += nodeData->getPacketLength(); - truePacketsSent++; - nodeData->resetVoxelPacket(); -} - -// Version of voxel distributor that sends the deepest LOD level at once -void deepestLevelVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - bool viewFrustumChanged) { - - pthread_mutex_lock(&::treeLock); - - int truePacketsSent = 0; - int trueBytesSent = 0; - - // FOR NOW... node tells us if it wants to receive only view frustum deltas - bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use - // the clients requested color state. - bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor(); - - // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. - if (wantColor != nodeData->getCurrentPacketIsColor()) { - - if (nodeData->isPacketWaiting()) { - if (::debugVoxelSending) { - printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - - } else { - if (::debugVoxelSending) { - printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); - } - nodeData->resetVoxelPacket(); - } - } - - if (::debugVoxelSending) { - printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - - if (::debugVoxelSending) { - printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - - // If the current view frustum has changed OR we have nothing to send, then search against - // the current view frustum for things to send. - if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - uint64_t now = usecTimestampNow(); - if (::debugVoxelSending) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } - - // if our view has changed, we need to reset these things... - if (viewFrustumChanged) { - if (::dumpVoxelsOnMove) { - nodeData->nodeBag.deleteAll(); - } - nodeData->map.erase(); - } - - if (!viewFrustumChanged && !nodeData->getWantDelta()) { - // only set our last sent time if we weren't resetting due to frustum change - uint64_t now = usecTimestampNow(); - nodeData->setLastTimeBagEmpty(now); - } - - nodeData->stats.sceneCompleted(); - - if (::displayVoxelStats) { - nodeData->stats.printDebugDetails(); - } - - // start tracking our stats - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); - - // If we're starting a full scene, then definitely we want to empty the nodeBag - if (isFullScene) { - nodeData->nodeBag.deleteAll(); - } - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); - - // This is the start of "resending" the scene. - nodeData->nodeBag.insert(serverTree.rootNode); - } - - // If we have something in our nodeBag, then turn them into packets and send them out... - if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static - int bytesWritten = 0; - int packetsSentThisInterval = 0; - uint64_t start = usecTimestampNow(); - - bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); - while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { - // Check to see if we're taking too long, and if so bail early... - uint64_t now = usecTimestampNow(); - long elapsedUsec = (now - start); - long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent); - long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec); - - if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) { - if (::debugVoxelSending) { - printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n", - usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket, - nodeData->nodeBag.count()); - } - break; - } - - if (!nodeData->nodeBag.isEmpty()) { - VoxelNode* subTree = nodeData->nodeBag.extract(); - bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); - CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; - - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, - WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, - nodeData->getLastTimeBagEmpty(), - isFullScene, &nodeData->stats, ::jurisdiction); - - nodeData->stats.encodeStarted(); - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, - nodeData->nodeBag, params); - nodeData->stats.encodeStopped(); - - if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } else { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - packetsSentThisInterval++; - nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); - } - } else { - if (nodeData->isPacketWaiting()) { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); - nodeData->resetVoxelPacket(); - } - packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left - } - } - // send the environment packet - if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); - } - - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); - trueBytesSent += envPacketLength; - truePacketsSent++; - } - - uint64_t end = usecTimestampNow(); - int elapsedmsec = (end - start)/1000; - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (::debugVoxelSending) { - printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all - // the voxels from the current view frustum - if (nodeData->nodeBag.isEmpty()) { - nodeData->updateLastKnownViewFrustum(); - nodeData->setViewSent(true); - if (::debugVoxelSending) { - nodeData->map.printStats(); - } - nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes - } - - } // end if bag wasn't empty, and so we sent stuff... - - pthread_mutex_unlock(&::treeLock); -} - -void* distributeVoxelsToListeners(void* args) { - - NodeList* nodeList = NodeList::getInstance(); - timeval lastSendTime; - - while (true) { - gettimeofday(&lastSendTime, NULL); - - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); - - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (::debugVoxelSending) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); - } - } - - // dynamically sleep until we need to fire off the next set of voxels - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); - - if (usecToSleep > 0) { - usleep(usecToSleep); - } else { - if (::debugVoxelSending) { - std::cout << "Last send took too much time, not sleeping!\n"; - } - } - } - - pthread_exit(0); -} - void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { newNode->setLinkedData(new VoxelNodeData(newNode)); @@ -532,9 +244,12 @@ int main(int argc, const char * argv[]) { environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue - - pthread_t sendVoxelThread; - pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL); + + // Create voxel sending thread... + ::voxelSendThread = new VoxelSendThread(); + if (::voxelSendThread) { + ::voxelSendThread->initialize(true); + } sockaddr senderAddress; @@ -597,9 +312,6 @@ int main(int argc, const char * argv[]) { } } - pthread_join(sendVoxelThread, NULL); - pthread_mutex_destroy(&::treeLock); - if (::jurisdiction) { delete ::jurisdiction; } @@ -618,6 +330,13 @@ int main(int argc, const char * argv[]) { ::voxelPersistThread->terminate(); delete ::voxelPersistThread; } + + if (::voxelSendThread) { + ::voxelSendThread->terminate(); + delete ::voxelSendThread; + } + + pthread_mutex_destroy(&::treeLock); return 0; } From 7da9556815a9df4a90167a88b6e58e4ac1de5a5f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 11:23:28 -0700 Subject: [PATCH 05/31] Glow when moving. --- interface/src/avatar/Avatar.cpp | 15 ++++++++++++++- interface/src/avatar/Avatar.h | 4 ++++ interface/src/avatar/MyAvatar.cpp | 14 ++++++++++++-- interface/src/renderer/GlowEffect.cpp | 12 +++++++++++- interface/src/renderer/GlowEffect.h | 8 ++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5f206fb6ed..39ce97c8eb 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -105,7 +105,8 @@ Avatar::Avatar(Node* owningNode) : _initialized(false), _handHoldingPosition(0.0f, 0.0f, 0.0f), _maxArmLength(0.0f), - _pelvisStandingHeight(0.0f) + _pelvisStandingHeight(0.0f), + _moving(false) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = &_head; @@ -695,6 +696,10 @@ float Avatar::getBallRenderAlpha(int ball, bool lookingInMirror) const { } void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { + + // glow when moving + Glower glower(_moving ? 1.0f : 0.0f); + if (_head.getFace().isFullFrame()) { // Render the full-frame video float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); @@ -793,6 +798,14 @@ void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, gl rotation = _bodyBall[jointID].rotation; } +int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { + // change in position implies movement + glm::vec3 oldPosition = _position; + AvatarData::parseData(sourceBuffer, numBytes); + const float MOVE_DISTANCE_THRESHOLD = 0.001f; + _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; +} + void Avatar::saveData(QSettings* set) { set->beginGroup("Avatar"); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index eb7a7851d0..4048f15d0f 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,6 +164,8 @@ public: // Get the position/rotation of a single body ball void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const; + virtual int parseData(unsigned char* sourceBuffer, int numBytes); + static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); public slots: @@ -219,6 +221,8 @@ protected: float _stringLength; AvatarVoxelSystem _voxels; + bool _moving; ///< set when position is changing + // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00898da333..175b38378d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -318,6 +318,10 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam _mode = AVATAR_MODE_INTERACTING; } + // update moving flag based on speed + const float MOVING_SPEED_THRESHOLD = 0.01f; + _moving = _speed > MOVING_SPEED_THRESHOLD; + // update position by velocity, and subtract the change added earlier for gravity _position += _velocity * deltaTime; @@ -511,7 +515,13 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON) { // Dont display body - } else if (_head.getFace().isFullFrame()) { + return; + } + + // glow when moving + Glower glower(_moving ? 1.0f : 0.0f); + + if (_head.getFace().isFullFrame()) { // Render the full-frame video float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { @@ -1032,4 +1042,4 @@ void MyAvatar::setOrientation(const glm::quat& orientation) { void MyAvatar::setNewScale(const float scale) { _newScale = scale; -} \ No newline at end of file +} diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index 782bda2a06..24936b85d3 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -73,10 +73,11 @@ void GlowEffect::begin(float intensity) { // store the current intensity and add the new amount _intensityStack.push(_intensity); glBlendColor(0.0f, 0.0f, 0.0f, _intensity += intensity); - _isEmpty = false; + _isEmpty &= (_intensity == 0.0f); } void GlowEffect::end() { + // restore the saved intensity glBlendColor(0.0f, 0.0f, 0.0f, _intensity = _intensityStack.pop()); } @@ -270,3 +271,12 @@ void GlowEffect::cycleRenderMode() { break; } } + +Glower::Glower(float amount) { + Application::getInstance()->getGlowEffect()->begin(amount); +} + +Glower::~Glower() { + Application::getInstance()->getGlowEffect()->end(); +} + diff --git a/interface/src/renderer/GlowEffect.h b/interface/src/renderer/GlowEffect.h index 81acd0c108..37765d18ba 100644 --- a/interface/src/renderer/GlowEffect.h +++ b/interface/src/renderer/GlowEffect.h @@ -69,4 +69,12 @@ private: QStack _intensityStack; }; +/// RAII-style glow handler. Applies glow when in scope. +class Glower { +public: + + Glower(float amount = 1.0f); + ~Glower(); +}; + #endif /* defined(__interface__GlowEffect__) */ From 2eaaee673f9e9c1dc0f2c71843d6c68e885bbe77 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 13:52:10 -0700 Subject: [PATCH 06/31] Argh, forgot to return the result of the superclass implementation. --- interface/src/avatar/Avatar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 39ce97c8eb..08533666f3 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -801,9 +801,10 @@ void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, gl int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; - AvatarData::parseData(sourceBuffer, numBytes); + int bytesRead = AvatarData::parseData(sourceBuffer, numBytes); const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; + return bytesRead; } void Avatar::saveData(QSettings* set) { From 9224bca88b1150fe32f1573f453078342ba12abd Mon Sep 17 00:00:00 2001 From: atlante45 Date: Thu, 22 Aug 2013 13:52:24 -0700 Subject: [PATCH 07/31] Fixed glBuffers not deleted in VoxelSystem --- interface/src/VoxelSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index b0cbcf02c3..9c31040ca5 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -115,6 +115,10 @@ void VoxelSystem::clearFreeBufferIndexes() { } VoxelSystem::~VoxelSystem() { + glDeleteBuffers(1, &_vboVerticesID); + glDeleteBuffers(1, &_vboNormalsID); + glDeleteBuffers(1, &_vboColorsID); + glDeleteBuffers(1, &_vboIndicesID); delete[] _readVerticesArray; delete[] _writeVerticesArray; delete[] _readColorsArray; From 3dfefefeae9a74e0352bed5701f27c81125a7923 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 14:00:50 -0700 Subject: [PATCH 08/31] Make the surrounding spheres glow when moving, too. --- interface/src/avatar/Avatar.cpp | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 08533666f3..9f2ac37518 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -486,22 +486,27 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) { // render a simple round on the ground projected down from the avatar's position renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f); - // render body - renderBody(lookingInMirror, renderAvatarBalls); + { + // glow when moving + Glower glower(_moving ? 1.0f : 0.0f); + + // render body + renderBody(lookingInMirror, renderAvatarBalls); - // render sphere when far away - const float MAX_ANGLE = 10.f; - glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition(); - glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f; - float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); + // render sphere when far away + const float MAX_ANGLE = 10.f; + glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition(); + glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f; + float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); - if (angle < MAX_ANGLE) { - glColor4f(0.5f, 0.8f, 0.8f, 1.f - angle / MAX_ANGLE); - glPushMatrix(); - glTranslatef(_position.x, _position.y, _position.z); - glScalef(_height / 2.f, _height / 2.f, _height / 2.f); - glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20); - glPopMatrix(); + if (angle < MAX_ANGLE) { + glColor4f(0.5f, 0.8f, 0.8f, 1.f - angle / MAX_ANGLE); + glPushMatrix(); + glTranslatef(_position.x, _position.y, _position.z); + glScalef(_height / 2.f, _height / 2.f, _height / 2.f); + glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20); + glPopMatrix(); + } } // Render the balls @@ -697,9 +702,6 @@ float Avatar::getBallRenderAlpha(int ball, bool lookingInMirror) const { void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { - // glow when moving - Glower glower(_moving ? 1.0f : 0.0f); - if (_head.getFace().isFullFrame()) { // Render the full-frame video float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); From 99321b3e096f66505d518976ab86c37a469d7778 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Thu, 22 Aug 2013 15:43:58 -0700 Subject: [PATCH 09/31] Fixed VBOs not being deleted in VoxelSystem destructor --- interface/src/VoxelSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index b0cbcf02c3..9c31040ca5 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -115,6 +115,10 @@ void VoxelSystem::clearFreeBufferIndexes() { } VoxelSystem::~VoxelSystem() { + glDeleteBuffers(1, &_vboVerticesID); + glDeleteBuffers(1, &_vboNormalsID); + glDeleteBuffers(1, &_vboColorsID); + glDeleteBuffers(1, &_vboIndicesID); delete[] _readVerticesArray; delete[] _writeVerticesArray; delete[] _readColorsArray; From 2cff4b6b3a1c293771a0fc92f1673823d7ea556a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 17:09:23 -0700 Subject: [PATCH 10/31] "Laser pointer" for transmitter. --- interface/src/Application.cpp | 59 ++++++++++++++++++++++++++++++++- interface/src/Application.h | 3 ++ interface/src/Util.cpp | 36 ++++++++++++++++---- interface/src/Util.h | 3 +- interface/src/avatar/Avatar.cpp | 15 +++++++++ interface/src/avatar/Avatar.h | 7 ++++ 6 files changed, 114 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d69770678c..de9759959a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1384,7 +1384,9 @@ Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3 if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = (Avatar *) node->getLinkedData(); glm::vec3 headPosition = avatar->getHead().getPosition(); - if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS * avatar->getScale())) { + float distance; + if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, + HEAD_SPHERE_RADIUS * avatar->getScale(), distance)) { eyePosition = avatar->getHead().getEyePosition(); _lookatIndicatorScale = avatar->getScale(); _lookatOtherPosition = headPosition; @@ -1722,6 +1724,40 @@ void Application::update(float deltaTime) { _myAvatar.simulate(deltaTime, NULL, Menu::getInstance()->getGyroCameraSensitivity()); } + // no transmitter drive implies transmitter pick + if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { + _transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position; + glm::vec3 direction = _myAvatar.getOrientation() * + glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; + + // check against voxels, avatars + const float MAX_PICK_DISTANCE = 100.0f; + float minDistance = MAX_PICK_DISTANCE; + VoxelDetail detail; + float distance; + BoxFace face; + if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) { + minDistance = min(minDistance, distance); + } + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->lock(); + if (node->getLinkedData() != NULL) { + Avatar *avatar = (Avatar*)node->getLinkedData(); + if (!avatar->isInitialized()) { + avatar->init(); + } + if (avatar->findRayIntersection(_transmitterPickStart, direction, distance)) { + minDistance = min(minDistance, distance); + } + } + node->unlock(); + } + _transmitterPickEnd = _transmitterPickStart + direction * minDistance; + + } else { + _transmitterPickStart = _transmitterPickEnd = glm::vec3(); + } + if (!OculusManager::isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { @@ -2271,6 +2307,27 @@ void Application::displaySide(Camera& whichCamera) { } renderFollowIndicator(); + + // render transmitter pick ray, if non-empty + if (_transmitterPickStart != _transmitterPickEnd) { + Glower glower; + float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; + glColor3fv(TRANSMITTER_PICK_COLOR); + glLineWidth(3.0f); + glBegin(GL_LINES); + glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z); + glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + glEnd(); + glLineWidth(1.0f); + + glPushMatrix(); + glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); + + float PICK_END_RADIUS = 0.025f; + glutSolidSphere(PICK_END_RADIUS, 8, 8); + + glPopMatrix(); + } } void Application::displayOverlay() { diff --git a/interface/src/Application.h b/interface/src/Application.h index a88ea3269f..7ad64ca707 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -298,6 +298,9 @@ private: glm::vec3 _lookatOtherPosition; float _lookatIndicatorScale; + glm::vec3 _transmitterPickStart; + glm::vec3 _transmitterPickEnd; + bool _perfStatsOn; // Do we want to display perfStats? ChatEntry _chatEntry; // chat entry field diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 743957128c..dc825ca765 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -598,13 +598,35 @@ float loadSetting(QSettings* settings, const char* name, float defaultValue) { return value; } -bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius) { - glm::vec3 vecFromRayToSphereCenter = sphereCenter - rayStarting; - double projection = glm::dot(vecFromRayToSphereCenter, rayNormalizedDirection); - double shortestDistance = sqrt(glm::dot(vecFromRayToSphereCenter, vecFromRayToSphereCenter) - projection * projection); - if (shortestDistance <= sphereRadius) { - return true; +bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, + const glm::vec3& sphereCenter, float sphereRadius, float& distance) { + glm::vec3 relativeOrigin = rayStarting - sphereCenter; + + // compute the b, c terms of the quadratic equation (a is dot(direction, direction), which is one) + float b = 2.0f * glm::dot(rayNormalizedDirection, relativeOrigin); + float c = glm::dot(relativeOrigin, relativeOrigin) - sphereRadius * sphereRadius; + + // compute the radicand of the quadratic. if less than zero, there's no intersection + float radicand = b * b - 4.0f * c; + if (radicand < 0.0f) { + return false; } + + // compute the first solution of the quadratic + float root = sqrtf(radicand); + float firstSolution = -b - root; + if (firstSolution > 0.0f) { + distance = firstSolution / 2.0f; + return true; // origin is outside the sphere + } + + // now try the second solution + float secondSolution = -b + root; + if (secondSolution > 0.0f) { + distance = 0.0f; + return true; // origin is inside the sphere + } + return false; } @@ -615,4 +637,4 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu return true; } return false; -} \ No newline at end of file +} diff --git a/interface/src/Util.h b/interface/src/Util.h index c1bb2949fa..2926d5bfc4 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -74,7 +74,8 @@ void runTimingTests(); float loadSetting(QSettings* settings, const char* name, float defaultValue); -bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius); +bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, + const glm::vec3& sphereCenter, float sphereRadius, float& distance); bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadius); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9f2ac37518..4635be7357 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -800,6 +800,21 @@ void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, gl rotation = _bodyBall[jointID].rotation; } +bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + float minDistance = FLT_MAX; + for (int i = 0; i < NUM_AVATAR_BODY_BALLS; i++) { + float distance; + if (rayIntersectsSphere(origin, direction, _bodyBall[i].position, _bodyBall[i].radius, distance)) { + minDistance = min(minDistance, distance); + } + } + if (minDistance == FLT_MAX) { + return false; + } + distance = minDistance; + return true; +} + int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 4048f15d0f..b04f9f04b8 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,6 +164,13 @@ public: // Get the position/rotation of a single body ball void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const; + /// Checks for an intersection between the described ray and any of the avatar's body balls. + /// \param origin the origin of the ray + /// \param direction the unit direction vector + /// \param[out] distance the variable in which to store the distance to intersection + /// \return whether or not the ray intersected + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + virtual int parseData(unsigned char* sourceBuffer, int numBytes); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); From 0693d0cbbdc055824d85697e1f3765053c4a52e1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Aug 2013 17:09:25 -0700 Subject: [PATCH 11/31] moved each client to its own sending thread --- voxel-server/src/NodeWatcher.cpp | 26 ++++++++++++ voxel-server/src/NodeWatcher.h | 23 +++++++++++ voxel-server/src/VoxelNodeData.cpp | 16 +++++++- voxel-server/src/VoxelNodeData.h | 4 ++ voxel-server/src/VoxelSendThread.cpp | 60 ++++++++++++++-------------- voxel-server/src/VoxelSendThread.h | 12 +++--- voxel-server/src/main.cpp | 21 ++++------ 7 files changed, 110 insertions(+), 52 deletions(-) create mode 100644 voxel-server/src/NodeWatcher.cpp create mode 100644 voxel-server/src/NodeWatcher.h diff --git a/voxel-server/src/NodeWatcher.cpp b/voxel-server/src/NodeWatcher.cpp new file mode 100644 index 0000000000..dd3244326a --- /dev/null +++ b/voxel-server/src/NodeWatcher.cpp @@ -0,0 +1,26 @@ +// +// NodeWatcher.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded object for sending voxels to a client +// + +#include +#include "NodeWatcher.h" +#include "VoxelNodeData.h" + +void NodeWatcher::nodeAdded(Node* node) { + // do nothing +} + +void NodeWatcher::nodeKilled(Node* node) { + // Use this to cleanup our node + if (node->getType() == NODE_TYPE_AGENT) { + VoxelNodeData* nodeData = (VoxelNodeData*)node->getLinkedData(); + node->setLinkedData(NULL); + delete nodeData; + } +}; diff --git a/voxel-server/src/NodeWatcher.h b/voxel-server/src/NodeWatcher.h new file mode 100644 index 0000000000..7d67397faa --- /dev/null +++ b/voxel-server/src/NodeWatcher.h @@ -0,0 +1,23 @@ +// +// NodeWatcher.h +// voxel-server +// +// Created by Brad Hefta-Gaub on 8/21/13 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// Threaded or non-threaded object for sending voxels to a client +// + +#ifndef __voxel_server__NodeWatcher__ +#define __voxel_server__NodeWatcher__ + +#include + +/// Voxel server's node watcher, which watches for nodes being killed and cleans up their data and threads +class NodeWatcher : public virtual NodeListHook { +public: + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); +}; + +#endif // __voxel_server__NodeWatcher__ diff --git a/voxel-server/src/VoxelNodeData.cpp b/voxel-server/src/VoxelNodeData.cpp index c4b8ee8b79..6d0d7d23cf 100644 --- a/voxel-server/src/VoxelNodeData.cpp +++ b/voxel-server/src/VoxelNodeData.cpp @@ -11,6 +11,7 @@ #include "VoxelNodeData.h" #include #include +#include "VoxelSendThread.h" VoxelNodeData::VoxelNodeData(Node* owningNode) : AvatarData(owningNode), @@ -21,11 +22,19 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : _lastTimeBagEmpty(0), _viewFrustumChanging(false), _viewFrustumJustStoppedChanging(true), - _currentPacketIsColor(true) + _currentPacketIsColor(true), + _voxelSendThread(NULL) { _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _voxelPacketAt = _voxelPacket; resetVoxelPacket(); + + // Create voxel sending thread... + uint16_t nodeID = getOwningNode()->getNodeID(); + _voxelSendThread = new VoxelSendThread(nodeID); + if (_voxelSendThread) { + _voxelSendThread->initialize(true); + } } @@ -49,6 +58,11 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) { VoxelNodeData::~VoxelNodeData() { delete[] _voxelPacket; + + if (_voxelSendThread) { + _voxelSendThread->terminate(); + delete _voxelSendThread; + } } bool VoxelNodeData::updateCurrentViewFrustum() { diff --git a/voxel-server/src/VoxelNodeData.h b/voxel-server/src/VoxelNodeData.h index 746db6da93..efe28243a7 100644 --- a/voxel-server/src/VoxelNodeData.h +++ b/voxel-server/src/VoxelNodeData.h @@ -18,6 +18,8 @@ #include #include +class VoxelSendThread; // forward declare + class VoxelNodeData : public AvatarData { public: VoxelNodeData(Node* owningNode); @@ -80,6 +82,8 @@ private: bool _viewFrustumChanging; bool _viewFrustumJustStoppedChanging; bool _currentPacketIsColor; + + VoxelSendThread* _voxelSendThread; }; #endif /* defined(__hifi__VoxelNodeData__) */ diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp index d0c9503be7..4c69c7f035 100644 --- a/voxel-server/src/VoxelSendThread.cpp +++ b/voxel-server/src/VoxelSendThread.cpp @@ -15,31 +15,34 @@ #include "VoxelSendThread.h" #include "VoxelServer.h" -VoxelSendThread::VoxelSendThread() { +VoxelSendThread::VoxelSendThread(uint16_t nodeID) : + _nodeID(nodeID) { +} + +VoxelSendThread::~VoxelSendThread() { } bool VoxelSendThread::process() { - - NodeList* nodeList = NodeList::getInstance(); - timeval lastSendTime; + uint64_t lastSendTime = usecTimestampNow(); - gettimeofday(&lastSendTime, NULL); + Node* node = NodeList::getInstance()->nodeWithID(_nodeID); + VoxelNodeData* nodeData = NULL; - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData(); + if (node) { + nodeData = (VoxelNodeData*) node->getLinkedData(); + } - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (::debugVoxelSending) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } - deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged); + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (::debugVoxelSending) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); } + deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); } // dynamically sleep until we need to fire off the next set of voxels - int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime)); + int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - lastSendTime); if (usecToSleep > 0) { usleep(usecToSleep); @@ -53,10 +56,8 @@ bool VoxelSendThread::process() { } -void VoxelSendThread::handlePacketSend(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent) { +void VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) { + // If we've got a stats message ready to send, then see if we can piggyback them together if (nodeData->stats.isReadyToSend()) { // Send the stats message to the client @@ -71,16 +72,16 @@ void VoxelSendThread::handlePacketSend(NodeList* nodeList, statsMessageLength += nodeData->getPacketLength(); // actually send it - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); } else { // not enough room in the packet, send two packets - nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); - nodeList->getNodeSocket()->send(node->getActiveSocket(), + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), nodeData->getPacket(), nodeData->getPacketLength()); } } else { // just send the voxel packet - nodeList->getNodeSocket()->send(node->getActiveSocket(), + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), nodeData->getPacket(), nodeData->getPacketLength()); } // remember to track our stats @@ -91,10 +92,7 @@ void VoxelSendThread::handlePacketSend(NodeList* nodeList, } /// Version of voxel distributor that sends the deepest LOD level at once -void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, - NodeList::iterator& node, - VoxelNodeData* nodeData, - bool viewFrustumChanged) { +void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { pthread_mutex_lock(&::treeLock); @@ -120,7 +118,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor())); } - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { if (::debugVoxelSending) { @@ -247,14 +245,14 @@ void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, if (nodeData->getAvailable() >= bytesWritten) { nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); } else { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval++; nodeData->resetVoxelPacket(); nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); } } else { if (nodeData->isPacketWaiting()) { - handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent); + handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); nodeData->resetVoxelPacket(); } packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left @@ -270,7 +268,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(NodeList* nodeList, envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); } - nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); trueBytesSent += envPacketLength; truePacketsSent++; } diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h index 568659982b..1d5af7b012 100644 --- a/voxel-server/src/VoxelSendThread.h +++ b/voxel-server/src/VoxelSendThread.h @@ -20,19 +20,17 @@ /// Threaded processor for sending voxel packets to a single client class VoxelSendThread : public virtual GenericThread { public: - VoxelSendThread(); + VoxelSendThread(uint16_t nodeID); + ~VoxelSendThread(); protected: /// Implements generic processing behavior for this thread. virtual bool process(); private: + uint16_t _nodeID; - void handlePacketSend(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, - int& trueBytesSent, int& truePacketsSent); - - void deepestLevelVoxelDistributor(NodeList* nodeList, NodeList::iterator& node, VoxelNodeData* nodeData, - bool viewFrustumChanged); - + void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent); + void deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged); }; #endif // __voxel_server__VoxelSendThread__ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 493c52845f..131ac9b7a2 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -23,6 +23,7 @@ #include #include +#include "NodeWatcher.h" #include "VoxelPersistThread.h" #include "VoxelSendThread.h" #include "VoxelServerPacketProcessor.h" @@ -58,9 +59,8 @@ JurisdictionMap* jurisdiction = NULL; JurisdictionSender* jurisdictionSender = NULL; VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL; VoxelPersistThread* voxelPersistThread = NULL; -VoxelSendThread* voxelSendThread = NULL; pthread_mutex_t treeLock; - +NodeWatcher nodeWatcher; // used to cleanup AGENT data when agents are killed void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { @@ -132,6 +132,9 @@ int main(int argc, const char * argv[]) { NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort); setvbuf(stdout, NULL, _IOLBF, 0); + + // tell our NodeList about our desire to get notifications + nodeList->addHook(&nodeWatcher); // Handle Local Domain testing with the --local command line const char* local = "--local"; @@ -245,12 +248,6 @@ int main(int argc, const char * argv[]) { environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue - // Create voxel sending thread... - ::voxelSendThread = new VoxelSendThread(); - if (::voxelSendThread) { - ::voxelSendThread->initialize(true); - } - sockaddr senderAddress; unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; @@ -331,11 +328,9 @@ int main(int argc, const char * argv[]) { delete ::voxelPersistThread; } - if (::voxelSendThread) { - ::voxelSendThread->terminate(); - delete ::voxelSendThread; - } - + // tell our NodeList we're done with notifications + nodeList->removeHook(&nodeWatcher); + pthread_mutex_destroy(&::treeLock); return 0; From 3682e32eb15ae4cf508a741a67690022e52b5d33 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Aug 2013 17:40:55 -0700 Subject: [PATCH 12/31] Yes, these should be consts. --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index de9759959a..cc95aec1fd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2311,7 +2311,7 @@ void Application::displaySide(Camera& whichCamera) { // render transmitter pick ray, if non-empty if (_transmitterPickStart != _transmitterPickEnd) { Glower glower; - float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; + const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f }; glColor3fv(TRANSMITTER_PICK_COLOR); glLineWidth(3.0f); glBegin(GL_LINES); @@ -2323,7 +2323,7 @@ void Application::displaySide(Camera& whichCamera) { glPushMatrix(); glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); - float PICK_END_RADIUS = 0.025f; + const float PICK_END_RADIUS = 0.025f; glutSolidSphere(PICK_END_RADIUS, 8, 8); glPopMatrix(); From 28ffd8eca78113261cf0a04b60506216f64f4e92 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 23 Aug 2013 14:22:00 -0700 Subject: [PATCH 13/31] Render the ball heads with the voxeltar bodies. --- interface/src/avatar/Avatar.cpp | 3 ++- interface/src/avatar/MyAvatar.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4635be7357..1b9cc71b7f 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -763,10 +763,11 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { } } } else { - // Render the body's voxels + // Render the body's voxels and head float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { _voxels.render(false); + _head.render(alpha); } } _hand.render(lookingInMirror); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 175b38378d..0a811e2d48 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -595,10 +595,11 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { } } } else { - // Render the body's voxels + // Render the body's voxels and head float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { _voxels.render(false); + _head.render(alpha); } } _hand.render(lookingInMirror); From 818355dc1adc53de7760e2441274bbc460ea3fd7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 23 Aug 2013 15:18:06 -0700 Subject: [PATCH 14/31] implementing @problem's proposed changes to menus --- interface/src/Application.cpp | 6 +- interface/src/Menu.cpp | 378 ++++++++++++++++++---------------- interface/src/Menu.h | 11 +- 3 files changed, 205 insertions(+), 190 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cc95aec1fd..494a1e6976 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1041,10 +1041,8 @@ void Application::terminate() { LeapManager::terminate(); - if (Menu::getInstance()->isOptionChecked(MenuOption::SettingsAutosave)) { - Menu::getInstance()->saveSettings(); - _settings->sync(); - } + Menu::getInstance()->saveSettings(); + _settings->sync(); if (_enableNetworkThread) { _stopNetworkReceiveThread = true; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 119f21df60..edb7b3cb67 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -52,12 +52,7 @@ Menu::Menu() : Application *appInstance = Application::getInstance(); QMenu* fileMenu = addMenu("File"); - (addActionToQMenuAndActionHash(fileMenu, - MenuOption::Quit, - Qt::CTRL | Qt::Key_Q, - appInstance, - SLOT(quit())))->setMenuRole(QAction::QuitRole); - + (addActionToQMenuAndActionHash(fileMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, @@ -66,159 +61,78 @@ Menu::Menu() : #if defined(Q_OS_MAC) && defined(QT_NO_DEBUG) // show "Check for Updates" in the menu - (addActionToQMenuAndActionHash(fileMenu, MenuOption::CheckForUpdates, 0, this, SLOT(checkForUpdates())))->setMenuRole(QAction::ApplicationSpecificRole); + (addActionToQMenuAndActionHash(fileMenu, + MenuOption::CheckForUpdates, + 0, + this, + SLOT(checkForUpdates())))->setMenuRole(QAction::ApplicationSpecificRole); #endif - QMenu* pairMenu = addMenu("Pair"); - addActionToQMenuAndActionHash(pairMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest())); + addDisabledActionAndSeparator(fileMenu, "Voxels"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels())); + addDisabledActionAndSeparator(fileMenu, "Go"); + addActionToQMenuAndActionHash(fileMenu, + MenuOption::GoHome, + Qt::CTRL | Qt::Key_G, + appInstance->getAvatar(), + SLOT(goHome())); - QMenu* optionsMenu = addMenu("Options"); + addDisabledActionAndSeparator(fileMenu, "Settings"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Mirror, Qt::Key_H); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::GyroLook, 0, true); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::HeadMouse); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TransmitterDrive, 0, true); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true); - addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TestPing, 0, true); + addDisabledActionAndSeparator(fileMenu, "Devices"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest())); + addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true); - addCheckableActionToQMenuAndActionHash(optionsMenu, - MenuOption::Fullscreen, - Qt::Key_F, - false, - appInstance, - SLOT(setFullscreen(bool))); + (addActionToQMenuAndActionHash(fileMenu, + MenuOption::Quit, + Qt::CTRL | Qt::Key_Q, + appInstance, + SLOT(quit())))->setMenuRole(QAction::QuitRole); - addCheckableActionToQMenuAndActionHash(optionsMenu, - MenuOption::Webcam, - 0, - false, - appInstance->getWebcam(), - SLOT(setEnabled(bool))); + QMenu* editMenu = addMenu("Edit"); + addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels())); + addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels())); + addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels())); - addCheckableActionToQMenuAndActionHash(optionsMenu, - MenuOption::SkeletonTracking, - 0, - false, - appInstance->getWebcam(), - SLOT(setSkeletonTrackingOn(bool))); - - addCheckableActionToQMenuAndActionHash(optionsMenu, + addDisabledActionAndSeparator(editMenu, "Physics"); + addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true); + addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Collisions, 0, true, appInstance->getAvatar(), SLOT(setWantCollisionsOn(bool))); - addActionToQMenuAndActionHash(optionsMenu, - MenuOption::WebcamMode, - 0, - appInstance->getWebcam()->getGrabber(), - SLOT(cycleVideoSendMode())); - addCheckableActionToQMenuAndActionHash(optionsMenu, - MenuOption::WebcamTexture, - 0, - false, - appInstance->getWebcam()->getGrabber(), - SLOT(setDepthOnly(bool))); - - addActionToQMenuAndActionHash(optionsMenu, - MenuOption::GoHome, - Qt::CTRL | Qt::Key_G, - appInstance->getAvatar(), - SLOT(goHome())); - - QMenu* audioMenu = addMenu("Audio"); - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::EchoAudio); - - QMenu* renderMenu = addMenu("Render"); - addCheckableActionToQMenuAndActionHash(renderMenu, - MenuOption::Voxels, - Qt::SHIFT | Qt::Key_V, - true, - appInstance, - SLOT(setRenderVoxels(bool))); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::VoxelTextures); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Stars, 0, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::GroundPlane, 0, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Avatars, 0, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::AvatarAsBalls); - - addActionToQMenuAndActionHash(renderMenu, - MenuOption::VoxelMode, - 0, - appInstance->getAvatar()->getVoxels(), - SLOT(cycleMode())); - - addActionToQMenuAndActionHash(renderMenu, - MenuOption::FaceMode, - 0, - &appInstance->getAvatar()->getHead().getFace(), - SLOT(cycleRenderMode())); - - addActionToQMenuAndActionHash(renderMenu, - MenuOption::GlowMode, - 0, - appInstance->getGlowEffect(), - SLOT(cycleRenderMode())); - - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::AmbientOcclusion); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FrameTimer); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtVectors, 0, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtIndicator, 0, true); - addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FirstPerson, Qt::Key_P, true); - - addActionToQMenuAndActionHash(renderMenu, - MenuOption::IncreaseAvatarSize, - Qt::Key_Plus, - appInstance->getAvatar(), - SLOT(increaseSize())); - addActionToQMenuAndActionHash(renderMenu, - MenuOption::DecreaseAvatarSize, - Qt::Key_Minus, - appInstance->getAvatar(), - SLOT(decreaseSize())); - addActionToQMenuAndActionHash(renderMenu, - MenuOption::ResetAvatarSize, - 0, - appInstance->getAvatar(), - SLOT(resetSize())); - QMenu* toolsMenu = addMenu("Tools"); - addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Stats, Qt::Key_Slash); - addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L); - addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Oscilloscope, 0, true); - addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Bandwidth, 0, true); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); - - QMenu* voxelMenu = addMenu("Voxels"); _voxelModeActionsGroup = new QActionGroup(this); _voxelModeActionsGroup->setExclusive(false); - QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelAddMode, Qt::Key_V); + QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelAddMode, Qt::Key_V); _voxelModeActionsGroup->addAction(addVoxelMode); - QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelDeleteMode, Qt::Key_R); + QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelDeleteMode, Qt::Key_R); _voxelModeActionsGroup->addAction(deleteVoxelMode); - QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelColorMode, Qt::Key_B); + QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelColorMode, Qt::Key_B); _voxelModeActionsGroup->addAction(colorVoxelMode); - QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelSelectMode, Qt::Key_O); + QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelSelectMode, Qt::Key_O); _voxelModeActionsGroup->addAction(selectVoxelMode); - QAction* getColorMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelGetColorMode, Qt::Key_G); + QAction* getColorMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelGetColorMode, Qt::Key_G); _voxelModeActionsGroup->addAction(getColorMode); // connect each of the voxel mode actions to the updateVoxelModeActionsSlot foreach (QAction* action, _voxelModeActionsGroup->actions()) { connect(action, SIGNAL(triggered()), this, SLOT(updateVoxelModeActions())); } - - QAction* voxelPaintColor = addActionToQMenuAndActionHash(voxelMenu, + + QAction* voxelPaintColor = addActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelPaintColor, Qt::META | Qt::Key_C, this, @@ -230,29 +144,106 @@ Menu::Menu() : voxelPaintColor->setData(paintColor); voxelPaintColor->setIcon(Swatch::createIcon(paintColor)); - addActionToQMenuAndActionHash(voxelMenu, + addActionToQMenuAndActionHash(toolsMenu, MenuOption::DecreaseVoxelSize, QKeySequence::ZoomOut, appInstance, SLOT(decreaseVoxelSize())); - addActionToQMenuAndActionHash(voxelMenu, + addActionToQMenuAndActionHash(toolsMenu, MenuOption::IncreaseVoxelSize, QKeySequence::ZoomIn, appInstance, SLOT(increaseVoxelSize())); - addActionToQMenuAndActionHash(voxelMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); - - addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::DestructiveAddVoxel); - - addActionToQMenuAndActionHash(voxelMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels())); - addActionToQMenuAndActionHash(voxelMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels())); - addActionToQMenuAndActionHash(voxelMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels())); - addActionToQMenuAndActionHash(voxelMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels())); - addActionToQMenuAndActionHash(voxelMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels())); - - QMenu* debugMenu = addMenu("Debug"); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); - QMenu* frustumMenu = debugMenu->addMenu("View Frustum Debugging Tools"); + + QMenu* viewMenu = addMenu("View"); + + addCheckableActionToQMenuAndActionHash(viewMenu, + MenuOption::Fullscreen, + Qt::Key_F, + false, + appInstance, + SLOT(setFullscreen(bool))); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); + + addActionToQMenuAndActionHash(viewMenu, + MenuOption::IncreaseAvatarSize, + Qt::Key_Plus, + appInstance->getAvatar(), + SLOT(increaseSize())); + addActionToQMenuAndActionHash(viewMenu, + MenuOption::DecreaseAvatarSize, + Qt::Key_Minus, + appInstance->getAvatar(), + SLOT(decreaseSize())); + addActionToQMenuAndActionHash(viewMenu, + MenuOption::ResetAvatarSize, + 0, + appInstance->getAvatar(), + SLOT(resetSize())); + + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::Key_H); + + addCheckableActionToQMenuAndActionHash(viewMenu, + MenuOption::SkeletonTracking, + 0, + false, + appInstance->getWebcam(), + SLOT(setSkeletonTrackingOn(bool))); + + addDisabledActionAndSeparator(viewMenu, "Stats"); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); + addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); + + QMenu* developerMenu = addMenu("Developer"); + addDisabledActionAndSeparator(developerMenu, "Rendering"); + + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::Voxels, + Qt::SHIFT | Qt::Key_V, + true, + appInstance, + SLOT(setRenderVoxels(bool))); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VoxelTextures); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AmbientOcclusion); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stars, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GroundPlane, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Avatars, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AvatarAsBalls); + + addActionToQMenuAndActionHash(developerMenu, + MenuOption::VoxelMode, + 0, + appInstance->getAvatar()->getVoxels(), + SLOT(cycleMode())); + + addActionToQMenuAndActionHash(developerMenu, + MenuOption::FaceMode, + 0, + &appInstance->getAvatar()->getHead().getFace(), + SLOT(cycleRenderMode())); + + addActionToQMenuAndActionHash(developerMenu, + MenuOption::GlowMode, + 0, + appInstance->getGlowEffect(), + SLOT(cycleRenderMode())); + + + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::FrameTimer); + + addDisabledActionAndSeparator(developerMenu, "Testing"); + + QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools"); addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F); addActionToQMenuAndActionHash(frustumMenu, @@ -262,14 +253,14 @@ Menu::Menu() : SLOT(cycleFrustumRenderMode())); updateFrustumRenderModeAction(); - addActionToQMenuAndActionHash(debugMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests())); - addActionToQMenuAndActionHash(debugMenu, + addActionToQMenuAndActionHash(developerMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests())); + addActionToQMenuAndActionHash(developerMenu, MenuOption::TreeStats, Qt::SHIFT | Qt::Key_S, appInstance->getVoxels(), SLOT(collectStatsForTreesAndVBOs())); - QMenu* renderDebugMenu = debugMenu->addMenu("Render Debugging Tools"); + QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings); addActionToQMenuAndActionHash(renderDebugMenu, @@ -330,41 +321,29 @@ Menu::Menu() : Qt::CTRL | Qt::Key_T, appInstance->getVoxels(), SLOT(trueColorize())); + - addCheckableActionToQMenuAndActionHash(debugMenu, - MenuOption::SendVoxelColors, + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::Webcam, 0, - true, - appInstance->getAvatar(), - SLOT(setWantColor(bool))); - - addCheckableActionToQMenuAndActionHash(debugMenu, - MenuOption::LowRes, + false, + appInstance->getWebcam(), + SLOT(setEnabled(bool))); + + addActionToQMenuAndActionHash(developerMenu, + MenuOption::WebcamMode, + 0, + appInstance->getWebcam()->getGrabber(), + SLOT(cycleVideoSendMode())); + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::WebcamTexture, 0, - true, - appInstance->getAvatar(), - SLOT(setWantLowResMoving(bool))); + false, + appInstance->getWebcam()->getGrabber(), + SLOT(setDepthOnly(bool))); - addCheckableActionToQMenuAndActionHash(debugMenu, - MenuOption::DeltaSending, - 0, - true, - appInstance->getAvatar(), - SLOT(setWantDelta(bool))); - - addCheckableActionToQMenuAndActionHash(debugMenu, - MenuOption::OcclusionCulling, - Qt::SHIFT | Qt::Key_C, - true, - appInstance->getAvatar(), - SLOT(setWantOcclusionCulling(bool))); - - addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O); - addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P); - addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::SimulateLeapHand); - addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::TestRaveGlove); - - QMenu* audioDebugMenu = debugMenu->addMenu("Audio Debugging Tools"); + QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio); addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::ListenModeNormal, Qt::CTRL | Qt::Key_1, @@ -381,12 +360,46 @@ Menu::Menu() : appInstance, SLOT(setListenModeSingleSource())); - QMenu* settingsMenu = addMenu("Settings"); - addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsAutosave, 0, true); - addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsLoad, 0, this, SLOT(loadSettings())); - addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsSave, 0, this, SLOT(saveSettings())); - addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); - addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestPing, 0, true); + + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::SendVoxelColors, + 0, + true, + appInstance->getAvatar(), + SLOT(setWantColor(bool))); + + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::LowRes, + 0, + true, + appInstance->getAvatar(), + SLOT(setWantLowResMoving(bool))); + + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::DeltaSending, + 0, + true, + appInstance->getAvatar(), + SLOT(setWantDelta(bool))); + + addCheckableActionToQMenuAndActionHash(developerMenu, + MenuOption::OcclusionCulling, + Qt::SHIFT | Qt::Key_C, + true, + appInstance->getAvatar(), + SLOT(setWantOcclusionCulling(bool))); + + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::SimulateLeapHand); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestRaveGlove); + + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GyroLook, 0, true); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::HeadMouse); + + addDisabledActionAndSeparator(developerMenu, "Voxels"); + addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DestructiveAddVoxel); } void Menu::loadSettings(QSettings* settings) { @@ -553,6 +566,11 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) { } } +void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName) { + destinationMenu->addSeparator(); + (destinationMenu->addAction(actionName))->setEnabled(false); +} + QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, const QKeySequence& shortcut, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 252c9a8707..e484fe5c04 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -81,6 +81,8 @@ private: void scanMenuBar(settingsAction modifySetting, QSettings* set); void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set); + /// helper method to have separators with labels that are also compatible with OS X + void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName); QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, const QKeySequence& shortcut = 0, @@ -116,10 +118,10 @@ namespace MenuOption { const QString BandwidthDetails = "Bandwidth Details"; const QString CheckForUpdates = "Check for Updates..."; const QString Collisions = "Collisions"; - const QString CopyVoxels = "Copy Voxels"; + const QString CopyVoxels = "Copy"; const QString CoverageMap = "Render Coverage Map"; const QString CoverageMapV2 = "Render Coverage Map V2"; - const QString CutVoxels = "Cut Voxels"; + const QString CutVoxels = "Cut"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; const QString DestructiveAddVoxel = "Create Voxel is Destructive"; @@ -161,7 +163,7 @@ namespace MenuOption { const QString OcclusionCulling = "Occlusion Culling"; const QString Oscilloscope = "Audio Oscilloscope"; const QString Pair = "Pair"; - const QString PasteVoxels = "Paste Voxels"; + const QString PasteVoxels = "Paste"; const QString PipelineWarnings = "Show Render Pipeline Warnings"; const QString Preferences = "Preferences..."; const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors"; @@ -169,9 +171,6 @@ namespace MenuOption { const QString ResetSwatchColors = "Reset Swatch Colors"; const QString RunTimingTests = "Run Timing Tests"; const QString SendVoxelColors = "Colored Voxels"; - const QString SettingsAutosave = "Autosave"; - const QString SettingsLoad = "Load Settings"; - const QString SettingsSave = "Save Settings"; const QString SettingsImport = "Import Settings"; const QString SettingsExport = "Export Settings"; const QString ShowTrueColors = "Show TRUE Colors"; From 0c03fdd366d791203ae53061f6960bf496274c4e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Aug 2013 16:25:17 -0700 Subject: [PATCH 15/31] CR feedback --- voxel-server/src/NodeWatcher.cpp | 2 +- voxel-server/src/NodeWatcher.h | 2 +- voxel-server/src/VoxelNodeData.cpp | 10 +++------- voxel-server/src/VoxelNodeData.h | 2 +- voxel-server/src/VoxelPersistThread.cpp | 2 +- voxel-server/src/VoxelPersistThread.h | 2 +- voxel-server/src/VoxelSendThread.cpp | 5 +---- voxel-server/src/VoxelSendThread.h | 1 - voxel-server/src/main.cpp | 2 +- 9 files changed, 10 insertions(+), 18 deletions(-) diff --git a/voxel-server/src/NodeWatcher.cpp b/voxel-server/src/NodeWatcher.cpp index dd3244326a..894e53b4b5 100644 --- a/voxel-server/src/NodeWatcher.cpp +++ b/voxel-server/src/NodeWatcher.cpp @@ -5,7 +5,7 @@ // Created by Brad Hefta-Gaub on 8/21/13 // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -// Threaded or non-threaded object for sending voxels to a client +// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads // #include diff --git a/voxel-server/src/NodeWatcher.h b/voxel-server/src/NodeWatcher.h index 7d67397faa..a730f7d575 100644 --- a/voxel-server/src/NodeWatcher.h +++ b/voxel-server/src/NodeWatcher.h @@ -5,7 +5,7 @@ // Created by Brad Hefta-Gaub on 8/21/13 // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -// Threaded or non-threaded object for sending voxels to a client +// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads // #ifndef __voxel_server__NodeWatcher__ diff --git a/voxel-server/src/VoxelNodeData.cpp b/voxel-server/src/VoxelNodeData.cpp index 6d0d7d23cf..443edaaf2c 100644 --- a/voxel-server/src/VoxelNodeData.cpp +++ b/voxel-server/src/VoxelNodeData.cpp @@ -32,9 +32,7 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : // Create voxel sending thread... uint16_t nodeID = getOwningNode()->getNodeID(); _voxelSendThread = new VoxelSendThread(nodeID); - if (_voxelSendThread) { - _voxelSendThread->initialize(true); - } + _voxelSendThread->initialize(true); } @@ -59,10 +57,8 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) { VoxelNodeData::~VoxelNodeData() { delete[] _voxelPacket; - if (_voxelSendThread) { - _voxelSendThread->terminate(); - delete _voxelSendThread; - } + _voxelSendThread->terminate(); + delete _voxelSendThread; } bool VoxelNodeData::updateCurrentViewFrustum() { diff --git a/voxel-server/src/VoxelNodeData.h b/voxel-server/src/VoxelNodeData.h index efe28243a7..e3abb7f415 100644 --- a/voxel-server/src/VoxelNodeData.h +++ b/voxel-server/src/VoxelNodeData.h @@ -18,7 +18,7 @@ #include #include -class VoxelSendThread; // forward declare +class VoxelSendThread; class VoxelNodeData : public AvatarData { public: diff --git a/voxel-server/src/VoxelPersistThread.cpp b/voxel-server/src/VoxelPersistThread.cpp index 19c69bdc4b..ca40316d28 100644 --- a/voxel-server/src/VoxelPersistThread.cpp +++ b/voxel-server/src/VoxelPersistThread.cpp @@ -5,7 +5,7 @@ // Created by Brad Hefta-Gaub on 8/21/13 // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -// Threaded or non-threaded voxel persistance +// Threaded or non-threaded voxel persistence // #include diff --git a/voxel-server/src/VoxelPersistThread.h b/voxel-server/src/VoxelPersistThread.h index bdf8bbd73c..68487da182 100644 --- a/voxel-server/src/VoxelPersistThread.h +++ b/voxel-server/src/VoxelPersistThread.h @@ -5,7 +5,7 @@ // Created by Brad Hefta-Gaub on 8/21/13 // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -// Threaded or non-threaded voxel persistance +// Threaded or non-threaded voxel persistence // #ifndef __voxel_server__VoxelPersistThread__ diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp index 4c69c7f035..1db3d4864f 100644 --- a/voxel-server/src/VoxelSendThread.cpp +++ b/voxel-server/src/VoxelSendThread.cpp @@ -5,7 +5,7 @@ // Created by Brad Hefta-Gaub on 8/21/13 // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -// Threaded or non-threaded voxel persistance +// Threaded or non-threaded voxel packet sender // #include @@ -19,9 +19,6 @@ VoxelSendThread::VoxelSendThread(uint16_t nodeID) : _nodeID(nodeID) { } -VoxelSendThread::~VoxelSendThread() { -} - bool VoxelSendThread::process() { uint64_t lastSendTime = usecTimestampNow(); diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h index 1d5af7b012..4cd04700bb 100644 --- a/voxel-server/src/VoxelSendThread.h +++ b/voxel-server/src/VoxelSendThread.h @@ -21,7 +21,6 @@ class VoxelSendThread : public virtual GenericThread { public: VoxelSendThread(uint16_t nodeID); - ~VoxelSendThread(); protected: /// Implements generic processing behavior for this thread. virtual bool process(); diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 131ac9b7a2..56ea5003e1 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -177,7 +177,7 @@ int main(int argc, const char * argv[]) { } printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist)); - // if we want Voxel Persistance, load the local file now... + // if we want Voxel Persistence, load the local file now... bool persistantFileRead = false; if (::wantVoxelPersist) { From f7fa172ab7db8422036800321941dac9913ddd23 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Aug 2013 16:37:41 -0700 Subject: [PATCH 16/31] make temporary buffer bound to each sending thread object --- voxel-server/src/VoxelSendThread.cpp | 13 ++++++------- voxel-server/src/VoxelSendThread.h | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/voxel-server/src/VoxelSendThread.cpp b/voxel-server/src/VoxelSendThread.cpp index 1db3d4864f..38f8c6f9b8 100644 --- a/voxel-server/src/VoxelSendThread.cpp +++ b/voxel-server/src/VoxelSendThread.cpp @@ -196,7 +196,6 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no // If we have something in our nodeBag, then turn them into packets and send them out... if (!nodeData->nodeBag.isEmpty()) { - static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static int bytesWritten = 0; int packetsSentThisInterval = 0; uint64_t start = usecTimestampNow(); @@ -235,17 +234,17 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no isFullScene, &nodeData->stats, ::jurisdiction); nodeData->stats.encodeStarted(); - bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, + bytesWritten = serverTree.encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1, nodeData->nodeBag, params); nodeData->stats.encodeStopped(); if (nodeData->getAvailable() >= bytesWritten) { - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + nodeData->writeToPacket(_tempOutputBuffer, bytesWritten); } else { handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval++; nodeData->resetVoxelPacket(); - nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten); + nodeData->writeToPacket(_tempOutputBuffer, bytesWritten); } } else { if (nodeData->isPacketWaiting()) { @@ -257,15 +256,15 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no } // send the environment packet if (shouldSendEnvironments) { - int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); + int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); int envPacketLength = numBytesPacketHeader; int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData); for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength); + envPacketLength += environmentData[i].getBroadcastData(_tempOutputBuffer + envPacketLength); } - NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength); + NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength); trueBytesSent += envPacketLength; truePacketsSent++; } diff --git a/voxel-server/src/VoxelSendThread.h b/voxel-server/src/VoxelSendThread.h index 4cd04700bb..fa6c1460c7 100644 --- a/voxel-server/src/VoxelSendThread.h +++ b/voxel-server/src/VoxelSendThread.h @@ -30,6 +30,8 @@ private: void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent); void deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged); + + unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE]; }; #endif // __voxel_server__VoxelSendThread__ From 5cd085dae5c05a7722493ee4de76e4be31898272 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 26 Aug 2013 10:41:10 -0700 Subject: [PATCH 17/31] updated README to include details on running local domain --- README.md | 111 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index af433f917a..70fb0d3f2d 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,18 @@ In this repository you'll find the source to many of the components in our alpha-stage virtual world. The project embraces distributed development and if you'd like to help, we'll pay you -- find out more at Worklist.net. If you find a small bug and have a fix, pull requests are welcome. If you'd -like to get paid for your work, make sure you report the bug via a job on Worklist.net. +like to get paid for your work, make sure you report the bug via a job on +Worklist.net. We're hiring! We're looking for skilled developers; send your resume to hiring@highfidelity.io -Building Interface +Building Interface & other High Fidelity Components ========= -Interface is our OS X and Linux build-able -client for accessing our virtual world. +Interface is our OS X and Linux build-able client for accessing our virtual +world. CMake ----- @@ -27,7 +28,8 @@ for your platform. You can download CMake at cmake.org Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean, -and makes the gitignore a little easier to handle (since we can just ignore build). +and makes the gitignore a little easier to handle (since we can just ignore +build). mkdir build cd build @@ -37,40 +39,105 @@ Those are the commands used on OS X to run CMake from the build folder and generate Xcode project files. If you are building on a *nix system, you'll run something like "cmake .." (this will depend on your exact needs) +Building in XCode +----- + +After running cmake, you will have the make files or Xcode project file +necessary to build all of the components. For OS X, load Xcode, open the +hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target +drop down), and click Run. + +If the build completes successfully, you will have built targets for all HiFi +components located in the build/target_name/Debug directories. + Other dependencies & information ---- In addition to CMake, Qt 5.1 is required to build all components. What can I build on? -We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux distributions. -A Windows build is planned for the future, but not currently in development. +We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux +distributions. A Windows build is planned for the future, but not currently in +development. + +Running Interface +----- + +Using finder locate the interface.app Application in build/interface/Debug, +double-click the icon, and wait for interface to launch. At this point you will +connect to our default domain: "root.highfidelity.io". I'm in-world, what can I do? ---- -If you don't see anything, make sure your preferences are pointing to root.highfidelity.io, -if you still have no luck it's possible our servers are simply down; if you're experiencing -a major bug, let us know by suggesting a Job on Worklist.net -- make sure to include details -about your operating system and your computer system. +If you don't see anything, make sure your preferences are pointing to +root.highfidelity.io, if you still have no luck it's possible our servers are +simply down; if you're experiencing a major bug, let us know by suggesting a Job +on Worklist.net -- make sure to include details about your operating system and +your computer system. -To move around in-world, use the arrow keys (and Shift + up/down to fly up or down) -or W A S D, and E or C to fly up/down. All of the other possible options and features -are available via menus in the Interface application. +To move around in-world, use the arrow keys (and Shift + up/down to fly up or +down) or W A S D, and E or C to fly up/down. All of the other possible options +and features are available via menus in the Interface application. Other components ======== -voxel-server, animation-server, audio-mixer, avatar-mixer, domain-server, pairing-server -and space-server are architectural components that will allow you to run the full stack of -the virtual world should you choose to. +voxel-server, animation-server, audio-mixer, avatar-mixer, domain-server, +pairing-server and space-server are architectural components that will allow +you to run the full stack of the virtual world should you choose to. I want to run my own virtual world! ======== -In the voxel-server/src directory you will find a README that explains -how to setup and administer a voxel-server. - -Keep in mind that, at a minimum, you must run a domain-server, voxel-server, +In order to set up your own virtual world, you need to set up and run your own +local "domain". At a minimum, you must run a domain-server, voxel-server, audio-mixer, and avatar-mixer to have a working virtual world. -Basic documentation for the other components is on its way. + +Complete the steps above to build the system components. Then from the terminal +window, change directory into the build direction, then launch the following +components. + + ./domain-server/Debug/domain-server --local & + ./voxel-server/Debug/voxel-server --local & + ./avatar-mixer/Debug/avatar-mixer --local & + ./audio-mixer/Debug/audio-mixer --local & + +To confirm that the components are running you can type the following command: + + ps ax | grep -w "domain-server\|voxel-server\|audio-mixer\|avatar-mixer" + +You should see something like this: + + 70488 s001 S 0:00.04 ./domain-server/Debug/domain-server --local + 70489 s001 S 0:00.23 ./voxel-server/Debug/voxel-server --local + 70490 s001 S 0:00.03 ./avatar-mixer/Debug/avatar-mixer --local + 70491 s001 S 0:00.48 ./audio-mixer/Debug/audio-mixer --local + 70511 s001 S+ 0:00.00 grep -w domain-server\|voxel-server\|audio-mixer\ + |avatar-mixer + +Determine the IP address of the machine you're running these servers on. Here's +a handy resource that explains how to do this for different operating systems. +http://kb.iu.edu/data/aapa.html + +On Mac OS X, and many Unix systems you can use the ifconfig command. Typically, +the following command will give you the IP address you need to use. + + ifconfig | grep inet | grep broadcast + +You should get something like this: + + inet 192.168.1.104 netmask 0xffffff00 broadcast 192.168.1.255 + +Your IP address is the first set of numbers. In this case "192.168.1.104". You +may now use this IP address to access your domain. If you are running a local +DNS or other name service you should be able to access this IP address by name +as well. + +To access your local domain in Interface, open the Preferences dialog box, from +the Interface menu, and enter the IP address of the local DNS name for the +server computer in the "Domain" edit control. + +In the voxel-server/src directory you will find a README that explains in +further detail how to setup and administer a voxel-server. + From 943e1f9110dbd1337efb3c54282fa08b7d2b5a72 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 26 Aug 2013 14:06:23 -0700 Subject: [PATCH 18/31] added Go To Domain, Go To Location, and tweaks to TREE_SCALE settings --- interface/src/Menu.cpp | 134 +++++++++++++++++++++++++- interface/src/Menu.h | 4 + libraries/voxels/src/VoxelConstants.h | 4 +- 3 files changed, 139 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index edb7b3cb67..33fb4d5dfe 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -78,6 +78,17 @@ Menu::Menu() : Qt::CTRL | Qt::Key_G, appInstance->getAvatar(), SLOT(goHome())); + addActionToQMenuAndActionHash(fileMenu, + MenuOption::GoToDomain, + Qt::CTRL | Qt::Key_D, + this, + SLOT(goToDomain())); + addActionToQMenuAndActionHash(fileMenu, + MenuOption::GoToLocation, + Qt::CTRL | Qt::SHIFT | Qt::Key_L, + this, + SLOT(goToLocation())); + addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); @@ -625,7 +636,7 @@ bool Menu::isVoxelModeActionChecked() { } void Menu::editPreferences() { - Application *applicationInstance = Application::getInstance(); + Application* applicationInstance = Application::getInstance(); QDialog dialog(applicationInstance->getGLWidget()); dialog.setWindowTitle("Interface Preferences"); QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); @@ -670,6 +681,8 @@ void Menu::editPreferences() { layout->addWidget(buttons); if (dialog.exec() != QDialog::Accepted) { + // restore the main window's active state + applicationInstance->getWindow()->activateWindow(); return; } @@ -714,6 +727,125 @@ void Menu::editPreferences() { _fieldOfView = fieldOfView->value(); applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); + + // restore the main window's active state + applicationInstance->getWindow()->activateWindow(); +} + +void Menu::goToDomain() { + Application* applicationInstance = Application::getInstance(); + QDialog dialog(applicationInstance->getGLWidget()); + dialog.setWindowTitle("Go To Domain"); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); + dialog.setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form, 1); + + const int QLINE_MINIMUM_WIDTH = 400; + + QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname())); + domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH); + form->addRow("Domain server:", domainServerHostname); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); + layout->addWidget(buttons); + + if (dialog.exec() != QDialog::Accepted) { + // restore the main window's active state + applicationInstance->getWindow()->activateWindow(); + return; + } + + QByteArray newHostname; + + if (domainServerHostname->text().size() > 0) { + // the user input a new hostname, use that + newHostname = domainServerHostname->text().toLocal8Bit(); + } else { + // the user left the field blank, use the default hostname + newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME); + } + + // check if the domain server hostname is new + if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) { + + NodeList::getInstance()->clear(); + + // kill the local voxels + applicationInstance->getVoxels()->killLocalVoxels(); + + // reset the environment to default + applicationInstance->getEnvironment()->resetToDefault(); + + // set the new hostname + NodeList::getInstance()->setDomainHostname(newHostname.constData()); + } + // restore the main window's active state + applicationInstance->getWindow()->activateWindow(); +} + +void Menu::goToLocation() { + Application* applicationInstance = Application::getInstance(); + QDialog dialog(applicationInstance->getGLWidget()); + dialog.setWindowTitle("Go To Location"); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); + dialog.setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form, 1); + + const int QLINE_MINIMUM_WIDTH = 300; + + Application* appInstance = Application::getInstance(); + MyAvatar* myAvatar = appInstance->getAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x), + QString::number(avatarPos.y), QString::number(avatarPos.z)); + + QLineEdit* coordinates = new QLineEdit(currentLocation); + coordinates->setMinimumWidth(QLINE_MINIMUM_WIDTH); + form->addRow("Coordinates as x,y,z:", coordinates); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); + layout->addWidget(buttons); + + if (dialog.exec() != QDialog::Accepted) { + // restore the main window's active state + appInstance->getWindow()->activateWindow(); + return; + } + + QByteArray newCoordinates; + + if (coordinates->text().size() > 0) { + // the user input a new hostname, use that + + QString delimiterPattern(","); + QStringList coordinateItems = coordinates->text().split(delimiterPattern); + + const int NUMBER_OF_COORDINATE_ITEMS = 3; + const int X_ITEM = 0; + const int Y_ITEM = 1; + const int Z_ITEM = 2; + if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { + double x = coordinateItems[X_ITEM].toDouble(); + double y = coordinateItems[Y_ITEM].toDouble(); + double z = coordinateItems[Z_ITEM].toDouble(); + glm::vec3 newAvatarPos(x, y, z); + + if (newAvatarPos != avatarPos) { + qDebug("Going To Location: %f, %f, %f...\n", x, y, z); + myAvatar->setPosition(newAvatarPos); + } + } + } + // restore the main window's active state + appInstance->getWindow()->activateWindow(); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e484fe5c04..37d1644e5c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -62,6 +62,8 @@ public slots: private slots: void editPreferences(); + void goToDomain(); + void goToLocation(); void bandwidthDetailsClosed(); void voxelStatsDetailsClosed(); void cycleFrustumRenderMode(); @@ -143,6 +145,8 @@ namespace MenuOption { const QString FrustumRenderMode = "Render Mode"; const QString Fullscreen = "Fullscreen"; const QString GlowMode = "Cycle Glow Mode"; + const QString GoToDomain = "Go To Domain..."; + const QString GoToLocation = "Go To Location..."; const QString ImportVoxels = "Import Voxels"; const QString ImportVoxelsClipboard = "Import Voxels to Clipboard"; const QString IncreaseAvatarSize = "Increase Avatar Size"; diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index 5a5815b6b3..3e94ad6c90 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -24,8 +24,8 @@ const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); const bool LOW_RES_MONO = false; // while in "low res mode" do voxels switch to monochrome const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels -const int TREE_SCALE = 128; // This is the number of meters of the 0.0 to 1.0 voxel universe -const float VOXEL_SIZE_SCALE = 50000.0f; // This controls the LOD bigger will make smaller voxels visible at greater distance +const int TREE_SCALE = 128; //1048576; // This is the number of meters of the 0.0 to 1.0 voxel universe +const float VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f; // Controls the LOD. Larger number makes smaller voxels visible in distance const int NUMBER_OF_CHILDREN = 8; const int MAX_VOXEL_PACKET_SIZE = 1492; From 0df11124b3688abbfb59f04d6fd62b194ad272bc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 26 Aug 2013 14:24:42 -0700 Subject: [PATCH 19/31] tweak to how activateWindow() is called after dialog boxes --- interface/src/Menu.cpp | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 33fb4d5dfe..2b4b07f898 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -680,11 +680,11 @@ void Menu::editPreferences() { dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout->addWidget(buttons); - if (dialog.exec() != QDialog::Accepted) { - // restore the main window's active state - applicationInstance->getWindow()->activateWindow(); - return; - } + int ret = dialog.exec(); + applicationInstance->getWindow()->activateWindow(); + if (ret != QDialog::Accepted) { + return; + } QByteArray newHostname; @@ -727,9 +727,6 @@ void Menu::editPreferences() { _fieldOfView = fieldOfView->value(); applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); - - // restore the main window's active state - applicationInstance->getWindow()->activateWindow(); } void Menu::goToDomain() { @@ -753,11 +750,11 @@ void Menu::goToDomain() { dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout->addWidget(buttons); - if (dialog.exec() != QDialog::Accepted) { - // restore the main window's active state - applicationInstance->getWindow()->activateWindow(); - return; - } + int ret = dialog.exec(); + applicationInstance->getWindow()->activateWindow(); + if (ret != QDialog::Accepted) { + return; + } QByteArray newHostname; @@ -783,8 +780,6 @@ void Menu::goToDomain() { // set the new hostname NodeList::getInstance()->setDomainHostname(newHostname.constData()); } - // restore the main window's active state - applicationInstance->getWindow()->activateWindow(); } void Menu::goToLocation() { @@ -814,11 +809,11 @@ void Menu::goToLocation() { dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout->addWidget(buttons); - if (dialog.exec() != QDialog::Accepted) { - // restore the main window's active state - appInstance->getWindow()->activateWindow(); - return; - } + int ret = dialog.exec(); + applicationInstance->getWindow()->activateWindow(); + if (ret != QDialog::Accepted) { + return; + } QByteArray newCoordinates; @@ -844,8 +839,6 @@ void Menu::goToLocation() { } } } - // restore the main window's active state - appInstance->getWindow()->activateWindow(); } From a3bf373ffa93fe78515775b5859d540f23992d54 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 27 Aug 2013 11:41:22 -0700 Subject: [PATCH 20/31] revert TREE_SCALE and VOXEL_SCALE_SIZE for now --- libraries/voxels/src/VoxelConstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index 3e94ad6c90..5a5815b6b3 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -24,8 +24,8 @@ const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); const bool LOW_RES_MONO = false; // while in "low res mode" do voxels switch to monochrome const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels -const int TREE_SCALE = 128; //1048576; // This is the number of meters of the 0.0 to 1.0 voxel universe -const float VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f; // Controls the LOD. Larger number makes smaller voxels visible in distance +const int TREE_SCALE = 128; // This is the number of meters of the 0.0 to 1.0 voxel universe +const float VOXEL_SIZE_SCALE = 50000.0f; // This controls the LOD bigger will make smaller voxels visible at greater distance const int NUMBER_OF_CHILDREN = 8; const int MAX_VOXEL_PACKET_SIZE = 1492; From 24a43c050c876654f44e10feb60c3483b3a52a6e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 28 Aug 2013 10:54:22 -0700 Subject: [PATCH 21/31] require Qt5Script for avatars library --- libraries/avatars/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index 207057e244..2ae71ec21c 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -8,9 +8,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm set(TARGET_NAME avatars) +find_package(Qt5Script REQUIRED) + include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) +qt5_use_modules(${TARGET_NAME} Script) + include(${MACRO_DIR}/IncludeGLM.cmake) include_glm(${TARGET_NAME} ${ROOT_DIR}) From 1dc8d8dd0c8d699c0e7370d7cbcf17f0366762c0 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Thu, 29 Aug 2013 17:59:36 -0700 Subject: [PATCH 22/31] Added LOD to preview + Shared voxelSystem for reduced memory consumption --- interface/src/Application.cpp | 70 +++++++-------- interface/src/Application.h | 9 +- interface/src/ImportDialog.cpp | 124 ++++++++++++++++++++++++--- interface/src/ImportDialog.h | 7 +- interface/src/VoxelImporter.cpp | 72 +++++++++------- interface/src/VoxelImporter.h | 14 ++- interface/src/VoxelSystem.cpp | 14 ++- interface/src/VoxelSystem.h | 6 +- libraries/voxels/src/ViewFrustum.cpp | 1 + libraries/voxels/src/VoxelTree.cpp | 14 ++- 10 files changed, 227 insertions(+), 104 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 494a1e6976..8402236d78 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,7 +82,7 @@ const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. -static const float CLIPBOARD_TREE_SCALE = 1.0f; +static const float SHARED_VOXEL_SYSTEM_TREE_SCALE = 1.0f; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); @@ -97,7 +97,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _frameCount(0), _fps(120.0f), _justStarted(true), - _clipboard(CLIPBOARD_TREE_SCALE), _voxelImporter(_window), _wantToKillLocalVoxels(false), _audioScope(256, 200, true), @@ -1207,15 +1206,6 @@ void Application::exportVoxels() { void Application::importVoxels() { if (_voxelImporter.exec()) { qDebug("[DEBUG] Import succedded.\n"); - - if (_voxelImporter.getImportIntoClipboard()) { - _clipboard.killLocalVoxels(); - _voxelImporter.getVoxelSystem()->copySubTreeIntoNewTree( - _voxelImporter.getVoxelSystem()->getVoxelAt(0, 0, 0, 1), - &_clipboard, - true); - _voxelImporter.reset(); - } } else { qDebug("[DEBUG] Import failed.\n"); } @@ -1230,13 +1220,17 @@ void Application::cutVoxels() { } void Application::copyVoxels() { + // switch to and clear the clipboard first... + _sharedVoxelSystem.killLocalVoxels(); + if (_sharedVoxelSystem.getTree() != &_clipboard) { + _clipboard.eraseAllVoxels(); + _sharedVoxelSystem.changeTree(&_clipboard); + } + + // then copy onto it if there is something to copy VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); if (selectedNode) { - // clear the clipboard first... - _clipboard.killLocalVoxels(); - - // then copy onto it - _voxels.copySubTreeIntoNewTree(selectedNode, &_clipboard, true); + _voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true); } } @@ -1257,12 +1251,13 @@ void Application::pasteVoxels() { args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); } - if (_voxelImporter.getImportWaiting()) { - _voxelImporter.getVoxelSystem()->recurseTreeWithOperation(sendVoxelsOperation, &args); - _voxelImporter.reset(); - } else { - _clipboard.recurseTreeWithOperation(sendVoxelsOperation, &args); + _sharedVoxelSystem.getTree()->recurseTreeWithOperation(sendVoxelsOperation, &args); + + if (_sharedVoxelSystem.getTree() != &_clipboard) { + _sharedVoxelSystem.killLocalVoxels(); + _sharedVoxelSystem.changeTree(&_clipboard); } + _voxelEditSender.flushQueue(); if (calculatedOctCode) { @@ -1304,10 +1299,21 @@ void Application::initDisplay() { void Application::init() { _voxels.init(); - _clipboard.init(); - _clipboardViewFrustum.setKeyholeRadius(1000.0f); - _clipboardViewFrustum.calculate(); - _clipboard.setViewFrustum(&_clipboardViewFrustum); + _sharedVoxelSystemViewFrustum.setPosition(glm::vec3(TREE_SCALE / 2.0f, + TREE_SCALE / 2.0f, + 3.0f * TREE_SCALE / 2.0f)); + _sharedVoxelSystemViewFrustum.setNearClip(TREE_SCALE / 2.0f); + _sharedVoxelSystemViewFrustum.setFarClip(3.0f * TREE_SCALE / 2.0f); + _sharedVoxelSystemViewFrustum.setFieldOfView(90); + _sharedVoxelSystemViewFrustum.setOrientation(glm::quat()); + _sharedVoxelSystemViewFrustum.calculate(); + _sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum); + _sharedVoxelSystem.init(); + VoxelTree* tmpTree = _sharedVoxelSystem.getTree(); + _sharedVoxelSystem.changeTree(&_clipboard); + delete tmpTree; + + _voxelImporter.init(); _environment.init(); @@ -1318,6 +1324,7 @@ void Application::init() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; + QCursor::setPos(_headMouseX, _headMouseY); _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); @@ -1325,7 +1332,6 @@ void Application::init() { _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); - QCursor::setPos(_headMouseX, _headMouseY); OculusManager::connect(); if (OculusManager::isConnected()) { @@ -2225,15 +2231,11 @@ void Application::displaySide(Camera& whichCamera) { glTranslatef(_mouseVoxel.x * TREE_SCALE, _mouseVoxel.y * TREE_SCALE, _mouseVoxel.z * TREE_SCALE); - glScalef(_mouseVoxel.s * TREE_SCALE, - _mouseVoxel.s * TREE_SCALE, - _mouseVoxel.s * TREE_SCALE); + glScalef(_mouseVoxel.s, + _mouseVoxel.s, + _mouseVoxel.s); - if (_voxelImporter.getImportWaiting()) { - _voxelImporter.getVoxelSystem()->render(true); - } else { - _clipboard.render(true); - } + _sharedVoxelSystem.render(true); glPopMatrix(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 7ad64ca707..dfb6079e81 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -112,6 +112,8 @@ public: Camera* getCamera() { return &_myCamera; } ViewFrustum* getViewFrustum() { return &_viewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } + VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } + VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } SerialInterface* getSerialHeadSensor() { return &_serialHeadSensor; } Webcam* getWebcam() { return &_webcam; } @@ -238,10 +240,11 @@ private: Stars _stars; - VoxelSystem _voxels; - VoxelSystem _clipboard; // if I copy/paste - ViewFrustum _clipboardViewFrustum; + VoxelSystem _voxels; + VoxelTree _clipboard; // if I copy/paste VoxelImporter _voxelImporter; + VoxelSystem _sharedVoxelSystem; + ViewFrustum _sharedVoxelSystemViewFrustum; QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index 9c6653cc8a..bb2365cec1 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -34,9 +34,9 @@ const float FIELD_OF_VIEW = 60.0f; class GLWidget : public QGLWidget { public: - GLWidget(QWidget* parent = NULL, VoxelSystem* voxelSystem = NULL); + GLWidget(QWidget* parent = NULL); void setDraw(bool draw) {_draw = draw;} - void setTargetCenter(glm::vec3 targetCenter) {_targetCenter = targetCenter;} + void setTargetCenter(glm::vec3 targetCenter) { _targetCenter = targetCenter; } protected: virtual void initializeGL(); @@ -61,9 +61,8 @@ private: int _mouseY; }; -GLWidget::GLWidget(QWidget *parent, VoxelSystem *voxelSystem) +GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent, Application::getInstance()->getGLWidget()), - _voxelSystem(voxelSystem), _draw(false), _a(0.0f), _h(VERTICAL_ANGLE), @@ -71,6 +70,7 @@ GLWidget::GLWidget(QWidget *parent, VoxelSystem *voxelSystem) _pressed(false), _mouseX(0), _mouseY(0) { + _voxelSystem = Application::getInstance()->getSharedVoxelSystem(); } void GLWidget::initializeGL() { @@ -110,7 +110,7 @@ void GLWidget::paintGL() { UP_VECT.x, UP_VECT.y, UP_VECT.z); - if (_draw && _voxelSystem) { + if (_draw) { glBegin(GL_LINES); glColor3d(1, 1 ,1); glVertex3d(0, 0, 0); @@ -134,11 +134,109 @@ void GLWidget::paintGL() { glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 0 ); glEnd(); + glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE); + _voxelSystem->render(false); + + + ViewFrustum& viewFrustum = *Application::getInstance()->getSharedVoxelSystem()->getViewFrustum(); + glm::vec3 position = viewFrustum.getOffsetPosition(); + glm::vec3 direction = viewFrustum.getOffsetDirection(); + glm::vec3 up = viewFrustum.getOffsetUp(); + glm::vec3 right = viewFrustum.getOffsetRight(); + + // Calculate the origin direction vectors + glm::vec3 lookingAt = position + (direction * 0.2f); + glm::vec3 lookingAtUp = position + (up * 0.2f); + glm::vec3 lookingAtRight = position + (right * 0.2f); + + glBegin(GL_LINES); + // Looking At = white + glColor3d(1.0f, 1.0f, 1.0f); + glVertex3d(position.x, position.y, position.z); + glVertex3d(lookingAt.x, lookingAt.y, lookingAt.z); + + // Looking At Up = purple + glColor3d(1.0f, 0.0f, 1.0f); + glVertex3d(position.x, position.y, position.z); + glVertex3d(lookingAtUp.x, lookingAtUp.y, lookingAtUp.z); + + // Looking At Right = cyan + glColor3d(0.0f, 1.0f, 1.0f); + glVertex3d(position.x, position.y, position.z); + glVertex3d(lookingAtRight.x, lookingAtRight.y, lookingAtRight.z); + + + // Drawing the bounds of the frustum + // viewFrustum.getNear plane - bottom edge + glColor3d(1.0f, 0.0f, 0.0f); + glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); + glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); + + // viewFrustum.getNear plane - top edge + glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); + glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); + + // viewFrustum.getNear plane - right edge + glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); + glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); + + // viewFrustum.getNear plane - left edge + glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); + glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); + + + // viewFrustum.getFar plane - bottom edge + glColor3d(0.0f, 1.0f, 0.0f); // GREEN!!! + glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); + glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); + + // viewFrustum.getFar plane - top edge + glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); + glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); + + // viewFrustum.getFar plane - right edge + glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); + glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); + + // viewFrustum.getFar plane - left edge + glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); + glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); + + // RIGHT PLANE IS CYAN + // right plane - bottom edge - viewFrustum.getNear to distant + glColor3d(0.0f, 1.0f, 1.0f); + glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); + glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); + + // right plane - top edge - viewFrustum.getNear to distant + glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); + glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); + + // LEFT PLANE IS BLUE + // left plane - bottom edge - viewFrustum.getNear to distant + glColor3d(0.0f, 0.0f, 1.0f); + glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); + glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); + + // left plane - top edge - viewFrustum.getNear to distant + glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); + glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); + glEnd(); + + // Draw the keyhole + float keyholeRadius = viewFrustum.getKeyholeRadius(); + if (keyholeRadius > 0.0f) { + glPushMatrix(); + glColor3d(1.0f, 1.0f, 0.0f); + glTranslatef(position.x, position.y, position.z); // where we actually want it! + glutWireSphere(keyholeRadius, 20, 20); + glPopMatrix(); + } + } } - void GLWidget::mousePressEvent(QMouseEvent* event) { _pressed = true; _mouseX = event->globalX(); @@ -158,14 +256,13 @@ void GLWidget::mouseReleaseEvent(QMouseEvent* event) { _pressed = false; } -ImportDialog::ImportDialog(QWidget *parent, VoxelSystem* voxelSystem) +ImportDialog::ImportDialog(QWidget *parent) : QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, IMPORT_FILE_TYPES), _importButton (IMPORT_BUTTON_NAME, this), _clipboardImportBox(IMPORT_TO_CLIPBOARD_CHECKBOX_STRING, this), _previewBox (PREVIEW_CHECKBOX_STRING, this), _previewBar (this), - _glPreview (new GLWidget(this, voxelSystem)) { - + _glPreview (new GLWidget(this)) { setOption(QFileDialog::DontUseNativeDialog, true); setFileMode(QFileDialog::ExistingFile); setViewMode(QFileDialog::Detail); @@ -188,15 +285,18 @@ ImportDialog::ImportDialog(QWidget *parent, VoxelSystem* voxelSystem) connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString))); connect(&_glTimer, SIGNAL(timeout()), SLOT(timer())); - - connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float, float, float))); - connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int))); } ImportDialog::~ImportDialog() { delete _glPreview; } +void ImportDialog::init() { + VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); + connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float, float, float))); + connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int))); +} + void ImportDialog::import() { _importButton.setDisabled(true); _clipboardImportBox.setDisabled(true); diff --git a/interface/src/ImportDialog.h b/interface/src/ImportDialog.h index f3c1934760..35102dfe4c 100644 --- a/interface/src/ImportDialog.h +++ b/interface/src/ImportDialog.h @@ -23,15 +23,16 @@ class GLWidget; class ImportDialog : public QFileDialog { Q_OBJECT public: - ImportDialog(QWidget* parent = NULL, VoxelSystem* voxelSystem = NULL); + ImportDialog(QWidget* parent = NULL); ~ImportDialog(); + void init(); + void reset(); + bool getWantPreview() const { return _previewBox.isChecked(); } QString getCurrentFile() const { return _currentFile; } bool getImportIntoClipboard() const { return _clipboardImportBox.isChecked(); } - void reset(); - signals: void previewToggled(bool); void accepted(); diff --git a/interface/src/VoxelImporter.cpp b/interface/src/VoxelImporter.cpp index ef4eeafc4b..b3ca39bbb0 100644 --- a/interface/src/VoxelImporter.cpp +++ b/interface/src/VoxelImporter.cpp @@ -7,29 +7,24 @@ // #include +#include #include #include -static const int IMPORT_SYSTEM_SCALE = 1.0f; -static const int MAX_VOXELS_PER_IMPORT = 2000000; - class ImportTask : public QObject, public QRunnable { public: - ImportTask(VoxelSystem* voxelSystem, const QString &filename); + ImportTask(const QString &filename); void run(); private: - VoxelSystem* _voxelSystem; - QString _filename; + QString _filename; }; VoxelImporter::VoxelImporter(QWidget* parent) : QObject(parent), - _voxelSystem(IMPORT_SYSTEM_SCALE, MAX_VOXELS_PER_IMPORT), - _initialized(false), - _importWaiting(false), - _importDialog(parent, &_voxelSystem), + _voxelTree(true), + _importDialog(parent), _currentTask(NULL), _nextTask(NULL) { @@ -38,6 +33,10 @@ VoxelImporter::VoxelImporter(QWidget* parent) connect(&_importDialog, SIGNAL(accepted()), SLOT(import())); } +void VoxelImporter::init() { + _importDialog.init(); +} + VoxelImporter::~VoxelImporter() { if (_nextTask) { delete _nextTask; @@ -46,16 +45,15 @@ VoxelImporter::~VoxelImporter() { if (_currentTask) { disconnect(_currentTask, 0, 0, 0); - _voxelSystem.cancelImport(); + _voxelTree.cancelImport(); _currentTask = NULL; } } void VoxelImporter::reset() { - _voxelSystem.killLocalVoxels(); + _voxelTree.eraseAllVoxels(); _importDialog.reset(); _filename = ""; - _importWaiting = false; if (_nextTask) { delete _nextTask; @@ -63,17 +61,11 @@ void VoxelImporter::reset() { } if (_currentTask) { - _voxelSystem.cancelImport(); + _voxelTree.cancelImport(); } } int VoxelImporter::exec() { - if (!_initialized) { - _voxelSystem.init(); - _importViewFrustum.calculate(); - _voxelSystem.setViewFrustum(&_importViewFrustum); - _initialized = true; - } reset(); int ret = _importDialog.exec(); @@ -82,7 +74,15 @@ int VoxelImporter::exec() { reset(); } else { _importDialog.reset(); - _importWaiting = true; + + if (_importDialog.getImportIntoClipboard()) { + VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); + + voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->rootNode, + Application::getInstance()->getClipboard(), + true); + voxelSystem->changeTree(Application::getInstance()->getClipboard()); + } } return ret; @@ -102,11 +102,11 @@ int VoxelImporter::preImport() { delete _nextTask; } - _nextTask = new ImportTask(&_voxelSystem, _filename); + _nextTask = new ImportTask(_filename); connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask())); if (_currentTask != NULL) { - _voxelSystem.cancelImport(); + _voxelTree.cancelImport(); } else { launchTask(); } @@ -138,12 +138,12 @@ int VoxelImporter::import() { delete _nextTask; } - _nextTask = new ImportTask(&_voxelSystem, _filename); + _nextTask = new ImportTask(_filename); connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask())); connect(_nextTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept())); if (_currentTask != NULL) { - _voxelSystem.cancelImport(); + _voxelTree.cancelImport(); } else { launchTask(); } @@ -153,28 +153,36 @@ int VoxelImporter::import() { void VoxelImporter::launchTask() { if (_nextTask != NULL) { - _voxelSystem.killLocalVoxels(); _currentTask = _nextTask; _nextTask = NULL; + + if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) { + Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree); + } + QThreadPool::globalInstance()->start(_currentTask); } else { _currentTask = NULL; } } -ImportTask::ImportTask(VoxelSystem* voxelSystem, const QString &filename) - : _voxelSystem(voxelSystem), - _filename(filename) { +ImportTask::ImportTask(const QString &filename) + : _filename(filename) { } void ImportTask::run() { + VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); + voxelSystem->killLocalVoxels(); + if (_filename.endsWith(".png", Qt::CaseInsensitive)) { - _voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data()); + voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data()); } else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) { - _voxelSystem->readFromSVOFile(_filename.toLocal8Bit().data()); + voxelSystem->readFromSVOFile(_filename.toLocal8Bit().data()); } else if (_filename.endsWith(".schematic", Qt::CaseInsensitive)) { - _voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data()); + voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data()); } else { qDebug("[ERROR] Invalid file extension.\n"); } + + voxelSystem->getTree()->reaverageVoxelColors(voxelSystem->getTree()->rootNode); } diff --git a/interface/src/VoxelImporter.h b/interface/src/VoxelImporter.h index 9b25adcee0..a84955f2c9 100644 --- a/interface/src/VoxelImporter.h +++ b/interface/src/VoxelImporter.h @@ -22,11 +22,11 @@ class VoxelImporter : public QObject { public: VoxelImporter(QWidget* parent = NULL); ~VoxelImporter(); + + void init(); void reset(); - bool getImportWaiting() const { return _importWaiting; } - VoxelSystem* getVoxelSystem() { return &_voxelSystem; } - bool getImportIntoClipboard() const { return _importDialog.getImportIntoClipboard(); } + VoxelTree* getVoxelTree() { return &_voxelTree; } public slots: int exec(); @@ -37,14 +37,10 @@ private slots: void launchTask(); private: - VoxelSystem _voxelSystem; - ViewFrustum _importViewFrustum; - bool _initialized; - bool _importWaiting; - + VoxelTree _voxelTree; ImportDialog _importDialog; - QString _filename; + QString _filename; ImportTask* _currentTask; ImportTask* _nextTask; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 9c31040ca5..50cfd819cc 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -420,7 +420,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(_viewFrustum); - + node->setShouldRender(shouldRender); // let children figure out their renderness if (!node->isLeaf()) { @@ -631,6 +631,18 @@ void VoxelSystem::init() { _perlinModulateProgram->release(); } +void VoxelSystem::changeTree(VoxelTree* newTree) { + disconnect(_tree, 0, this, 0); + + _tree = newTree; + _tree->setDirtyBit(); + + connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); + connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); + + setupNewVoxelsForDrawing(); +} + void VoxelSystem::updateFullVBOs() { updateVBOSegment(0, _voxelsInReadArrays); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 7b6c001e8a..53a083ada9 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -43,6 +43,8 @@ public: void simulate(float deltaTime) { }; void render(bool texture); + void changeTree(VoxelTree* newTree); + VoxelTree* getTree() const {return _tree;} ViewFrustum* getViewFrustum() const {return _viewFrustum;} void setViewFrustum(ViewFrustum* viewFrustum) {_viewFrustum = viewFrustum;} unsigned long getVoxelsUpdated() const {return _voxelsUpdated;}; @@ -93,6 +95,7 @@ public: virtual void nodeDeleted(VoxelNode* node); virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); + void setupNewVoxelsForDrawing(); signals: void importSize(float x, float y, float z); @@ -120,8 +123,7 @@ protected: VoxelTree* _tree; glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; - - void setupNewVoxelsForDrawing(); + virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color); diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index f12608f5dd..a35dc49ec0 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -423,6 +423,7 @@ void ViewFrustum::printDebugDetails() const { qDebug("_right=%f,%f,%f\n", _right.x, _right.y, _right.z ); qDebug("_fieldOfView=%f\n", _fieldOfView); qDebug("_aspectRatio=%f\n", _aspectRatio); + qDebug("_keyHoleRadius=%f\n", _keyholeRadius); qDebug("_nearClip=%f\n", _nearClip); qDebug("_farClip=%f\n", _farClip); qDebug("_eyeOffsetPosition=%f,%f,%f\n", _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z ); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b17200448b..8ecb95d4d6 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1581,7 +1581,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool VoxelTree::readFromSVOFile(const char* fileName) { std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); if(file.is_open()) { - emit importSize(1.0f, 1.0f, 1.0f); emit importProgress(0); @@ -1607,9 +1606,7 @@ bool VoxelTree::readFromSVOFile(const char* fileName) { } bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) { - emit importSize(1.0f, 1.0f, 1.0f); emit importProgress(0); - int minAlpha = INT_MAX; QImage pngImage = QImage(filename); @@ -1624,6 +1621,8 @@ bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) { while (maxSize > scale) {scale *= 2;} float size = 1.0f / scale; + emit importSize(size * pngImage.width(), 1.0f, size * pngImage.height()); + QRgb pixel; int minNeighborhoodAlpha; @@ -1692,14 +1691,13 @@ bool VoxelTree::readFromSchematicFile(const char *fileName) { while (max > scale) {scale *= 2;} float size = 1.0f / scale; - int create = 1; - int red = 128, green = 128, blue = 128; - int count = 0; - emit importSize(size * schematics.getWidth(), size * schematics.getHeight(), size * schematics.getLength()); - emit importProgress(0); + + int create = 1; + int red = 128, green = 128, blue = 128; + int count = 0; for (int y = 0; y < schematics.getHeight(); ++y) { for (int z = 0; z < schematics.getLength(); ++z) { From fc991bf1aca1fbadc0537d220405bebb5b7896eb Mon Sep 17 00:00:00 2001 From: atlante45 Date: Thu, 29 Aug 2013 18:11:16 -0700 Subject: [PATCH 23/31] Some code reformating --- interface/src/Application.cpp | 1 - interface/src/VoxelSystem.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8402236d78..27d11a61b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,7 +82,6 @@ const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. -static const float SHARED_VOXEL_SYSTEM_TREE_SCALE = 1.0f; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 53a083ada9..ed635dae3f 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -95,7 +95,6 @@ public: virtual void nodeDeleted(VoxelNode* node); virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); - void setupNewVoxelsForDrawing(); signals: void importSize(float x, float y, float z); @@ -121,6 +120,8 @@ protected: float _treeScale; int _maxVoxels; VoxelTree* _tree; + + void setupNewVoxelsForDrawing(); glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; From b46bd1fd4126fbb5255cc0f178eb9ea70f1c9a8c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 30 Aug 2013 10:28:30 -0700 Subject: [PATCH 24/31] display the Avatar's hand in first person --- interface/src/avatar/MyAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0a811e2d48..34f4d5caa4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -514,7 +514,9 @@ float MyAvatar::getBallRenderAlpha(int ball, bool lookingInMirror) const { void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON) { - // Dont display body + // Dont display body, only the hand + _hand.render(lookingInMirror); + return; } From f21a2798ffb2da0c3566e3d3fc784e895464e2e8 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Fri, 30 Aug 2013 10:37:55 -0700 Subject: [PATCH 25/31] Fixed _sharedVoxelSystemTree deleted twice --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27d11a61b6..3f0d418fd3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -235,6 +235,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : Application::~Application() { NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(this); + + _sharedVoxelSystem.changeTree(new VoxelTree); } void Application::initializeGL() { From 229821e211cbcb6b878a826a8bb74ceb4fd9cc0d Mon Sep 17 00:00:00 2001 From: atlante45 Date: Fri, 30 Aug 2013 15:28:10 -0700 Subject: [PATCH 26/31] Removed frustum display in preview mode --- interface/src/ImportDialog.cpp | 98 ---------------------------------- 1 file changed, 98 deletions(-) diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index bb2365cec1..beda8977cd 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -135,105 +135,7 @@ void GLWidget::paintGL() { glEnd(); glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE); - _voxelSystem->render(false); - - - ViewFrustum& viewFrustum = *Application::getInstance()->getSharedVoxelSystem()->getViewFrustum(); - glm::vec3 position = viewFrustum.getOffsetPosition(); - glm::vec3 direction = viewFrustum.getOffsetDirection(); - glm::vec3 up = viewFrustum.getOffsetUp(); - glm::vec3 right = viewFrustum.getOffsetRight(); - - // Calculate the origin direction vectors - glm::vec3 lookingAt = position + (direction * 0.2f); - glm::vec3 lookingAtUp = position + (up * 0.2f); - glm::vec3 lookingAtRight = position + (right * 0.2f); - - glBegin(GL_LINES); - // Looking At = white - glColor3d(1.0f, 1.0f, 1.0f); - glVertex3d(position.x, position.y, position.z); - glVertex3d(lookingAt.x, lookingAt.y, lookingAt.z); - - // Looking At Up = purple - glColor3d(1.0f, 0.0f, 1.0f); - glVertex3d(position.x, position.y, position.z); - glVertex3d(lookingAtUp.x, lookingAtUp.y, lookingAtUp.z); - - // Looking At Right = cyan - glColor3d(0.0f, 1.0f, 1.0f); - glVertex3d(position.x, position.y, position.z); - glVertex3d(lookingAtRight.x, lookingAtRight.y, lookingAtRight.z); - - - // Drawing the bounds of the frustum - // viewFrustum.getNear plane - bottom edge - glColor3d(1.0f, 0.0f, 0.0f); - glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); - glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); - - // viewFrustum.getNear plane - top edge - glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); - glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); - - // viewFrustum.getNear plane - right edge - glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); - glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); - - // viewFrustum.getNear plane - left edge - glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); - glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); - - - // viewFrustum.getFar plane - bottom edge - glColor3d(0.0f, 1.0f, 0.0f); // GREEN!!! - glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); - glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); - - // viewFrustum.getFar plane - top edge - glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); - glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); - - // viewFrustum.getFar plane - right edge - glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); - glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); - - // viewFrustum.getFar plane - left edge - glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); - glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); - - // RIGHT PLANE IS CYAN - // right plane - bottom edge - viewFrustum.getNear to distant - glColor3d(0.0f, 1.0f, 1.0f); - glVertex3d(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); - glVertex3d(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); - - // right plane - top edge - viewFrustum.getNear to distant - glVertex3d(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); - glVertex3d(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); - - // LEFT PLANE IS BLUE - // left plane - bottom edge - viewFrustum.getNear to distant - glColor3d(0.0f, 0.0f, 1.0f); - glVertex3d(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); - glVertex3d(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); - - // left plane - top edge - viewFrustum.getNear to distant - glVertex3d(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); - glVertex3d(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); - glEnd(); - - // Draw the keyhole - float keyholeRadius = viewFrustum.getKeyholeRadius(); - if (keyholeRadius > 0.0f) { - glPushMatrix(); - glColor3d(1.0f, 1.0f, 0.0f); - glTranslatef(position.x, position.y, position.z); // where we actually want it! - glutWireSphere(keyholeRadius, 20, 20); - glPopMatrix(); - } - } } From 0068b838b2821d90047695c7707dc859aaf9ab10 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Fri, 30 Aug 2013 15:56:28 -0700 Subject: [PATCH 27/31] Fixed importer crashing on non ARGB32 png. --- libraries/voxels/src/VoxelTree.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b17200448b..c673755e82 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1614,8 +1614,10 @@ bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) { QImage pngImage = QImage(filename); - for (int x = 0; x < pngImage.width() * pngImage.height(); ++x) { - minAlpha = std::min(qAlpha(pngImage.color(x)) , minAlpha); + for (int i = 0; i < pngImage.width(); ++i) { + for (int j = 0; j < pngImage.height(); ++j) { + minAlpha = std::min(qAlpha(pngImage.pixel(i, j)) , minAlpha); + } } int maxSize = std::max(pngImage.width(), pngImage.height()); From 66425763b82682257cb700b700df1ebb808a3e3e Mon Sep 17 00:00:00 2001 From: atlante45 Date: Fri, 30 Aug 2013 16:01:18 -0700 Subject: [PATCH 28/31] Revert "Fixed glBuffers not deleted in VoxelSystem" This reverts commit 9224bca88b1150fe32f1573f453078342ba12abd. --- interface/src/VoxelSystem.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 9c31040ca5..b0cbcf02c3 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -115,10 +115,6 @@ void VoxelSystem::clearFreeBufferIndexes() { } VoxelSystem::~VoxelSystem() { - glDeleteBuffers(1, &_vboVerticesID); - glDeleteBuffers(1, &_vboNormalsID); - glDeleteBuffers(1, &_vboColorsID); - glDeleteBuffers(1, &_vboIndicesID); delete[] _readVerticesArray; delete[] _writeVerticesArray; delete[] _readColorsArray; From 26691118de431585757f1e295c093f40d2131df4 Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 3 Sep 2013 10:30:46 -0700 Subject: [PATCH 29/31] Some code reformatting. --- interface/src/ImportDialog.cpp | 2 +- interface/src/VoxelSystem.h | 10 ++--- libraries/voxels/src/ViewFrustum.h | 66 +++++++++++++++--------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/interface/src/ImportDialog.cpp b/interface/src/ImportDialog.cpp index beda8977cd..eea090dfbd 100644 --- a/interface/src/ImportDialog.cpp +++ b/interface/src/ImportDialog.cpp @@ -195,7 +195,7 @@ ImportDialog::~ImportDialog() { void ImportDialog::init() { VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem(); - connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float, float, float))); + connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float,float,float))); connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int))); } diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index ed635dae3f..48204295ef 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -44,11 +44,11 @@ public: void render(bool texture); void changeTree(VoxelTree* newTree); - VoxelTree* getTree() const {return _tree;} - ViewFrustum* getViewFrustum() const {return _viewFrustum;} - void setViewFrustum(ViewFrustum* viewFrustum) {_viewFrustum = viewFrustum;} - unsigned long getVoxelsUpdated() const {return _voxelsUpdated;}; - unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;}; + VoxelTree* getTree() const { return _tree; } + ViewFrustum* getViewFrustum() const { return _viewFrustum; } + void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } + unsigned long getVoxelsUpdated() const { return _voxelsUpdated; } + unsigned long getVoxelsRendered() const { return _voxelsInReadArrays; } void loadVoxelsFile(const char* fileName,bool wantColorRandomizer); void writeToSVOFile(const char* filename, VoxelNode* node) const; diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 188b85c0de..aea81f7b7a 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -24,50 +24,50 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f; class ViewFrustum { public: // setters for camera attributes - void setPosition (const glm::vec3& p) { _position = p; }; - void setOrientation (const glm::quat& orientationAsQuaternion); + void setPosition(const glm::vec3& p) { _position = p; } + void setOrientation(const glm::quat& orientationAsQuaternion); // getters for camera attributes - const glm::vec3& getPosition() const { return _position; }; - const glm::quat& getOrientation() const { return _orientation; }; - const glm::vec3& getDirection() const { return _direction; }; - const glm::vec3& getUp() const { return _up; }; - const glm::vec3& getRight() const { return _right; }; + const glm::vec3& getPosition() const { return _position; } + const glm::quat& getOrientation() const { return _orientation; } + const glm::vec3& getDirection() const { return _direction; } + const glm::vec3& getUp() const { return _up; } + const glm::vec3& getRight() const { return _right; } // setters for lens attributes - void setFieldOfView ( float f ) { _fieldOfView = f; }; - void setAspectRatio ( float a ) { _aspectRatio = a; }; - void setNearClip ( float n ) { _nearClip = n; }; - void setFarClip ( float f ) { _farClip = f; }; - void setEyeOffsetPosition (const glm::vec3& p) { _eyeOffsetPosition = p; }; - void setEyeOffsetOrientation (const glm::quat& o) { _eyeOffsetOrientation = o; }; + void setFieldOfView(float f) { _fieldOfView = f; } + void setAspectRatio(float a) { _aspectRatio = a; } + void setNearClip(float n) { _nearClip = n; } + void setFarClip(float f) { _farClip = f; } + void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } + void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } // getters for lens attributes - float getFieldOfView() const { return _fieldOfView; }; - float getAspectRatio() const { return _aspectRatio; }; - float getNearClip() const { return _nearClip; }; - float getFarClip() const { return _farClip; }; - const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }; - const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation;}; + float getFieldOfView() const { return _fieldOfView; } + float getAspectRatio() const { return _aspectRatio; } + float getNearClip() const { return _nearClip; } + float getFarClip() const { return _farClip; } + const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; } + const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; } - const glm::vec3& getOffsetPosition() const { return _offsetPosition; }; - const glm::vec3& getOffsetDirection() const { return _offsetDirection;}; - const glm::vec3& getOffsetUp() const { return _offsetUp; }; - const glm::vec3& getOffsetRight() const { return _offsetRight; }; + const glm::vec3& getOffsetPosition() const { return _offsetPosition; } + const glm::vec3& getOffsetDirection() const { return _offsetDirection; } + const glm::vec3& getOffsetUp() const { return _offsetUp; } + const glm::vec3& getOffsetRight() const { return _offsetRight; } - const glm::vec3& getFarTopLeft() const { return _farTopLeft; }; - const glm::vec3& getFarTopRight() const { return _farTopRight; }; - const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; }; - const glm::vec3& getFarBottomRight() const { return _farBottomRight; }; + const glm::vec3& getFarTopLeft() const { return _farTopLeft; } + const glm::vec3& getFarTopRight() const { return _farTopRight; } + const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; } + const glm::vec3& getFarBottomRight() const { return _farBottomRight; } - const glm::vec3& getNearTopLeft() const { return _nearTopLeft; }; - const glm::vec3& getNearTopRight() const { return _nearTopRight; }; - const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; }; - const glm::vec3& getNearBottomRight() const { return _nearBottomRight;}; + const glm::vec3& getNearTopLeft() const { return _nearTopLeft; } + const glm::vec3& getNearTopRight() const { return _nearTopRight; } + const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; } + const glm::vec3& getNearBottomRight() const { return _nearBottomRight; } // get/set for keyhole attribute - void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; }; - float getKeyholeRadius() const { return _keyholeRadius; }; + void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; } + float getKeyholeRadius() const { return _keyholeRadius; } void calculate(); From 15a3a8d252e95499f8efe57b5d81c98fc90e818a Mon Sep 17 00:00:00 2001 From: atlante45 Date: Tue, 3 Sep 2013 10:32:38 -0700 Subject: [PATCH 30/31] Some more code reformatting. --- interface/src/VoxelSystem.h | 6 +++--- libraries/voxels/src/ViewFrustum.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 48204295ef..189d19f866 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -34,13 +34,13 @@ public: VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); - void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; }; - int getDataSourceID() const { return _dataSourceID; }; + void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; } + int getDataSourceID() const { return _dataSourceID; } int parseData(unsigned char* sourceBuffer, int numBytes); virtual void init(); - void simulate(float deltaTime) { }; + void simulate(float deltaTime) { } void render(bool texture); void changeTree(VoxelTree* newTree); diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index aea81f7b7a..22830cb48d 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -81,7 +81,7 @@ public: // some frustum comparisons bool matches(const ViewFrustum& compareTo, bool debug = false) const; - bool matches(const ViewFrustum* compareTo, bool debug = false) const { return matches(*compareTo, debug); }; + bool matches(const ViewFrustum* compareTo, bool debug = false) const { return matches(*compareTo, debug); } void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const; From 843d5656bd63cb651945139fe1a758398ec5e7d0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 3 Sep 2013 10:45:33 -0700 Subject: [PATCH 31/31] Fix for compile warnings on Linux: check return values on read/write. --- interface/src/SerialInterface.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/SerialInterface.cpp b/interface/src/SerialInterface.cpp index d9c1bd928f..4082bc87ef 100644 --- a/interface/src/SerialInterface.cpp +++ b/interface/src/SerialInterface.cpp @@ -125,9 +125,15 @@ void SerialInterface::initializePort(char* portname) { tcflush(_serialDescriptor, TCIOFLUSH); // this disables streaming so there's no garbage data on reads - write(_serialDescriptor, "SD\n", 3); + if (write(_serialDescriptor, "SD\n", 3) != 3) { + qDebug("Failed.\n"); + return; + } char result[4]; - read(_serialDescriptor, result, 4); + if (read(_serialDescriptor, result, 4) != 4) { + qDebug("Failed.\n"); + return; + } tty_set_file_descriptor(_serialDescriptor); mpu_init(0);