diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b0a2be54c..9227b7be11 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -225,6 +225,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _audio(&_audioScope, STARTUP_JITTER_SAMPLES), #endif _stopNetworkReceiveThread(false), + _stopProcessVoxelsThread(false), _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), @@ -249,7 +250,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : NodeList::getInstance()->addHook(this); - _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); + // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line + _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); if (!_enableNetworkThread) { NodeList::getInstance()->getNodeSocket()->setBlocking(false); } @@ -373,6 +375,12 @@ void Application::initializeGL() { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); qDebug("Network receive thread created.\n"); } + + // create thread for parsing of voxel data independent of the main network and rendering threads + if (_enableProcessVoxelsThread) { + pthread_create(&_processVoxelsThread, NULL, processVoxels, NULL); + qDebug("Voxel parsing thread created.\n"); + } // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); @@ -1178,6 +1186,11 @@ void Application::terminate() { _stopNetworkReceiveThread = true; pthread_join(_networkReceiveThread, NULL); } + + if (_enableProcessVoxelsThread) { + _stopProcessVoxelsThread = true; + pthread_join(_processVoxelsThread, NULL); + } } void Application::sendAvatarVoxelURLMessage(const QUrl& url) { @@ -1502,8 +1515,8 @@ void Application::doTreeStats() { _voxels.collectStatsForTreesAndVBOs(); } -void Application::setWantsLowResMoving(bool wantsLowResMoving) { - _myAvatar.setWantLowResMoving(wantsLowResMoving); +void Application::disableLowResMoving(bool disableLowResMoving) { + _myAvatar.setWantLowResMoving(!disableLowResMoving); } void Application::setWantsMonochrome(bool wantsMonochrome) { @@ -2082,7 +2095,8 @@ void Application::initMenu() { debugMenu->addAction("Calculate Tree Stats", this, SLOT(doTreeStats()), Qt::SHIFT | Qt::Key_S); QMenu* renderDebugMenu = debugMenu->addMenu("Render Debugging Tools"); - renderDebugMenu->addAction("Show Render Pipeline Warnings", this, SLOT(setRenderWarnings(bool)))->setCheckable(true); + (_renderPipelineWarnings = renderDebugMenu->addAction("Show Render Pipeline Warnings", + this, SLOT(setRenderWarnings(bool))))->setCheckable(true); renderDebugMenu->addAction("Kill Local Voxels", this, SLOT(doKillLocalVoxels()), Qt::CTRL | Qt::Key_K); renderDebugMenu->addAction("Randomize Voxel TRUE Colors", this, SLOT(doRandomizeVoxelColors()), Qt::CTRL | Qt::Key_R); renderDebugMenu->addAction("FALSE Color Voxels Randomly", this, SLOT(doFalseRandomizeVoxelColors())); @@ -2097,7 +2111,7 @@ void Application::initMenu() { (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); - debugMenu->addAction("Use Lower Resolution While Moving", this, SLOT(setWantsLowResMoving(bool)))->setCheckable(true); + debugMenu->addAction("Disable Lower Resolution While Moving", this, SLOT(disableLowResMoving(bool)))->setCheckable(true); debugMenu->addAction("Disable Delta Sending", this, SLOT(disableDeltaSending(bool)))->setCheckable(true); (_occlusionCulling = debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)), Qt::SHIFT | Qt::Key_C))->setCheckable(true); @@ -2574,6 +2588,11 @@ void Application::update(float deltaTime) { if (!_enableNetworkThread) { networkReceive(0); } + + // parse voxel packets + if (!_enableProcessVoxelsThread) { + processVoxels(0); + } //loop through all the other avatars and simulate them... NodeList* nodeList = NodeList::getInstance(); @@ -4035,6 +4054,75 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng return statsMessageLength; } +// Receive packets from other nodes/servers and decide what to do with them! +void* Application::processVoxels(void* args) { + Application* app = Application::getInstance(); + while (!app->_stopProcessVoxelsThread) { + + // check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that + if (app->_wantToKillLocalVoxels) { + app->_voxels.killLocalVoxels(); + app->_wantToKillLocalVoxels = false; + } + + while (app->_voxelPackets.size() > 0) { + NetworkPacket& packet = app->_voxelPackets.front(); + app->processVoxelPacket(packet.getSenderAddress(), packet.getData(), packet.getLength()); + app->_voxelPackets.erase(app->_voxelPackets.begin()); + } + + if (!app->_enableProcessVoxelsThread) { + break; + } + } + + if (app->_enableProcessVoxelsThread) { + pthread_exit(0); + } + return NULL; +} + +void Application::queueVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { + _voxelPackets.push_back(NetworkPacket(senderAddress, packetData, packetLength)); +} + +void Application::processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { + PerformanceWarning warn(_renderPipelineWarnings->isChecked(),"processVoxelPacket()"); + ssize_t messageLength = packetLength; + + // note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME + // immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first + // then process any remaining bytes as if it was another packet + if (packetData[0] == PACKET_TYPE_VOXEL_STATS) { + + int statsMessageLength = parseVoxelStats(packetData, messageLength, senderAddress); + if (messageLength > statsMessageLength) { + packetData += statsMessageLength; + messageLength -= statsMessageLength; + if (!packetVersionMatch(packetData)) { + return; // bail since piggyback data doesn't match our versioning + } + } else { + return; // bail since no piggyback data + } + } // fall through to piggyback message + + if (_renderVoxels->isChecked()) { + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); + if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { + voxelServer->lock(); + if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { + _environment.parseData(&senderAddress, packetData, messageLength); + } else { + _voxels.setDataSourceID(voxelServer->getNodeID()); + _voxels.parseData(packetData, messageLength); + _voxels.setDataSourceID(UNKNOWN_NODE_ID); + } + voxelServer->unlock(); + } + } +} + // Receive packets from other nodes/servers and decide what to do with them! void* Application::networkReceive(void* args) { sockaddr senderAddress; @@ -4070,41 +4158,8 @@ void* Application::networkReceive(void* args) { case PACKET_TYPE_ERASE_VOXEL: case PACKET_TYPE_VOXEL_STATS: case PACKET_TYPE_ENVIRONMENT_DATA: { - - unsigned char* messageData = app->_incomingPacket; - ssize_t messageLength = bytesReceived; - - // note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME - // immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first - // then process any remaining bytes as if it was another packet - if (messageData[0] == PACKET_TYPE_VOXEL_STATS) { - - int statsMessageLength = app->parseVoxelStats(messageData, messageLength, senderAddress); - if (messageLength > statsMessageLength) { - messageData += statsMessageLength; - messageLength -= statsMessageLength; - if (!packetVersionMatch(messageData)) { - break; // bail since piggyback data doesn't match our versioning - } - } else { - break; // bail since no piggyback data - } - } // fall through to piggyback message - - if (app->_renderVoxels->isChecked()) { - Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); - if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { - voxelServer->lock(); - if (messageData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { - app->_environment.parseData(&senderAddress, messageData, messageLength); - } else { - app->_voxels.setDataSourceID(voxelServer->getNodeID()); - app->_voxels.parseData(messageData, messageLength); - app->_voxels.setDataSourceID(UNKNOWN_NODE_ID); - } - voxelServer->unlock(); - } - } + // add this packet to our list of voxel packets and process them on the voxel processing + app->queueVoxelPacket(senderAddress, app->_incomingPacket, bytesReceived); break; } case PACKET_TYPE_BULK_AVATAR_DATA: diff --git a/interface/src/Application.h b/interface/src/Application.h index f2c9c38e15..bbd1b3ed7b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -69,8 +70,6 @@ static const float NODE_KILLED_RED = 1.0f; static const float NODE_KILLED_GREEN = 0.0f; static const float NODE_KILLED_BLUE = 0.0f; - - class Application : public QApplication, public NodeListHook { Q_OBJECT @@ -175,7 +174,7 @@ private slots: void doTrueVoxelColors(); void doTreeStats(); void setWantsMonochrome(bool wantsMonochrome); - void setWantsLowResMoving(bool wantsLowResMoving); + void disableLowResMoving(bool disableLowResMoving); void disableDeltaSending(bool disableDeltaSending); void disableOcclusionCulling(bool disableOcclusionCulling); void updateVoxelModeActions(); @@ -264,7 +263,11 @@ private: QAction* checkedVoxelModeAction() const; static void attachNewHeadToNode(Node *newNode); - static void* networkReceive(void* args); + static void* networkReceive(void* args); // network receive thread + + static void* processVoxels(void* args); // voxel parsing thread + void processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + void queueVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); // methodes handling menu settings typedef void(*settingsAction)(QSettings*, QAction*); @@ -317,6 +320,7 @@ private: QAction* _noise; QAction* _occlusionCulling; QAction* _wantCollisionsOn; + QAction* _renderPipelineWarnings; QAction* _renderCoverageMapV2; QAction* _renderCoverageMap; @@ -455,6 +459,12 @@ private: pthread_t _networkReceiveThread; bool _stopNetworkReceiveThread; + bool _enableProcessVoxelsThread; + pthread_t _processVoxelsThread; + bool _stopProcessVoxelsThread; + std::vector _voxelPackets; + + unsigned char _incomingPacket[MAX_PACKET_SIZE]; int _packetCount; int _packetsPerSecond; diff --git a/interface/src/VoxelFade.cpp b/interface/src/VoxelFade.cpp index 0bbcbeedc1..f7ec97ae30 100644 --- a/interface/src/VoxelFade.cpp +++ b/interface/src/VoxelFade.cpp @@ -13,11 +13,11 @@ #include "VoxelFade.h" const float VoxelFade::FADE_OUT_START = 0.5f; -const float VoxelFade::FADE_OUT_END = 0.0f; -const float VoxelFade::FADE_OUT_STEP = -0.005f; -const float VoxelFade::FADE_IN_START = 0.0f; +const float VoxelFade::FADE_OUT_END = 0.05f; +const float VoxelFade::FADE_OUT_STEP = 0.9f; +const float VoxelFade::FADE_IN_START = 0.05f; const float VoxelFade::FADE_IN_END = 0.5f; -const float VoxelFade::FADE_IN_STEP = 0.005f; +const float VoxelFade::FADE_IN_STEP = 1.1f; const float VoxelFade::DEFAULT_RED = 0.5f; const float VoxelFade::DEFAULT_GREEN = 0.5f; const float VoxelFade::DEFAULT_BLUE = 0.5f; @@ -45,7 +45,7 @@ void VoxelFade::render() { glPopMatrix(); glEnable(GL_LIGHTING); - opacity += (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP; + opacity *= (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP; } bool VoxelFade::isDone() const { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 796e5e5884..d7aae578d5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -39,7 +39,7 @@ AvatarData::AvatarData(Node* owningNode) : _keyState(NO_KEY_DOWN), _wantColor(true), _wantDelta(true), - _wantLowResMoving(false), + _wantLowResMoving(true), _wantOcclusionCulling(true), _headData(NULL), _handData(NULL) diff --git a/libraries/shared/src/NetworkPacket.cpp b/libraries/shared/src/NetworkPacket.cpp new file mode 100644 index 0000000000..99dc00e9e2 --- /dev/null +++ b/libraries/shared/src/NetworkPacket.cpp @@ -0,0 +1,29 @@ +// +// NetworkPacket.cpp +// shared +// +// Created by Brad Hefta-Gaub on 8/9/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// A really simple class that stores a network packet between being received and being processed +// + +#include +#include + +#include "NetworkPacket.h" + +NetworkPacket::NetworkPacket() : _packetLength(0) { +} + +NetworkPacket::NetworkPacket(const NetworkPacket& packet) { + memcpy(&_senderAddress, &packet.getSenderAddress(), sizeof(_senderAddress)); + _packetLength = packet.getLength(); + memcpy(&_packetData[0], packet.getData(), _packetLength); +} + +NetworkPacket::NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { + memcpy(&_senderAddress, &senderAddress, sizeof(_senderAddress)); + _packetLength = packetLength; + memcpy(&_packetData[0], packetData, packetLength); +}; diff --git a/libraries/shared/src/NetworkPacket.h b/libraries/shared/src/NetworkPacket.h new file mode 100644 index 0000000000..9f35004443 --- /dev/null +++ b/libraries/shared/src/NetworkPacket.h @@ -0,0 +1,40 @@ +// +// NetworkPacket.h +// shared +// +// Created by Brad Hefta-Gaub on 8/9/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// A really simple class that stores a network packet between being received and being processed +// + +#ifndef __shared_NetworkPacket__ +#define __shared_NetworkPacket__ + +#include +#include +#include + +#include "NodeList.h" // for MAX_PACKET_SIZE + +class NetworkPacket { +public: + NetworkPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); + NetworkPacket(const NetworkPacket& packet); + NetworkPacket(); + //~NetworkPacket(); + + sockaddr& getSenderAddress() { return _senderAddress; }; + ssize_t getLength() const { return _packetLength; }; + unsigned char* getData() { return &_packetData[0]; }; + + const sockaddr& getSenderAddress() const { return _senderAddress; }; + const unsigned char* getData() const { return &_packetData[0]; }; + +private: + sockaddr _senderAddress; + ssize_t _packetLength; + unsigned char _packetData[MAX_PACKET_SIZE]; +}; + +#endif /* defined(__shared_NetworkPacket__) */ diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp index 36913a2d6c..990f0fa5b8 100644 --- a/voxel-edit/src/main.cpp +++ b/voxel-edit/src/main.cpp @@ -101,7 +101,7 @@ int main(int argc, const char * argv[]) // jurisdiction of the server // This hack assumes the end nodes for demo dinner since it only guarantees // nodes in the 8 child voxels of the main root voxel - const float verySmall = verySmall; + const float verySmall = 0.015625; endNodeTree.createVoxel(0.0, 0.0, 0.0, verySmall, 1, 1, 1, true); endNodeTree.createVoxel(1.0, 0.0, 0.0, verySmall, 1, 1, 1, true); endNodeTree.createVoxel(0.0, 1.0, 0.0, verySmall, 1, 1, 1, true);