diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index 7b909b4e11..4d0577b960 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -697,8 +697,7 @@ int main(int argc, const char * argv[]) ::wantLocalDomain = cmdOptionExists(argc, argv,local); if (::wantLocalDomain) { printf("Local Domain MODE!\n"); - int ip = getLocalAddress(); - sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + nodeList->setDomainIPToLocalhost(); } nodeList->linkedDataCreateCallback = NULL; // do we need a callback? diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index b900c48479..239eb7a32d 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -70,16 +70,15 @@ bool wantLocalDomain = false; int main(int argc, const char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT); + // Handle Local Domain testing with the --local command line const char* local = "--local"; ::wantLocalDomain = cmdOptionExists(argc, argv,local); if (::wantLocalDomain) { printf("Local Domain MODE!\n"); - int ip = getLocalAddress(); - sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); - } - - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AUDIO_MIXER, MIXER_LISTEN_PORT); + nodeList->setDomainIPToLocalhost(); + } ssize_t receivedBytes = 0; diff --git a/avatar-mixer/src/main.cpp b/avatar-mixer/src/main.cpp index 497797d2e8..bff0b529f8 100644 --- a/avatar-mixer/src/main.cpp +++ b/avatar-mixer/src/main.cpp @@ -60,8 +60,7 @@ int main(int argc, const char* argv[]) { const char* local = "--local"; if (cmdOptionExists(argc, argv, local)) { printf("Local Domain MODE!\n"); - int ip = getLocalAddress(); - sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + nodeList->setDomainIPToLocalhost(); } nodeList->linkedDataCreateCallback = attachAvatarDataToNode; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 343dff4525..e04895d38e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,6 +82,7 @@ const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) +static QTimer* idleTimer = NULL; const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that @@ -227,14 +228,14 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : const char* domainIP = getCmdOption(argc, constArgv, "--domain"); if (domainIP) { - strcpy(DOMAIN_IP, domainIP); + NodeList::getInstance()->setDomainIP(domainIP); } // Handle Local Domain testing with the --local command line if (cmdOptionExists(argc, constArgv, "--local")) { printLog("Local Domain MODE!\n"); - int ip = getLocalAddress(); - sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + + NodeList::getInstance()->setDomainIPToLocalhost(); } // Check to see if the user passed in a command line option for loading a local @@ -341,7 +342,7 @@ void Application::initializeGL() { timer->start(1000); // call our idle function whenever we can - QTimer* idleTimer = new QTimer(this); + idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); _idleLoopStdev.reset(); @@ -973,19 +974,9 @@ void Application::idle() { gettimeofday(&check, NULL); // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran - sendPostedEvents(NULL, QEvent::TouchBegin); - sendPostedEvents(NULL, QEvent::TouchUpdate); - sendPostedEvents(NULL, QEvent::TouchEnd); double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check); if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) { - - // If we're using multi-touch look, immediately process any - // touch events, and no other events. - // This is necessary because id the idle() call takes longer than the - // interval between idle() calls, the event loop never gets to run, - // and touch events get delayed. - const float BIGGEST_DELTA_TIME_SECS = 0.25f; update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS)); _glWidget->updateGL(); @@ -998,6 +989,9 @@ void Application::idle() { _idleLoopMeasuredJitter = _idleLoopStdev.getStDev(); _idleLoopStdev.reset(); } + + // After finishing all of the above work, restart the idle timer, allowing 2ms to process events. + idleTimer->start(2); } } void Application::terminate() { @@ -1101,8 +1095,14 @@ void Application::editPreferences() { 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); + QLineEdit* avatarURL = new QLineEdit(_myAvatar.getVoxels()->getVoxelURL().toString()); - avatarURL->setMinimumWidth(400); + avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); form->addRow("Avatar URL:", avatarURL); QSpinBox* horizontalFieldOfView = new QSpinBox(); @@ -1133,9 +1133,33 @@ void Application::editPreferences() { if (dialog.exec() != QDialog::Accepted) { return; } + + + const char* newHostname = domainServerHostname->text().toLocal8Bit().data(); + + // check if the domain server hostname is new + if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname, sizeof(&newHostname)) != 0) { + // if so we need to clear the nodelist and delete the local voxels + Node *voxelServer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_VOXEL_SERVER); + + if (voxelServer) { + voxelServer->lock(); + } + + _voxels.killLocalVoxels(); + + if (voxelServer) { + voxelServer->unlock(); + } + + NodeList::getInstance()->clear(); + NodeList::getInstance()->setDomainHostname(newHostname); + } + QUrl url(avatarURL->text()); _myAvatar.getVoxels()->setVoxelURL(url); sendAvatarVoxelURLMessage(url); + _headCameraPitchYawScale = headCameraPitchYawScale->value(); _myAvatar.setLeanScale(leanScale->value()); _audioJitterBufferSamples = audioJitterBufferSamples->value(); @@ -1214,6 +1238,12 @@ void Application::setRenderWarnings(bool renderWarnings) { _voxels.setRenderPipelineWarnings(renderWarnings); } +void Application::setRenderVoxels(bool voxelRender) { + if (!voxelRender) { + doKillLocalVoxels(); + } +} + void Application::doKillLocalVoxels() { _wantToKillLocalVoxels = true; } @@ -1333,7 +1363,9 @@ struct SendVoxelsOperationArgs { unsigned char* newBaseOctCode; unsigned char messageBuffer[MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE]; int bufferInUse; - + uint64_t lastSendTime; + int packetsSent; + uint64_t bytesSent; }; bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { @@ -1365,17 +1397,30 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_INDEX ]; - // TODO: sendVoxelsOperation() is sending voxels too fast. - // This printf function accidently slowed down sending - // and hot-fixed the bug when importing - // large PNG models (256x256 px and more) - static unsigned int sendVoxelsOperationCalled = 0; printf("sending voxel #%u\n", ++sendVoxelsOperationCalled); - // if we have room don't have room in the buffer, then send the previously generated message first if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { + + args->packetsSent++; + args->bytesSent += args->bufferInUse; + controlledBroadcastToNodes(args->messageBuffer, args->bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); args->bufferInUse = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset + + uint64_t now = usecTimestampNow(); + // dynamically sleep until we need to fire off the next set of voxels + const uint64_t CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS = 1000 * 5; // 1 packet every 10 milliseconds + uint64_t elapsed = now - args->lastSendTime; + int usecToSleep = CLIENT_TO_SERVER_VOXEL_SEND_INTERVAL_USECS - elapsed; + if (usecToSleep > 0) { + printLog("sendVoxelsOperation: packet: %d bytes:%ld elapsed %ld usecs, sleeping for %d usecs!\n", + args->packetsSent, args->bytesSent, elapsed, usecToSleep); + usleep(usecToSleep); + } else { + printLog("sendVoxelsOperation: packet: %d bytes:%ld elapsed %ld usecs, no need to sleep!\n", + args->packetsSent, args->bytesSent, elapsed); + } + args->lastSendTime = now; } // copy this node's code color details into our buffer. @@ -1404,14 +1449,15 @@ void Application::exportVoxels() { _window->activateWindow(); } -void Application::importVoxels() { +const char* IMPORT_FILE_TYPES = "Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)"; +void Application::importVoxelsToClipboard() { QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); - QString fileNameString = QFileDialog::getOpenFileName( - _glWidget, tr("Import Voxels"), desktopLocation, - tr("Sparse Voxel Octree Files, Square PNG, Schematic Files (*.svo *.png *.schematic)")); - - const char* fileName = fileNameString.toAscii().data(); + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels to Clipboard"), desktopLocation, + tr(IMPORT_FILE_TYPES)); + QByteArray fileNameAscii = fileNameString.toAscii(); + const char* fileName = fileNameAscii.data(); + _clipboardTree.eraseAllVoxels(); if (fileNameString.endsWith(".png", Qt::CaseInsensitive)) { QImage pngImage = QImage(fileName); @@ -1427,7 +1473,6 @@ void Application::importVoxels() { QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32); pixels = reinterpret_cast(tmp.constBits()); } - _clipboardTree.readFromSquareARGB32Pixels(pixels, pngImage.height()); } else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) { _clipboardTree.readFromSVOFile(fileName); @@ -1439,6 +1484,76 @@ void Application::importVoxels() { _window->activateWindow(); } +void Application::importVoxels() { + QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation, + tr(IMPORT_FILE_TYPES)); + + QByteArray fileNameAscii = fileNameString.toAscii(); + const char* fileName = fileNameAscii.data(); + + VoxelTree importVoxels; + if (fileNameString.endsWith(".png", Qt::CaseInsensitive)) { + QImage pngImage = QImage(fileName); + if (pngImage.height() != pngImage.width()) { + printLog("ERROR: Bad PNG size: height != width.\n"); + return; + } + + const uint32_t* pixels; + if (pngImage.format() == QImage::Format_ARGB32) { + pixels = reinterpret_cast(pngImage.constBits()); + } else { + QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32); + pixels = reinterpret_cast(tmp.constBits()); + } + + importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height()); + } else if (fileNameString.endsWith(".svo", Qt::CaseInsensitive)) { + importVoxels.readFromSVOFile(fileName); + } else if (fileNameString.endsWith(".schematic", Qt::CaseInsensitive)) { + importVoxels.readFromSchematicFile(fileName); + } + + VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + + // Recurse the Import Voxels tree, where everything is root relative, and send all the colored voxels to + // the server as an set voxel message, this will also rebase the voxels to the new location + unsigned char* calculatedOctCode = NULL; + SendVoxelsOperationArgs args; + args.lastSendTime = usecTimestampNow(); + args.packetsSent = 0; + args.bytesSent = 0; + + int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); + + unsigned short int* sequenceAt = (unsigned short int*)&args.messageBuffer[numBytesPacketHeader]; + *sequenceAt = 0; + args.bufferInUse = numBytesPacketHeader + sizeof(unsigned short int); // set to command + sequence + + // we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the + // voxel size/position details. + if (selectedNode) { + args.newBaseOctCode = selectedNode->getOctalCode(); + } else { + args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); + } + + importVoxels.recurseTreeWithOperation(sendVoxelsOperation, &args); + + // If we have voxels left in the packet, then send the packet + if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) { + controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); + } + + if (calculatedOctCode) { + delete[] calculatedOctCode; + } + + // restore the main window's active state + _window->activateWindow(); +} + void Application::cutVoxels() { copyVoxels(); deleteVoxelUnderCursor(); @@ -1462,6 +1577,9 @@ void Application::pasteVoxels() { // Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to // the server as an set voxel message, this will also rebase the voxels to the new location SendVoxelsOperationArgs args; + args.lastSendTime = usecTimestampNow(); + args.packetsSent = 0; + args.bytesSent = 0; int numBytesPacketHeader = populateTypeAndVersion(args.messageBuffer, PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); @@ -1483,6 +1601,9 @@ void Application::pasteVoxels() { // If we have voxels left in the packet, then send the packet if (args.bufferInUse > (numBytesPacketHeader + sizeof(unsigned short int))) { controlledBroadcastToNodes(args.messageBuffer, args.bufferInUse, & NODE_TYPE_VOXEL_SERVER, 1); + printLog("sending packet: %d\n", ++args.packetsSent); + args.bytesSent += args.bufferInUse; + printLog("total bytes sent: %ld\n", args.bytesSent); } if (calculatedOctCode) { @@ -1524,9 +1645,8 @@ void Application::initMenu() { optionsMenu->addAction("Go Home", this, SLOT(goHome())); QMenu* renderMenu = menuBar->addMenu("Render"); - (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); + (_renderVoxels = renderMenu->addAction("Voxels", this, SLOT(setRenderVoxels(bool)), Qt::SHIFT | Qt::Key_V))->setCheckable(true); _renderVoxels->setChecked(true); - _renderVoxels->setShortcut(Qt::SHIFT | Qt::Key_V); (_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true); (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); _renderStarsOn->setChecked(true); @@ -1602,6 +1722,7 @@ void Application::initMenu() { voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E); voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I); + voxelMenu->addAction("Import Voxels to Clipboard", this, SLOT(importVoxelsToClipboard()), Qt::SHIFT | Qt::CTRL | Qt::Key_I); voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X); voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C); voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V); @@ -1927,8 +2048,8 @@ void Application::update(float deltaTime) { //loop through all the other avatars and simulate them... NodeList* nodeList = NodeList::getInstance(); - nodeList->lock(); for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->lock(); if (node->getLinkedData() != NULL) { Avatar *avatar = (Avatar *)node->getLinkedData(); if (!avatar->isInitialized()) { @@ -1937,8 +2058,8 @@ void Application::update(float deltaTime) { avatar->simulate(deltaTime, NULL); avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); } + node->unlock(); } - nodeList->unlock(); // Simulate myself if (_gravityUse->isChecked()) { @@ -2427,8 +2548,10 @@ void Application::displaySide(Camera& whichCamera) { if (_renderAvatarsOn->isChecked()) { // Render avatars of other nodes NodeList* nodeList = NodeList::getInstance(); - nodeList->lock(); + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->lock(); + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar *avatar = (Avatar *)node->getLinkedData(); if (!avatar->isInitialized()) { @@ -2437,8 +2560,9 @@ void Application::displaySide(Camera& whichCamera) { avatar->render(false, _renderAvatarBalls->isChecked()); avatar->setDisplayingLookatVectors(_renderLookatOn->isChecked()); } + + node->unlock(); } - nodeList->unlock(); // Render my own Avatar if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -3187,7 +3311,7 @@ void* Application::networkReceive(void* args) { if (app->_wantToKillLocalVoxels) { app->_voxels.killLocalVoxels(); app->_wantToKillLocalVoxels = false; - } + } if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) { app->_packetCount++; @@ -3208,11 +3332,23 @@ void* Application::networkReceive(void* args) { case PACKET_TYPE_VOXEL_DATA_MONOCHROME: case PACKET_TYPE_Z_COMMAND: case PACKET_TYPE_ERASE_VOXEL: - app->_voxels.parseData(app->_incomingPacket, bytesReceived); - break; - case PACKET_TYPE_ENVIRONMENT_DATA: - app->_environment.parseData(&senderAddress, app->_incomingPacket, bytesReceived); + case PACKET_TYPE_ENVIRONMENT_DATA: { + if (app->_renderVoxels->isChecked()) { + Node* voxelServer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_VOXEL_SERVER); + if (voxelServer) { + voxelServer->lock(); + + if (app->_incomingPacket[0] == PACKET_TYPE_ENVIRONMENT_DATA) { + app->_environment.parseData(&senderAddress, app->_incomingPacket, bytesReceived); + } else { + app->_voxels.parseData(app->_incomingPacket, bytesReceived); + } + + voxelServer->unlock(); + } + } break; + } case PACKET_TYPE_BULK_AVATAR_DATA: NodeList::getInstance()->processBulkNodeData(&senderAddress, app->_incomingPacket, diff --git a/interface/src/Application.h b/interface/src/Application.h index 7d56e4d673..32364cb695 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -128,6 +128,7 @@ private slots: void cycleFrustumRenderMode(); void setRenderWarnings(bool renderWarnings); + void setRenderVoxels(bool renderVoxels); void doKillLocalVoxels(); void doRandomizeVoxelColors(); void doFalseRandomizeVoxelColors(); @@ -153,6 +154,7 @@ private slots: void exportSettings(); void exportVoxels(); void importVoxels(); + void importVoxelsToClipboard(); void cutVoxels(); void copyVoxels(); void pasteVoxels(); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 73f971b7da..63fc24f56d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -96,6 +96,10 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o Node* audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); if (audioMixer) { + audioMixer->lock(); + sockaddr_in audioSocket = *(sockaddr_in*) audioMixer->getActiveSocket(); + audioMixer->unlock(); + glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition(); glm::quat headOrientation = interfaceAvatar->getHead().getOrientation(); @@ -122,12 +126,13 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL); - nodeList->getNodeSocket()->send(audioMixer->getActiveSocket(), - dataPacket, - BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); + nodeList->getNodeSocket()->send((sockaddr*) &audioSocket, + dataPacket, + BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); - interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO) - .updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); + interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO).updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL + + leadingBytes); + } } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 0a6f9b741b..9752fe2a45 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1330,7 +1330,11 @@ void Avatar::loadData(QSettings* settings) { _voxels.setVoxelURL(settings->value("voxelURL").toUrl()); _leanScale = loadSetting(settings, "leanScale", 0.5f); - + + _scale = loadSetting(settings, "scale", 1.0f); + setScale(_scale); + Application::getInstance()->getCamera()->setScale(_scale); + settings->endGroup(); } @@ -1353,6 +1357,7 @@ void Avatar::saveData(QSettings* set) { set->setValue("voxelURL", _voxels.getVoxelURL()); set->setValue("leanScale", _leanScale); + set->setValue("scale", _scale); set->endGroup(); } diff --git a/interface/src/starfield/renderer/Renderer.h b/interface/src/starfield/renderer/Renderer.h index 92fe53f69f..7eab70d178 100644 --- a/interface/src/starfield/renderer/Renderer.h +++ b/interface/src/starfield/renderer/Renderer.h @@ -509,6 +509,8 @@ namespace starfield { _program.release(); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); glDisable(GL_POINT_SMOOTH); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); glPopMatrix(); } diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 3fddc71ad1..f7c9758fd0 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -53,6 +53,8 @@ Node::Node(sockaddr* publicSocket, sockaddr* localSocket, char type, uint16_t no } else { _localSocket = NULL; } + + pthread_mutex_init(&_mutex, 0); } Node::~Node() { @@ -60,6 +62,8 @@ Node::~Node() { delete _localSocket; delete _linkedData; delete _bytesReceivedMovingAverage; + + pthread_mutex_destroy(&_mutex); } // Names of Node Types diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index de43558b7a..aa88cb158c 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -65,6 +65,9 @@ public: int getPingMs() const { return _pingMs; }; void setPingMs(int pingMs) { _pingMs = pingMs; }; + + void lock() { pthread_mutex_lock(&_mutex); } + void unlock() { pthread_mutex_unlock(&_mutex); } static void printLog(Node const&); private: @@ -83,6 +86,7 @@ private: NodeData* _linkedData; bool _isAlive; int _pingMs; + pthread_mutex_t _mutex; }; diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 391e22a915..004568584d 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -29,9 +29,9 @@ const char SOLO_NODE_TYPES[3] = { NODE_TYPE_VOXEL_SERVER }; -char DOMAIN_HOSTNAME[] = "highfidelity.below92.com"; -char DOMAIN_IP[100] = ""; // IP Address will be re-set by lookup on startup -const int DOMAINSERVER_PORT = 40102; +const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES] = "root.highfidelity.io"; +const char DEFAULT_DOMAIN_IP[INET_ADDRSTRLEN] = ""; // IP Address will be re-set by lookup on startup +const int DEFAULT_DOMAINSERVER_PORT = 40102; bool silentNodeThreadStopFlag = false; bool pingUnknownNodeThreadStopFlag = false; @@ -63,17 +63,37 @@ NodeList::NodeList(char newOwnerType, unsigned int newSocketListenPort) : _ownerType(newOwnerType), _nodeTypesOfInterest(NULL), _ownerID(UNKNOWN_NODE_ID), - _lastNodeID(0) { - pthread_mutex_init(&mutex, 0); + _lastNodeID(0) +{ + memcpy(_domainHostname, DEFAULT_DOMAIN_HOSTNAME, sizeof(DEFAULT_DOMAIN_HOSTNAME)); + memcpy(_domainIP, DEFAULT_DOMAIN_IP, sizeof(DEFAULT_DOMAIN_IP)); } NodeList::~NodeList() { delete _nodeTypesOfInterest; + clear(); + // stop the spawned threads, if they were started stopSilentNodeRemovalThread(); +} + +void NodeList::setDomainHostname(const char* domainHostname) { + memset(_domainHostname, 0, sizeof(_domainHostname)); + memcpy(_domainHostname, domainHostname, strlen(domainHostname)); - pthread_mutex_destroy(&mutex); + // reset the domain IP so the hostname is checked again + setDomainIP(""); +} + +void NodeList::setDomainIP(const char* domainIP) { + memset(_domainIP, 0, sizeof(_domainIP)); + memcpy(_domainIP, domainIP, strlen(domainIP)); +} + +void NodeList::setDomainIPToLocalhost() { + int ip = getLocalAddress(); + sprintf(_domainIP, "%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); } void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { @@ -92,7 +112,14 @@ void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetData, size_t dataBytes) { switch (packetData[0]) { case PACKET_TYPE_DOMAIN: { - processDomainServerList(packetData, dataBytes); + // only process the DS if this is our current domain server + sockaddr_in domainServerSocket = *(sockaddr_in*) senderAddress; + const char* domainSenderIP = inet_ntoa(domainServerSocket.sin_addr); + + if (memcmp(domainSenderIP, _domainIP, strlen(domainSenderIP)) == 0) { + processDomainServerList(packetData, dataBytes); + } + break; } case PACKET_TYPE_PING: { @@ -110,7 +137,6 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat } void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes) { - lock(); // find the avatar mixer in our node list and update the lastRecvTime from it Node* bulkSendNode = nodeWithAddress(senderAddress); @@ -118,38 +144,37 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe if (bulkSendNode) { bulkSendNode->setLastHeardMicrostamp(usecTimestampNow()); bulkSendNode->recordBytesReceived(numTotalBytes); - } - - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - - unsigned char *startPosition = packetData; - unsigned char *currentPosition = startPosition + numBytesPacketHeader; - unsigned char packetHolder[numTotalBytes]; - - // we've already verified packet version for the bulk packet, so all head data in the packet is also up to date - populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA); - - uint16_t nodeID = -1; - - while ((currentPosition - startPosition) < numTotalBytes) { - unpackNodeId(currentPosition, &nodeID); - memcpy(packetHolder + numBytesPacketHeader, - currentPosition, - numTotalBytes - (currentPosition - startPosition)); - Node* matchingNode = nodeWithID(nodeID); + int numBytesPacketHeader = numBytesForPacketHeader(packetData); - if (!matchingNode) { - // we're missing this node, we need to add it to the list - matchingNode = addOrUpdateNode(NULL, NULL, NODE_TYPE_AGENT, nodeID); + unsigned char* startPosition = packetData; + unsigned char* currentPosition = startPosition + numBytesPacketHeader; + unsigned char packetHolder[numTotalBytes]; + + // we've already verified packet version for the bulk packet, so all head data in the packet is also up to date + populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA); + + uint16_t nodeID = -1; + + while ((currentPosition - startPosition) < numTotalBytes) { + unpackNodeId(currentPosition, &nodeID); + memcpy(packetHolder + numBytesPacketHeader, + currentPosition, + numTotalBytes - (currentPosition - startPosition)); + + Node* matchingNode = nodeWithID(nodeID); + + if (!matchingNode) { + // we're missing this node, we need to add it to the list + matchingNode = addOrUpdateNode(NULL, NULL, NODE_TYPE_AGENT, nodeID); + } + + currentPosition += updateNodeWithData(matchingNode, + packetHolder, + numTotalBytes - (currentPosition - startPosition)); + } - - currentPosition += updateNodeWithData(matchingNode, - packetHolder, - numTotalBytes - (currentPosition - startPosition)); - } - - unlock(); + } } int NodeList::updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) { @@ -164,6 +189,8 @@ int NodeList::updateNodeWithData(sockaddr *senderAddress, unsigned char *packetD } int NodeList::updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes) { + node->lock(); + node->setLastHeardMicrostamp(usecTimestampNow()); if (node->getActiveSocket()) { @@ -174,7 +201,11 @@ int NodeList::updateNodeWithData(Node *node, unsigned char *packetData, int data linkedDataCreateCallback(node); } - return node->getLinkedData()->parseData(packetData, dataBytes); + int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes); + + node->unlock(); + + return numParsedBytes; } Node* NodeList::nodeWithAddress(sockaddr *senderAddress) { @@ -209,6 +240,21 @@ int NodeList::getNumAliveNodes() const { return numAliveNodes; } +void NodeList::clear() { + // delete all of the nodes in the list, set the pointers back to NULL and the number of nodes to 0 + for (int i = 0; i < _numNodes; i++) { + Node** nodeBucket = _nodeBuckets[i / NODES_PER_BUCKET]; + Node* node = nodeBucket[i % NODES_PER_BUCKET]; + + node->lock(); + delete node; + + node = NULL; + } + + _numNodes = 0; +} + void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) { delete _nodeTypesOfInterest; @@ -221,18 +267,19 @@ void NodeList::sendDomainServerCheckIn() { static bool printedDomainServerIP = false; // Lookup the IP address of the domain server if we need to - if (atoi(DOMAIN_IP) == 0) { + if (atoi(_domainIP) == 0) { + printf("Looking up %s\n", _domainHostname); struct hostent* pHostInfo; - if ((pHostInfo = gethostbyname(DOMAIN_HOSTNAME)) != NULL) { + if ((pHostInfo = gethostbyname(_domainHostname)) != NULL) { sockaddr_in tempAddress; memcpy(&tempAddress.sin_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); - strcpy(DOMAIN_IP, inet_ntoa(tempAddress.sin_addr)); - printLog("Domain Server: %s \n", DOMAIN_HOSTNAME); + strcpy(_domainIP, inet_ntoa(tempAddress.sin_addr)); + printLog("Domain Server: %s \n", _domainHostname); } else { printLog("Failed domain server lookup\n"); } } else if (!printedDomainServerIP) { - printLog("Domain Server IP: %s\n", DOMAIN_IP); + printLog("Domain Server IP: %s\n", _domainIP); printedDomainServerIP = true; } @@ -279,7 +326,7 @@ void NodeList::sendDomainServerCheckIn() { checkInPacketSize = packetPosition - checkInPacket; } - _nodeSocket.send(DOMAIN_IP, DOMAINSERVER_PORT, checkInPacket, checkInPacketSize); + _nodeSocket.send(_domainIP, DEFAULT_DOMAINSERVER_PORT, checkInPacket, checkInPacketSize); } int NodeList::processDomainServerList(unsigned char* packetData, size_t dataBytes) { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index da9c4a556a..20eaddc738 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -9,8 +9,10 @@ #ifndef __hifi__NodeList__ #define __hifi__NodeList__ +#include #include #include +#include #include "Node.h" #include "UDPSocket.h" @@ -30,9 +32,11 @@ const int DOMAIN_SERVER_CHECK_IN_USECS = 1 * 1000000; extern const char SOLO_NODE_TYPES[3]; -extern char DOMAIN_HOSTNAME[]; -extern char DOMAIN_IP[100]; // IP Address will be re-set by lookup on startup -extern const int DOMAINSERVER_PORT; +const int MAX_HOSTNAME_BYTES = 255; + +extern const char DEFAULT_DOMAIN_HOSTNAME[MAX_HOSTNAME_BYTES]; +extern const char DEFAULT_DOMAIN_IP[INET_ADDRSTRLEN]; // IP Address will be re-set by lookup on startup +extern const int DEFAULT_DOMAINSERVER_PORT; const int UNKNOWN_NODE_ID = -1; @@ -48,6 +52,12 @@ public: NodeListIterator begin() const; NodeListIterator end() const; + const char* getDomainHostname() const { return _domainHostname; }; + void setDomainHostname(const char* domainHostname); + + void setDomainIP(const char* domainIP); + void setDomainIPToLocalhost(); + char getOwnerType() const { return _ownerType; } uint16_t getLastNodeID() const { return _lastNodeID; } @@ -65,8 +75,7 @@ public: int size() { return _numNodes; } int getNumAliveNodes() const; - void lock() { pthread_mutex_lock(&mutex); } - void unlock() { pthread_mutex_unlock(&mutex); } + void clear(); void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); void sendDomainServerCheckIn(); @@ -101,6 +110,8 @@ private: void addNodeToList(Node* newNode); + char _domainHostname[MAX_HOSTNAME_BYTES]; + char _domainIP[INET_ADDRSTRLEN]; Node** _nodeBuckets[MAX_NUM_NODES / NODES_PER_BUCKET]; int _numNodes; UDPSocket _nodeSocket; @@ -111,7 +122,6 @@ private: uint16_t _lastNodeID; pthread_t removeSilentNodesThread; pthread_t checkInWithDomainServerThread; - pthread_mutex_t mutex; void handlePingReply(sockaddr *nodeAddress); void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index b84b374309..66fa81c425 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1251,7 +1251,7 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // wants occlusion culling & isLeaf() - bool shouldRender = childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); + bool shouldRender = !params.viewFrustum ? true : childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); // track children with actual color, only if the child wasn't previously in view! if (shouldRender && !childIsOccluded) { diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 254b1d2d96..df7011585d 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -313,6 +313,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, uint64_t lastPersistVoxels = 0; void persistVoxelsWhenDirty() { uint64_t now = usecTimestampNow(); + if (::lastPersistVoxels == 0) { + ::lastPersistVoxels = now; + } int sinceLastTime = (now - ::lastPersistVoxels) / 1000; // check the dirty bit and persist here... @@ -383,8 +386,7 @@ int main(int argc, const char * argv[]) { ::wantLocalDomain = cmdOptionExists(argc, argv,local); if (::wantLocalDomain) { printf("Local Domain MODE!\n"); - int ip = getLocalAddress(); - sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); + nodeList->setDomainIPToLocalhost(); } nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode;