diff --git a/audio-mixer/src/main.cpp b/audio-mixer/src/main.cpp index d473a8172e..c14df9ca43 100644 --- a/audio-mixer/src/main.cpp +++ b/audio-mixer/src/main.cpp @@ -138,7 +138,7 @@ int main(int argc, const char* argv[]) { const char MIXER_LOGSTASH_METRIC_NAME[] = "audio-mixer-frame-time-usage"; float averageFrameTimePercentage = sumFrameTimePercentages / numStatCollections; - Logstash::stashValue(MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage); + Logstash::stashValue(STAT_TYPE_GAUGE, MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage); sumFrameTimePercentages = 0.0f; numStatCollections = 0; diff --git a/cmake/modules/FindLeap.cmake b/cmake/modules/FindLeap.cmake index 121d422131..f93499b4d2 100755 --- a/cmake/modules/FindLeap.cmake +++ b/cmake/modules/FindLeap.cmake @@ -32,9 +32,9 @@ else (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) endif () # If we're using the Leap stubs, there's only a header, no lib. - if (LEAP_INCLUDE_DIRS) + if (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) set(LEAP_FOUND TRUE) - endif (LEAP_INCLUDE_DIRS) + endif (LEAP_LIBRARIES AND LEAP_INCLUDE_DIRS) if (LEAP_FOUND) if (NOT Leap_FIND_QUIETLY) diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 2ea7ecf9f6..5139e13dd8 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -17,35 +17,23 @@ // M - Audio Mixer // -#include +#include +#include #include #include #include #include -#include -#include + #include "AgentList.h" #include "AgentTypes.h" -#include +#include "Logstash.h" +#include "PacketHeaders.h" #include "SharedUtil.h" -#ifdef _WIN32 -#include "Syssocket.h" -#include "Systime.h" -#else -#include -#include -#include -#include -#endif - - const int DOMAIN_LISTEN_PORT = 40102; unsigned char packetData[MAX_PACKET_SIZE]; -const int LOGOFF_CHECK_INTERVAL = 5000; - -int lastActiveCount = 0; +const int NODE_COUNT_STAT_INTERVAL_MSECS = 5000; unsigned char* addAgentToBroadcastPacket(unsigned char* currentPosition, Agent* agentToAdd) { *currentPosition++ = agentToAdd->getType(); @@ -94,6 +82,8 @@ int main(int argc, const char * argv[]) agentList->startSilentAgentRemovalThread(); + timeval lastStatSendTime = {}; + while (true) { if (agentList->getAgentSocket()->receive((sockaddr *)&agentPublicAddress, packetData, &receivedBytes) && (packetData[0] == PACKET_HEADER_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_HEADER_DOMAIN_LIST_REQUEST)) { @@ -185,6 +175,17 @@ int main(int argc, const char * argv[]) broadcastPacket, (currentBufferPos - startPointer) + 1); } + + if (Logstash::shouldSendStats()) { + if (usecTimestampNow() - usecTimestamp(&lastStatSendTime) >= (NODE_COUNT_STAT_INTERVAL_MSECS * 1000)) { + // time to send our count of agents and servers to logstash + const char NODE_COUNT_LOGSTASH_KEY[] = "ds-node-count"; + + Logstash::stashValue(STAT_TYPE_GAUGE, NODE_COUNT_LOGSTASH_KEY, agentList->getNumAliveAgents()); + + gettimeofday(&lastStatSendTime, NULL); + } + } } return 0; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 72a79a1bd8..4eff24f3a6 100755 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -74,7 +74,7 @@ add_subdirectory(external/fervor/) include_directories(external/fervor/) # run qt moc on qt-enabled headers -qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h src/Webcam.h) +qt4_wrap_cpp(INTERFACE_SRCS src/Application.h src/AvatarVoxelSystem.h src/Webcam.h src/ui/BandwidthDialog.h) # create the executable, make it a bundle on OS X add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS}) @@ -112,6 +112,7 @@ include_directories( ${OPENCV_INCLUDE_DIRS} ) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${OPENCV_INCLUDE_DIRS}") target_link_libraries( ${TARGET_NAME} ${QT_LIBRARIES} @@ -153,8 +154,12 @@ if (APPLE) ${QuartzCore} ${UVCCAMERACONTROL_LIBRARIES} ${LIBOVR_LIBRARIES} - ${LEAP_LIBRARIES} ) + + if (LEAP_FOUND) + target_link_libraries(${TARGET_NAME} ${LEAP_LIBRARIES}) + endif(LEAP_FOUND) + else (APPLE) find_package(OpenGL REQUIRED) find_package(GLUT REQUIRED) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 33289e4e61..010db97c9c 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,8 @@ using namespace std; static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; static char STAR_CACHE_FILE[] = "cachedStars.txt"; +static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored + const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own agent begins in the world // (will be overwritten if avatar data file is found) @@ -162,6 +165,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), + _bandwidthDialog(NULL), _displayLevels(false), _frameCount(0), _fps(120.0f), @@ -350,7 +354,7 @@ void Application::initializeGL() { const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; // ask the Logstash class to record the startup time - Logstash::stashValue(LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); + Logstash::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } // update before the first render @@ -451,7 +455,7 @@ void Application::resizeGL(int width, int height) { } } else { camera.setAspectRatio(aspectRatio); - camera.setFieldOfView(fov = 60); + camera.setFieldOfView(fov = _horizontalFieldOfView); } // Tell our viewFrustum about this change @@ -482,11 +486,30 @@ void Application::resizeGL(int width, int height) { glLoadIdentity(); } -static void sendVoxelServerAddScene() { +void Application::broadcastToAgents(unsigned char* data, size_t bytes, const char type) { + + int n = AgentList::getInstance()->broadcastToAgents(data, bytes, &type, 1); + + BandwidthMeter::ChannelIndex channel; + switch (type) { + case AGENT_TYPE_AVATAR: + case AGENT_TYPE_AVATAR_MIXER: + channel = BandwidthMeter::AVATARS; + break; + case AGENT_TYPE_VOXEL_SERVER: + channel = BandwidthMeter::VOXELS; + break; + default: + return; + } + getInstance()->_bandwidthMeter.outputStream(channel).updateValue(n * bytes); +} + +void Application::sendVoxelServerAddScene() { char message[100]; sprintf(message,"%c%s",'Z',"add scene"); int messageSize = strlen(message) + 1; - AgentList::getInstance()->broadcastToAgents((unsigned char*)message, messageSize, &AGENT_TYPE_VOXEL_SERVER, 1); + broadcastToAgents((unsigned char*)message, messageSize, AGENT_TYPE_VOXEL_SERVER); } void Application::keyPressEvent(QKeyEvent* event) { @@ -813,6 +836,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { _mouseX = event->x(); _mouseY = event->y(); _mousePressed = false; + checkBandwidthMeterClick(); } } } @@ -953,7 +977,7 @@ void Application::terminate() { } } -static void sendAvatarVoxelURLMessage(const QUrl& url) { +void Application::sendAvatarVoxelURLMessage(const QUrl& url) { uint16_t ownerID = AgentList::getInstance()->getOwnerID(); if (ownerID == UNKNOWN_AGENT_ID) { @@ -964,10 +988,10 @@ static void sendAvatarVoxelURLMessage(const QUrl& url) { message.append((const char*)&ownerID, sizeof(ownerID)); message.append(url.toEncoded()); - AgentList::getInstance()->broadcastToAgents((unsigned char*)message.data(), message.size(), &AGENT_TYPE_AVATAR_MIXER, 1); + broadcastToAgents((unsigned char*)message.data(), message.size(), AGENT_TYPE_AVATAR_MIXER); } -static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes) { +void Application::processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes) { // skip the header packetData++; dataBytes--; @@ -992,6 +1016,37 @@ static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataB QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, url)); } +void Application::checkBandwidthMeterClick() { + // ... to be called upon button release + + if (_bandwidthDisplayOn->isChecked() && + glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH && + _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) { + + // The bandwidth meter is visible, the click didn't get dragged too far and + // we actually hit the bandwidth meter + bandwidthDetails(); + } +} + +void Application::bandwidthDetails() { + + if (! _bandwidthDialog) { + _bandwidthDialog = new BandwidthDialog(_glWidget, getBandwidthMeter()); + connect(_bandwidthDialog, SIGNAL(closed()), SLOT(bandwidthDetailsClosed())); + + _bandwidthDialog->show(); + } + _bandwidthDialog->raise(); +} + +void Application::bandwidthDetailsClosed() { + + QDialog* dlg = _bandwidthDialog; + _bandwidthDialog = NULL; + delete dlg; +} + void Application::editPreferences() { QDialog dialog(_glWidget); dialog.setWindowTitle("Interface Preferences"); @@ -1005,6 +1060,12 @@ void Application::editPreferences() { avatarURL->setMinimumWidth(400); form->addRow("Avatar URL:", avatarURL); + QSpinBox* horizontalFieldOfView = new QSpinBox(); + horizontalFieldOfView->setMaximum(180); + horizontalFieldOfView->setMinimum(1); + horizontalFieldOfView->setValue(_horizontalFieldOfView); + form->addRow("Horizontal field of view (degrees):", horizontalFieldOfView); + QDoubleSpinBox* headCameraPitchYawScale = new QDoubleSpinBox(); headCameraPitchYawScale->setValue(_headCameraPitchYawScale); form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); @@ -1036,6 +1097,9 @@ void Application::editPreferences() { if (!shouldDynamicallySetJitterBuffer()) { _audio.setJitterBufferSamples(_audioJitterBufferSamples); } + _horizontalFieldOfView = horizontalFieldOfView->value(); + resizeGL(_glWidget->width(), _glWidget->height()); + } void Application::pair() { @@ -1152,12 +1216,12 @@ void Application::updateVoxelModeActions() { } } -static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) { +void Application::sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) { unsigned char* bufferOut; int sizeOut; if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){ - AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL_SERVER, 1); + Application::broadcastToAgents(bufferOut, sizeOut, AGENT_TYPE_VOXEL_SERVER); delete[] bufferOut; } } @@ -1233,10 +1297,16 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ]; 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) { - AgentList::getInstance()->broadcastToAgents(args->messageBuffer, args->bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1); + broadcastToAgents(args->messageBuffer, args->bufferInUse, AGENT_TYPE_VOXEL_SERVER); args->bufferInUse = sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int); // reset } @@ -1269,14 +1339,31 @@ void Application::exportVoxels() { void Application::importVoxels() { QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation, - tr("Sparse Voxel Octree Files (*.svo)")); + tr("Sparse Voxel Octree Files, Square PNG (*.svo *.png)")); QByteArray fileNameAscii = fileNameString.toAscii(); const char* fileName = fileNameAscii.data(); - - // Read the file into a tree + VoxelTree importVoxels; - importVoxels.readFromSVOFile(fileName); - + 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 { + importVoxels.readFromSVOFile(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 @@ -1300,7 +1387,7 @@ void Application::importVoxels() { // If we have voxels left in the packet, then send the packet if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { - AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1); + broadcastToAgents(args.messageBuffer, args.bufferInUse, AGENT_TYPE_VOXEL_SERVER); } if (calculatedOctCode) { @@ -1352,7 +1439,7 @@ void Application::pasteVoxels() { // If we have voxels left in the packet, then send the packet if (args.bufferInUse > (sizeof(PACKET_HEADER_SET_VOXEL_DESTRUCTIVE) + sizeof(unsigned short int))) { - AgentList::getInstance()->broadcastToAgents(args.messageBuffer, args.bufferInUse, &AGENT_TYPE_VOXEL_SERVER, 1); + broadcastToAgents(args.messageBuffer, args.bufferInUse, AGENT_TYPE_VOXEL_SERVER); } if (calculatedOctCode) { @@ -1429,7 +1516,11 @@ void Application::initMenu() { (_logOn = toolsMenu->addAction("Log"))->setCheckable(true); _logOn->setChecked(false); _logOn->setShortcut(Qt::CTRL | Qt::Key_L); - + (_bandwidthDisplayOn = toolsMenu->addAction("Bandwidth Display"))->setCheckable(true); + _bandwidthDisplayOn->setChecked(true); + toolsMenu->addAction("Bandwidth Details", this, SLOT(bandwidthDetails())); + + QMenu* voxelMenu = menuBar->addMenu("Voxels"); _voxelModeActions = new QActionGroup(this); _voxelModeActions->setExclusive(false); // exclusivity implies one is always checked @@ -1561,7 +1652,7 @@ void Application::init() { _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); - _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); @@ -1627,7 +1718,7 @@ void Application::update(float deltaTime) { _myAvatar.getHead().setLookAtPosition(myLookAtFromMouse); // If we are dragging on a voxel, add thrust according to the amount the mouse is dragging - const float VOXEL_GRAB_THRUST = 5.0f; + const float VOXEL_GRAB_THRUST = 0.0f; if (_mousePressed && (_mouseVoxel.s != 0)) { glm::vec2 mouseDrag(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY); glm::quat orientation = _myAvatar.getOrientation(); @@ -1801,7 +1892,7 @@ void Application::update(float deltaTime) { _myCamera.setModeShiftRate(1.0f); } } else { - const float THIRD_PERSON_SHIFT_VELOCITY = 2.0f; + const float THIRD_PERSON_SHIFT_VELOCITY = 1000.0f; const float TIME_BEFORE_SHIFT_INTO_FIRST_PERSON = 0.75f; const float TIME_BEFORE_SHIFT_INTO_THIRD_PERSON = 0.1f; @@ -1818,6 +1909,11 @@ void Application::update(float deltaTime) { } } } + + // Update bandwidth dialog, if any + if (_bandwidthDialog) { + _bandwidthDialog->update(); + } // Update audio stats for procedural sounds #ifndef _WIN32 @@ -1901,8 +1997,8 @@ void Application::updateAvatar(float deltaTime) { endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL_SERVER, AGENT_TYPE_AVATAR_MIXER}; - AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers)); + broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, AGENT_TYPE_VOXEL_SERVER); + broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, AGENT_TYPE_AVATAR_MIXER); // once in a while, send my voxel url const float AVATAR_VOXEL_URL_SEND_INTERVAL = 1.0f; // seconds @@ -2290,6 +2386,9 @@ void Application::displayOverlay() { glPointSize(1.0f); if (_renderStatsOn->isChecked()) { displayStats(); } + + if (_bandwidthDisplayOn->isChecked()) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); } + if (_logOn->isChecked()) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); } // Show chat entry field @@ -2851,6 +2950,7 @@ void* Application::networkReceive(void* args) { AgentList::getInstance()->processBulkAgentData(&senderAddress, app->_incomingPacket, bytesReceived); + getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); break; case PACKET_HEADER_AVATAR_VOXEL_URL: processAvatarVoxelURLMessage(app->_incomingPacket, bytesReceived); @@ -2914,6 +3014,8 @@ void Application::loadSettings(QSettings* settings) { _headCameraPitchYawScale = loadSetting(settings, "headCameraPitchYawScale", 0.0f); _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); + _horizontalFieldOfView = loadSetting(settings, "horizontalFieldOfView", HORIZONTAL_FIELD_OF_VIEW_DEGREES); + settings->beginGroup("View Frustum Offset Camera"); // in case settings is corrupt or missing loadSetting() will check for NaN _viewFrustumOffsetYaw = loadSetting(settings, "viewFrustumOffsetYaw" , 0.0f); @@ -2936,6 +3038,7 @@ void Application::saveSettings(QSettings* settings) { settings->setValue("headCameraPitchYawScale", _headCameraPitchYawScale); settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); + settings->setValue("horizontalFieldOfView", _horizontalFieldOfView); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffsetYaw); settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffsetPitch); diff --git a/interface/src/Application.h b/interface/src/Application.h index ca60af97c9..73ae597da4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -21,6 +21,9 @@ #include +#include "BandwidthMeter.h" +#include "ui/BandwidthDialog.h" + #ifndef _WIN32 #include "Audio.h" #endif @@ -32,6 +35,7 @@ #include "Stars.h" #include "ViewFrustum.h" #include "VoxelSystem.h" +#include "PacketHeaders.h" #include "Webcam.h" #include "renderer/GeometryCache.h" #include "ui/ChatEntry.h" @@ -86,6 +90,7 @@ public: Environment* getEnvironment() { return &_environment; } SerialInterface* getSerialHeadSensor() { return &_serialHeadSensor; } Webcam* getWebcam() { return &_webcam; } + BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } bool shouldEchoAudio() { return _echoAudioMode->isChecked(); } bool shouldLowPassFilter() { return _shouldLowPassFilter->isChecked(); } @@ -99,8 +104,10 @@ private slots: void timer(); void idle(); void terminate(); - + + void bandwidthDetails(); void editPreferences(); + void bandwidthDetailsClosed(); void pair(); @@ -148,7 +155,12 @@ private slots: void runTests(); private: + static void broadcastToAgents(unsigned char* data, size_t bytes, const char type); + static void sendVoxelServerAddScene(); static bool sendVoxelsOperation(VoxelNode* node, void* extraData); + static void sendAvatarVoxelURLMessage(const QUrl& url); + static void processAvatarVoxelURLMessage(unsigned char *packetData, size_t dataBytes); + static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail); void initMenu(); void updateFrustumRenderModeAction(); @@ -164,7 +176,9 @@ private: void displayOverlay(); void displayStats(); void renderViewFrustum(ViewFrustum& viewFrustum); - + + void checkBandwidthMeterClick(); + void setupPaintingVoxel(); void shiftPaintingColor(); void maybeEditVoxelUnderCursor(); @@ -215,6 +229,7 @@ private: QAction* _manualFirstPerson; // Whether to force first-person mode QAction* _manualThirdPerson; // Whether to force third-person mode QAction* _logOn; // Whether to show on-screen log + QAction* _bandwidthDisplayOn; // Whether to show on-screen bandwidth bars QActionGroup* _voxelModeActions; // The group of voxel edit mode actions QAction* _addVoxelMode; // Whether add voxel mode is enabled QAction* _deleteVoxelMode; // Whether delete voxel mode is enabled @@ -229,6 +244,9 @@ private: QAction* _frustumRenderModeAction; QAction* _settingsAutosave; // Whether settings are saved automatically + BandwidthMeter _bandwidthMeter; + BandwidthDialog* _bandwidthDialog; + SerialInterface _serialHeadSensor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; @@ -243,7 +261,7 @@ private: timeval _timerStart, _timerEnd; timeval _lastTimeIdle; bool _justStarted; - + Stars _stars; VoxelSystem _voxels; @@ -282,6 +300,8 @@ private: int _audioJitterBufferSamples; // Number of extra samples to wait before starting audio playback + float _horizontalFieldOfView; // In Degrees, doesn't apply to HMD like Oculus + HandControl _handControl; int _mouseX; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index d0a49f4fe5..e673d2b1b1 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -120,10 +120,12 @@ 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); - agentList->getAgentSocket()->send(audioMixer->getActiveSocket(), dataPacket, BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); + + interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO) + .updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); } } @@ -335,16 +337,23 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : // Manually initialize the portaudio stream to ask for minimum latency PaStreamParameters inputParameters, outputParameters; - inputParameters.device = Pa_GetDefaultInputDevice(); + inputParameters.device = Pa_GetDefaultInputDevice(); + outputParameters.device = Pa_GetDefaultOutputDevice(); + + if (inputParameters.device == -1 || outputParameters.device == -1) { + printLog("Audio: Missing device.\n"); + outputPortAudioError(Pa_Terminate()); + return; + } + inputParameters.channelCount = 2; // Stereo input inputParameters.sampleFormat = (paInt16 | paNonInterleaved); - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; - outputParameters.device = Pa_GetDefaultOutputDevice(); outputParameters.channelCount = 2; // Stereo output outputParameters.sampleFormat = (paInt16 | paNonInterleaved); - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; outputPortAudioError(Pa_OpenStream(&_stream, @@ -438,7 +447,10 @@ void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBy //printf("Got audio packet %d\n", _packetsReceivedThisPlayback); _ringBuffer.parseData((unsigned char*) receivedData, PACKET_LENGTH_BYTES + sizeof(PACKET_HEADER)); - + + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO) + .updateValue(PACKET_LENGTH_BYTES + sizeof(PACKET_HEADER)); + _lastReceiveTime = currentReceiveTime; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 4382a86d87..a3c8cf1046 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -10,7 +10,6 @@ #define __interface__Audio__ #include - #include #include diff --git a/interface/src/Balls.cpp b/interface/src/Balls.cpp index 8b3f45ce40..20613b3448 100644 --- a/interface/src/Balls.cpp +++ b/interface/src/Balls.cpp @@ -19,7 +19,7 @@ #include "world.h" const float INITIAL_AREA = 0.2f; -const float BALL_RADIUS = 0.025f; +const float BALL_RADIUS = 0.016f; const glm::vec3 INITIAL_COLOR(0.62f, 0.74f, 0.91f); Balls::Balls(int numberOfBalls) { @@ -83,8 +83,9 @@ void Balls::render() { } const float CONSTANT_VELOCITY_DAMPING = 1.0f; -const float NOISE_SCALE = 0.00; -const float SPRING_FORCE = 1.0; +const float NOISE_SCALE = 0.06; +const float SPRING_FORCE = 30.0; +const float ORIGIN_DISTANCE = 0.1; const float SPRING_DAMPING = 1.0; void Balls::simulate(float deltaTime) { @@ -100,10 +101,21 @@ void Balls::simulate(float deltaTime) { // Add noise _balls[i].velocity += randVector() * NOISE_SCALE; + // spring force to origin + float separation = glm::distance(_balls[i].position, + _origin); + + _balls[i].velocity += + glm::normalize(_balls[i].position - _origin) + * deltaTime + * + SPRING_FORCE * + (ORIGIN_DISTANCE - separation); + // Approach target position - for (unsigned int i = 0; i < _numberOfBalls; ++i) { - _balls[i].position += randFloat() * deltaTime * (_balls[i].targetPosition - _balls[i].position); - } +// for (unsigned int i = 0; i < _numberOfBalls; ++i) { +// _balls[i].position += randFloat() * deltaTime * (_balls[i].targetPosition - _balls[i].position); +// } // Spring Force diff --git a/interface/src/BandwidthMeter.cpp b/interface/src/BandwidthMeter.cpp new file mode 100644 index 0000000000..ba89864807 --- /dev/null +++ b/interface/src/BandwidthMeter.cpp @@ -0,0 +1,230 @@ +// +// BandwidthMeter.h +// interface +// +// Created by Tobias Schwinger on 6/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include "BandwidthMeter.h" +#include "InterfaceConfig.h" + +#include "Log.h" +#include "Util.h" + +namespace { // .cpp-local + + int const AREA_WIDTH = -400; // Width of the area used. Aligned to the right when negative. + int const AREA_HEIGHT = 40; // Height of the area used. Aligned to the bottom when negative. + int const BORDER_DISTANCE_HORIZ = -20; // Distance to edge of screen (use negative value when width is negative). + int const BORDER_DISTANCE_VERT = 40; // Distance to edge of screen (use negative value when height is negative). + + int SPACING_VERT_BARS = 2; // Vertical distance between input and output bar + int SPACING_RIGHT_CAPTION_IN_OUT = 4; // IN/OUT <--> |######## : | + int SPACING_LEFT_CAPTION_UNIT = 4; // |######## : | <--> UNIT + int PADDING_HORIZ_VALUE = 2; // |<-->X.XX<:-># | + + unsigned const COLOR_TEXT = 0xe0e0e0e0; // ^ ^ ^ ^ ^ ^ + unsigned const COLOR_FRAME = 0xe0e0e0b0; // | | | + unsigned const COLOR_INDICATOR = 0xc0c0c0b0; // | + + char const* CAPTION_IN = "IN"; + char const* CAPTION_OUT = "OUT"; + char const* CAPTION_UNIT = "Mbps"; + + double const UNIT_SCALE = 8000.0 / (1024.0 * 1024.0); // Bytes/ms -> Mbps + int const INITIAL_SCALE_MAXIMUM_INDEX = 250; // / 9: exponent, % 9: mantissa - 2, 0 o--o 2 * 10^-10 +} + +BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = { + { "Audio" , "Kbps", 8000.0 / 1024.0, 0x40ff40d0 }, + { "Avatars" , "Kbps", 8000.0 / 1024.0, 0xffef40c0 }, + { "Voxels" , "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0 } +}; + +BandwidthMeter::BandwidthMeter() : + _textRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT), + _scaleMaxIndex(INITIAL_SCALE_MAXIMUM_INDEX) { + + _channels = static_cast( malloc(sizeof(_CHANNELS)) ); + memcpy(_channels, _CHANNELS, sizeof(_CHANNELS)); +} + +BandwidthMeter::~BandwidthMeter() { + + free(_channels); +} + +BandwidthMeter::Stream::Stream(float msToAverage) : + _value(0.0f), + _msToAverage(msToAverage) { + + gettimeofday(& _prevTime, NULL); +} + +void BandwidthMeter::Stream::updateValue(double amount) { + + // Determine elapsed time + timeval now; + gettimeofday(& now, NULL); + double dt = diffclock(& _prevTime, & now); + memcpy(& _prevTime, & now, sizeof(timeval)); + + // Compute approximate average + _value = glm::mix(_value, amount / dt, + glm::clamp(dt / _msToAverage, 0.0, 1.0)); +} + +void BandwidthMeter::setColorRGBA(unsigned c) { + + glColor4ub(GLubyte( c >> 24), + GLubyte((c >> 16) & 0xff), + GLubyte((c >> 8) & 0xff), + GLubyte( c & 0xff)); +} + +void BandwidthMeter::renderBox(int x, int y, int w, int h) { + + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x + w, y); + glVertex2i(x + w, y + h); + glVertex2i(x, y + h); + glEnd(); +} + +void BandwidthMeter::renderVerticalLine(int x, int y, int h) { + + glBegin(GL_LINES); + glVertex2i(x, y); + glVertex2i(x, y + h); + glEnd(); +} + +inline int BandwidthMeter::centered(int subject, int object) { + return (object - subject) / 2; +} + +bool BandwidthMeter::isWithinArea(int x, int y, int screenWidth, int screenHeight) { + + int minX = BORDER_DISTANCE_HORIZ + (AREA_WIDTH >= 0 ? 0 : screenWidth + AREA_WIDTH); + int minY = BORDER_DISTANCE_VERT + (AREA_HEIGHT >= 0 ? 0 : screenHeight + AREA_HEIGHT); + + return x >= minX && x < minX + glm::abs(AREA_WIDTH) && + y >= minY && y < minY + glm::abs(AREA_HEIGHT); +} + +void BandwidthMeter::render(int screenWidth, int screenHeight) { + + int x = BORDER_DISTANCE_HORIZ + (AREA_WIDTH >= 0 ? 0 : screenWidth + AREA_WIDTH); + int y = BORDER_DISTANCE_VERT + (AREA_HEIGHT >= 0 ? 0 : screenHeight + AREA_HEIGHT); + int w = glm::abs(AREA_WIDTH), h = glm::abs(AREA_HEIGHT); + + // Determine total + float totalIn = 0.0f, totalOut = 0.0f; + for (int i = 0; i < N_CHANNELS; ++i) { + + totalIn += inputStream(ChannelIndex(i)).getValue(); + totalOut += outputStream(ChannelIndex(i)).getValue(); + } + totalIn *= UNIT_SCALE; + totalOut *= UNIT_SCALE; + float totalMax = glm::max(totalIn, totalOut); + + // Get font / caption metrics + QFontMetrics const& fontMetrics = _textRenderer.metrics(); + int fontDescent = fontMetrics.descent(); + int labelWidthIn = fontMetrics.width(CAPTION_IN); + int labelWidthOut = fontMetrics.width(CAPTION_OUT); + int labelWidthInOut = glm::max(labelWidthIn, labelWidthOut); + int labelHeight = fontMetrics.ascent() + fontDescent; + int labelWidthUnit = fontMetrics.width(CAPTION_UNIT); + int labelsWidth = labelWidthInOut + SPACING_RIGHT_CAPTION_IN_OUT + SPACING_LEFT_CAPTION_UNIT + labelWidthUnit; + + // Calculate coordinates and dimensions + int barX = x + labelWidthInOut + SPACING_RIGHT_CAPTION_IN_OUT; + int barWidth = w - labelsWidth; + int barHeight = (h - SPACING_VERT_BARS) / 2; + int textYcenteredLine = h - centered(labelHeight, h) - fontDescent; + int textYupperLine = barHeight - centered(labelHeight, barHeight) - fontDescent; + int textYlowerLine = h - centered(labelHeight, barHeight) - fontDescent; + + // Center of coordinate system -> upper left of bar + glPushMatrix(); + glTranslatef(float(barX), float(y), 0.0f); + + // Render captions + setColorRGBA(COLOR_TEXT); + _textRenderer.draw(barWidth + SPACING_LEFT_CAPTION_UNIT, textYcenteredLine, CAPTION_UNIT); + _textRenderer.draw(-labelWidthIn - SPACING_RIGHT_CAPTION_IN_OUT, textYupperLine, CAPTION_IN); + _textRenderer.draw(-labelWidthOut - SPACING_RIGHT_CAPTION_IN_OUT, textYlowerLine, CAPTION_OUT); + + // Render vertical lines for the frame + setColorRGBA(COLOR_FRAME); + renderVerticalLine(0, 0, h); + renderVerticalLine(barWidth, 0, h); + + // Adjust scale + int steps; + double step, scaleMax; + bool commit = false; + do { + steps = (_scaleMaxIndex % 9) + 2; + step = pow(10.0, (_scaleMaxIndex / 9) - 10); + scaleMax = step * steps; + if (commit) { +// printLog("Bandwidth meter scale: %d\n", _scaleMaxIndex); + break; + } + if (totalMax < scaleMax * 0.5) { + _scaleMaxIndex = glm::max(0, _scaleMaxIndex-1); + commit = true; + } else if (totalMax > scaleMax) { + _scaleMaxIndex += 1; + commit = true; + } + } while (commit); + + // Render scale indicators + setColorRGBA(COLOR_INDICATOR); + for (int j = int((scaleMax + step - 0.000001) / step); --j > 0;) { + renderVerticalLine(int(barWidth * j * step / scaleMax), 0, h); + } + + // Render bars + int xIn = 0, xOut = 0; + for (int i = 0; i < N_CHANNELS; ++i) { + + ChannelIndex chIdx = ChannelIndex(i); + int wIn = int(barWidth * inputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); + int wOut = int(barWidth * outputStream(chIdx).getValue() * UNIT_SCALE / scaleMax); + + setColorRGBA(channelInfo(chIdx).colorRGBA); + + if (wIn > 0) { + renderBox(xIn, 0, wIn, barHeight); + } + xIn += wIn; + + if (wOut > 0) { + renderBox(xOut, h - barHeight, wOut, barHeight); + } + xOut += wOut; + } + + // Render numbers + char fmtBuf[8]; + setColorRGBA(COLOR_TEXT); + sprintf(fmtBuf, "%0.2f", totalIn); + _textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE, + PADDING_HORIZ_VALUE), + textYupperLine, fmtBuf); + sprintf(fmtBuf, "%0.2f", totalOut); + _textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE, + PADDING_HORIZ_VALUE), + textYlowerLine, fmtBuf); + + glPopMatrix(); +} + + diff --git a/interface/src/BandwidthMeter.h b/interface/src/BandwidthMeter.h new file mode 100644 index 0000000000..0221306ed7 --- /dev/null +++ b/interface/src/BandwidthMeter.h @@ -0,0 +1,83 @@ +// +// BandwidthMeter.h +// interface +// +// Created by Tobias Schwinger on 6/20/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__BandwidthMeter__ +#define __interface__BandwidthMeter__ + +#include + +#include "ui/TextRenderer.h" + + +class BandwidthMeter { + +public: + + BandwidthMeter(); + ~BandwidthMeter(); + + void render(int screenWidth, int screenHeight); + bool isWithinArea(int x, int y, int screenWidth, int screenHeight); + + // Number of channels / streams. + static size_t const N_CHANNELS = 3; + static size_t const N_STREAMS = N_CHANNELS * 2; + + // Channel usage. + enum ChannelIndex { AUDIO, AVATARS, VOXELS }; + + // Meta information held for a communication channel (bidirectional). + struct ChannelInfo { + + char const* const caption; + char const* unitCaption; + double unitScale; + unsigned colorRGBA; + }; + + // Representation of a data stream (unidirectional; input or output). + class Stream { + + public: + + Stream(float msToAverage = 3000.0f); + void updateValue(double amount); + double getValue() const { return _value; } + + private: + double _value; // Current value. + double _msToAverage; // Milliseconds to average. + timeval _prevTime; // Time of last feed. + }; + + // Data model accessors + Stream& inputStream(ChannelIndex i) { return _streams[i * 2]; } + Stream const& inputStream(ChannelIndex i) const { return _streams[i * 2]; } + Stream& outputStream(ChannelIndex i) { return _streams[i * 2 + 1]; } + Stream const& outputStream(ChannelIndex i) const { return _streams[i * 2 + 1]; } + ChannelInfo& channelInfo(ChannelIndex i) { return _channels[i]; } + ChannelInfo const& channelInfo(ChannelIndex i) const { return _channels[i]; } + +private: + static void setColorRGBA(unsigned c); + static void renderBox(int x, int y, int w, int h); + static void renderVerticalLine(int x, int y, int h); + + static inline int centered(int subject, int object); + + + static ChannelInfo _CHANNELS[]; + + TextRenderer _textRenderer; + ChannelInfo* _channels; + Stream _streams[N_STREAMS]; + int _scaleMaxIndex; +}; + +#endif /* defined(__interface__BandwidthMeter__) */ + diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e2705ab02a..868de81bfe 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -25,6 +25,7 @@ const float CAMERA_MIRROR_MODE_UP_SHIFT = 0.0f; const float CAMERA_MIRROR_MODE_DISTANCE = 0.3f; const float CAMERA_MIRROR_MODE_TIGHTNESS = 100.0f; + Camera::Camera() { _needsToInitialize = true; @@ -35,7 +36,7 @@ Camera::Camera() { _linearModeShift = 0.0f; _mode = CAMERA_MODE_THIRD_PERSON; _tightness = 10.0f; // default - _fieldOfView = 60.0f; // default + _fieldOfView = HORIZONTAL_FIELD_OF_VIEW_DEGREES; _nearClip = 0.08f; // default _farClip = 50.0f * TREE_SCALE; // default _upShift = 0.0f; diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 1f36d33f58..5e0dfaff95 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -11,6 +11,8 @@ #include #include +const float HORIZONTAL_FIELD_OF_VIEW_DEGREES = 90.0f; + enum CameraMode { CAMERA_MODE_NULL = -1, diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 69edf376ee..55d7e76d62 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -160,7 +160,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { setupNewVoxelsForDrawing(); pthread_mutex_unlock(&_treeLock); - + + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes); + return numBytes; } diff --git a/interface/src/ui/BandwidthDialog.cpp b/interface/src/ui/BandwidthDialog.cpp new file mode 100644 index 0000000000..1e0e2e616e --- /dev/null +++ b/interface/src/ui/BandwidthDialog.cpp @@ -0,0 +1,72 @@ + +#include "ui/BandwidthDialog.h" + +#include +#include + +#include +#include + +#include "Log.h" + +BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthMeter* model) : + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint), + _model(model) { + + char strBuf[64]; + + this->setWindowTitle("Bandwidth Details"); + + // Create layouter + QFormLayout* form = new QFormLayout(); + this->QDialog::setLayout(form); + + // Setup labels + for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) { + bool input = i % 2 == 0; + BandwidthMeter::ChannelInfo& ch = _model->channelInfo(BandwidthMeter::ChannelIndex(i / 2)); + QLabel* label = _labels[i] = new QLabel(); + label->setAlignment(Qt::AlignRight); + + // Set foreground color to 62.5% brightness of the meter (otherwise will be hard to read on the bright background) + QPalette palette = label->palette(); + unsigned rgb = ch.colorRGBA >> 8; + rgb = ((rgb & 0xfefefeu) >> 1) + ((rgb & 0xf8f8f8) >> 3); + palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb)); + label->setPalette(palette); + + snprintf(strBuf, sizeof(strBuf), " %s %s Bandwidth:", input ? "Input" : "Output", ch.caption); + form->addRow(strBuf, label); + } +} + +void BandwidthDialog::paintEvent(QPaintEvent* event) { + + // Update labels + char strBuf[64]; + for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) { + BandwidthMeter::ChannelIndex chIdx = BandwidthMeter::ChannelIndex(i / 2); + bool input = i % 2 == 0; + BandwidthMeter::ChannelInfo& ch = _model->channelInfo(chIdx); + BandwidthMeter::Stream& s = input ? _model->inputStream(chIdx) : _model->outputStream(chIdx); + QLabel* label = _labels[i]; + snprintf(strBuf, sizeof(strBuf), "%0.2f %s", s.getValue() * ch.unitScale, ch.unitCaption); + label->setText(strBuf); + } + + this->QDialog::paintEvent(event); + this->setFixedSize(this->width(), this->height()); +} + +void BandwidthDialog::reject() { + + // Just regularly close upon ESC + this->QDialog::close(); +} + +void BandwidthDialog::closeEvent(QCloseEvent* event) { + + this->QDialog::closeEvent(event); + emit closed(); +} + diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h new file mode 100644 index 0000000000..636e91dce9 --- /dev/null +++ b/interface/src/ui/BandwidthDialog.h @@ -0,0 +1,47 @@ +// +// BandwidthDialog.h +// interface +// +// Created by Tobias Schwinger on 6/21/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__BandwidthDialog__ +#define __hifi__BandwidthDialog__ + +#include +#include + +#include "BandwidthMeter.h" + + +class BandwidthDialog : public QDialog { + Q_OBJECT +public: + + // Sets up the UI based on the configuration of the BandwidthMeter + BandwidthDialog(QWidget* parent, BandwidthMeter* model); + +signals: + + void closed(); + +public slots: + + void reject(); + +protected: + + // State <- data model held by BandwidthMeter + void paintEvent(QPaintEvent*); + + // Emits a 'closed' signal when this dialog is closed. + void closeEvent(QCloseEvent*); + +private: + BandwidthMeter* _model; + QLabel* _labels[BandwidthMeter::N_STREAMS]; +}; + +#endif /* defined(__interface__BandwidthDialog__) */ + diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index efe52bdf57..29ff920317 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -11,6 +11,7 @@ #include #include +#include #include diff --git a/libraries/shared/src/AgentList.cpp b/libraries/shared/src/AgentList.cpp index 237b367252..3e5f7c331c 100644 --- a/libraries/shared/src/AgentList.cpp +++ b/libraries/shared/src/AgentList.cpp @@ -191,6 +191,18 @@ Agent* AgentList::agentWithID(uint16_t agentID) { return NULL; } +int AgentList::getNumAliveAgents() const { + int numAliveAgents = 0; + + for (AgentList::iterator agent = begin(); agent != end(); agent++) { + if (agent->isAlive()) { + ++numAliveAgents; + } + } + + return numAliveAgents; +} + void AgentList::setAgentTypesOfInterest(const char* agentTypesOfInterest, int numAgentTypesOfInterest) { delete _agentTypesOfInterest; @@ -350,14 +362,17 @@ void AgentList::addAgentToList(Agent* newAgent) { Agent::printLog(*newAgent); } -void AgentList::broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes) { +unsigned AgentList::broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes) { + unsigned n = 0; for(AgentList::iterator agent = begin(); agent != end(); agent++) { // only send to the AgentTypes we are asked to send to. if (agent->getActiveSocket() != NULL && memchr(agentTypes, agent->getType(), numAgentTypes)) { // we know which socket is good for this agent, send there _agentSocket.send(agent->getActiveSocket(), broadcastData, dataBytes); + ++n; } } + return n; } void AgentList::handlePingReply(sockaddr *agentAddress) { diff --git a/libraries/shared/src/AgentList.h b/libraries/shared/src/AgentList.h index 8a283009fe..71e1969c90 100644 --- a/libraries/shared/src/AgentList.h +++ b/libraries/shared/src/AgentList.h @@ -63,6 +63,7 @@ public: void(*linkedDataCreateCallback)(Agent *); int size() { return _numAgents; } + int getNumAliveAgents() const; void lock() { pthread_mutex_lock(&mutex); } void unlock() { pthread_mutex_unlock(&mutex); } @@ -82,7 +83,7 @@ public: int updateAgentWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes); int updateAgentWithData(Agent *agent, unsigned char *packetData, int dataBytes); - void broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes); + unsigned broadcastToAgents(unsigned char *broadcastData, size_t dataBytes, const char* agentTypes, int numAgentTypes); Agent* soloAgentOfType(char agentType); diff --git a/libraries/shared/src/Logstash.cpp b/libraries/shared/src/Logstash.cpp index 94279fdd4a..fd7fe125f2 100644 --- a/libraries/shared/src/Logstash.cpp +++ b/libraries/shared/src/Logstash.cpp @@ -45,12 +45,12 @@ bool Logstash::shouldSendStats() { return shouldSendStats; } -void Logstash::stashValue(const char* key, float value) { +void Logstash::stashValue(char statType, const char* key, float value) { static char logstashPacket[MAX_PACKET_SIZE]; // load up the logstash packet with the key and the passed float value // send it to 4 decimal places - int numPacketBytes = sprintf(logstashPacket, "%s %.4f", key, value); + int numPacketBytes = sprintf(logstashPacket, "%c %s %.4f", statType, key, value); AgentList *agentList = AgentList::getInstance(); diff --git a/libraries/shared/src/Logstash.h b/libraries/shared/src/Logstash.h index 5f2d247218..7c805ddb23 100644 --- a/libraries/shared/src/Logstash.h +++ b/libraries/shared/src/Logstash.h @@ -14,11 +14,15 @@ const int LOGSTASH_UDP_PORT = 9500; const char LOGSTASH_HOSTNAME[] = "graphite.highfidelity.io"; +const char STAT_TYPE_TIMER = 't'; +const char STAT_TYPE_COUNTER = 'c'; +const char STAT_TYPE_GAUGE = 'g'; + class Logstash { public: static sockaddr* socket(); static bool shouldSendStats(); - static void stashValue(const char* key, float value); + static void stashValue(char statType, const char* key, float value); private: static sockaddr_in logstashSocket; }; diff --git a/libraries/voxels/src/SquarePixelMap.cpp b/libraries/voxels/src/SquarePixelMap.cpp new file mode 100644 index 0000000000..bdc97624a7 --- /dev/null +++ b/libraries/voxels/src/SquarePixelMap.cpp @@ -0,0 +1,239 @@ +// +// SquarePixelMap.cpp +// hifi +// +// Created by Tomáš Horáček on 6/25/13. +// +// + +#include "SquarePixelMap.h" +#include +#include + +#define CHILD_COORD_X_IS_1 0x1 +#define CHILD_COORD_Y_IS_1 0x2 +#define ALPHA_CHANNEL_RANGE_FLOAT 256.f +#define ALPHA_CHANNEL_BIT_OFFSET 24 +#define RED_CHANNEL_BIT_OFFSET 16 +#define GREEN_CHANNEL_BIT_OFFSET 8 + +unsigned int numberOfBitsForSize(unsigned int size) { + if (size == 0) { + return 0; + } + + size--; + + unsigned int ans = 1; + while (size >>= 1) { + ans++; + } + return ans; +} + +struct PixelQuadTreeCoordinates { + unsigned int x; + unsigned int y; + unsigned int size; +}; + +class PixelQuadTreeNode { +public: + PixelQuadTreeCoordinates _coord; + uint32_t _color; // undefined value for _allChildrenHasSameColor = false + bool _allChildrenHasSameColor; + uint8_t _minimumNeighbourhoodAplha; + + // 0 x -> 1 + // +---+---+ + // y | 0 | 1 | <- child index + // | +---+---+ + // v | 2 | 3 | + // +---+---+ + // 1 + PixelQuadTreeNode* _children[4]; + + PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap); + ~PixelQuadTreeNode() { + for (int i = 0; i < 4; i++) { + delete _children[i]; + } + } + +private: + void updateChildCoordinates(int i, PixelQuadTreeCoordinates& childCoord) { + childCoord.x = _coord.x; + childCoord.y = _coord.y; + + if (i & CHILD_COORD_X_IS_1) { + childCoord.x += childCoord.size; + } + if (i & CHILD_COORD_Y_IS_1) { + childCoord.y += childCoord.size; + } + } + + bool hasAllChildrenSameColor() { + return false; //turn off import voxel grouping + + for (int i = 1; i < 4; i++) { + if (!_children[i]->_allChildrenHasSameColor) { + return false; + } + } + + uint32_t firstColor = _children[0]->_color; + + for (int i = 1; i < 4; i++) { + if (firstColor != _children[i]->_color) { + return false; + } + } + return true; + } +}; + +PixelQuadTreeNode::PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap) : _coord(coord), _minimumNeighbourhoodAplha(-1) { + for (int i = 0; i < 4; i++) { + _children[i] = NULL; + } + + if (_coord.size == 1) { + _color = pixelMap->getPixelAt(_coord.x, _coord.y); + + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x + 1, _coord.y), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x - 1, _coord.y), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x, _coord.y + 1), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x, _coord.y - 1), _minimumNeighbourhoodAplha); + + _allChildrenHasSameColor = true; + } else { + PixelQuadTreeCoordinates childCoord = PixelQuadTreeCoordinates(); + childCoord.size = _coord.size / 2; + + for (int i = 0; i < 4; i++) { + this->updateChildCoordinates(i, childCoord); + + + if (childCoord.x < pixelMap->dimension() && + childCoord.y < pixelMap->dimension()) { + + _children[i] = new PixelQuadTreeNode(childCoord, pixelMap); + } + } + + if (this->hasAllChildrenSameColor()) { + _allChildrenHasSameColor = true; + _color = _children[0]->_color; + + _minimumNeighbourhoodAplha = _children[0]->_minimumNeighbourhoodAplha; + + for (int i = 0; i < 4; i++) { + _minimumNeighbourhoodAplha = std::min(_children[i]->_minimumNeighbourhoodAplha, _minimumNeighbourhoodAplha); + delete _children[i]; + _children[i] = NULL; + } + } else { + _allChildrenHasSameColor = false; + } + } +} + +SquarePixelMap::SquarePixelMap(const uint32_t* pixels, int dimension) : _rootPixelQuadTreeNode(NULL) { + _data = new SquarePixelMapData(); + _data->dimension = dimension; + _data->reference_counter = 1; + + size_t pixels_size = dimension * dimension; + _data->pixels = new uint32_t[pixels_size]; + memcpy((void*)_data->pixels, (void*)pixels, sizeof(uint32_t) * pixels_size); +} + +SquarePixelMap::SquarePixelMap(const SquarePixelMap& other) { + this->_data = other._data; + this->_data->reference_counter++; +} + +SquarePixelMap::~SquarePixelMap() { + delete _rootPixelQuadTreeNode; + + if (--_data->reference_counter == 0) { + delete _data->pixels; + delete _data; + } +} + +void SquarePixelMap::addVoxelsToVoxelTree(VoxelTree* voxelTree) { + this->generateRootPixelQuadTreeNode(); + this->createVoxelsFromPixelQuadTreeToVoxelTree(_rootPixelQuadTreeNode, voxelTree); +} + +int SquarePixelMap::dimension() { + return _data->dimension; +} + +uint32_t SquarePixelMap::getPixelAt(unsigned int x, unsigned int y) { + return _data->pixels[x + y * _data->dimension]; +} + +uint8_t SquarePixelMap::getAlphaAt(int x, int y) { + int max_coord = this->dimension() - 1; + + if (x < 0 || y < 0 || x > max_coord || y > max_coord) { + return -1; + } + + return this->getPixelAt(x, y) >> ALPHA_CHANNEL_BIT_OFFSET; +} + +void SquarePixelMap::generateRootPixelQuadTreeNode() { + delete _rootPixelQuadTreeNode; + + PixelQuadTreeCoordinates rootNodeCoord = PixelQuadTreeCoordinates(); + rootNodeCoord.size = 1 << numberOfBitsForSize(_data->dimension); + rootNodeCoord.x = rootNodeCoord.y = 0; + + _rootPixelQuadTreeNode = new PixelQuadTreeNode(rootNodeCoord, this); +} + +void SquarePixelMap::createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree) { + if (pixelQuadTreeNode->_allChildrenHasSameColor) { + VoxelDetail voxel = this->getVoxelDetail(pixelQuadTreeNode); + + unsigned char minimumNeighbourhoodAplha = std::max(0, pixelQuadTreeNode->_minimumNeighbourhoodAplha - 1); + + float minimumNeighbourhoodY = voxel.s * (floor(minimumNeighbourhoodAplha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5); + + do { + voxelTree->createVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.red, voxel.green, voxel.blue, true); + } while ((voxel.y -= voxel.s) > minimumNeighbourhoodY); + } else { + for (int i = 0; i < 4; i++) { + PixelQuadTreeNode* child = pixelQuadTreeNode->_children[i]; + if (child) { + this->createVoxelsFromPixelQuadTreeToVoxelTree(child, voxelTree); + } + } + } +} + +VoxelDetail SquarePixelMap::getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode) { + VoxelDetail voxel = VoxelDetail(); + + uint32_t color = pixelQuadTreeNode->_color; + unsigned char alpha = std::max(0, (color >> ALPHA_CHANNEL_BIT_OFFSET) - 1); + + voxel.red = color >> RED_CHANNEL_BIT_OFFSET; + voxel.green = color >> GREEN_CHANNEL_BIT_OFFSET; + voxel.blue = color; + + + float rootSize = _rootPixelQuadTreeNode->_coord.size; + + voxel.s = pixelQuadTreeNode->_coord.size / rootSize; + voxel.y = voxel.s * (floor(alpha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5); + voxel.x = pixelQuadTreeNode->_coord.x / rootSize + voxel.s / 2; + voxel.z = pixelQuadTreeNode->_coord.y / rootSize + voxel.s / 2; + + return voxel; +} diff --git a/libraries/voxels/src/SquarePixelMap.h b/libraries/voxels/src/SquarePixelMap.h new file mode 100644 index 0000000000..0ef6dc55ee --- /dev/null +++ b/libraries/voxels/src/SquarePixelMap.h @@ -0,0 +1,44 @@ +// +// SquarePixelMap.h +// hifi +// +// Created by Tomáš Horáček on 6/25/13. +// +// + +#ifndef __hifi__SquarePixelMap__ +#define __hifi__SquarePixelMap__ + +#include +#include "VoxelTree.h" +#include "SharedUtil.h" + +class PixelQuadTreeNode; + +struct SquarePixelMapData { + const uint32_t* pixels; + int dimension; + int reference_counter; +}; + +class SquarePixelMap { +public: + SquarePixelMap(const uint32_t* pixels, int dimension); + SquarePixelMap(const SquarePixelMap& other); + ~SquarePixelMap(); + + void addVoxelsToVoxelTree(VoxelTree* voxelTree); + + int dimension(); + uint32_t getPixelAt(unsigned int x, unsigned int y); + uint8_t getAlphaAt(int x, int y); +private: + SquarePixelMapData* _data; + PixelQuadTreeNode* _rootPixelQuadTreeNode; + + void generateRootPixelQuadTreeNode(); + void createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree); + VoxelDetail getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode); +}; + +#endif /* defined(__hifi__SquarePixelMap__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index e2f66fa52b..b16425370e 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -23,6 +23,8 @@ #include // to load voxels from file #include "VoxelConstants.h" #include "CoverageMap.h" +#include "SquarePixelMap.h" + #include @@ -1447,6 +1449,12 @@ bool VoxelTree::readFromSVOFile(const char* fileName) { return false; } +bool VoxelTree::readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension) { + SquarePixelMap pixelMap = SquarePixelMap(pixels, dimension); + pixelMap.addVoxelsToVoxelTree(this); + return true; +} + void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const { std::ofstream file(fileName, std::ios::out|std::ios::binary); diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index df721d0ba5..72ea9e2326 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -133,6 +133,8 @@ public: // these will read/write files that match the wireformat, excluding the 'V' leading void writeToSVOFile(const char* filename, VoxelNode* node = NULL) const; bool readFromSVOFile(const char* filename); + // reads voxels from square image with alpha as a Y-axis + bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); unsigned long getVoxelCount();