diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index f684a6e672..ba07fac21a 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -807,7 +807,7 @@ AnimationServer::AnimationServer(int &argc, char **argv) : pthread_create(&::animateVoxelThread, NULL, animateVoxels, NULL); - NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_VOXEL_SERVER); QTimer* domainServerTimer = new QTimer(this); connect(domainServerTimer, SIGNAL(timeout()), nodeList, SLOT(sendDomainServerCheckIn())); diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f4f9d6fb69..4f81c42046 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,13 +52,7 @@ void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); - // XXXBHG - this seems less than ideal. There might be classes (like jurisdiction listeners, that need access to - // other node types, but for them to get access to those node types, we have to add them here. It seems like - // NodeList should support adding types of interest - const NODE_TYPE AGENT_NODE_TYPES_OF_INTEREST[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER }; - - nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index aa54c874ef..705a877a00 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -241,8 +241,7 @@ void AudioMixer::run() { NodeList* nodeList = NodeList::getInstance(); - const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR }; - nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST)); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); nodeList->linkedDataCreateCallback = attachNewBufferToNode; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 56464d2415..28b8358742 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -132,9 +132,9 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc // parse positional data from an node nodeList->updateNodeWithData(avatarNode.data(), senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); - } else { - break; + } + break; } case PACKET_TYPE_KILL_NODE: default: @@ -149,7 +149,7 @@ void AvatarMixer::run() { commonInit(AVATAR_MIXER_LOGGING_NAME, NODE_TYPE_AVATAR_MIXER); NodeList* nodeList = NodeList::getInstance(); - nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp index 2916403c97..4362c8ace0 100644 --- a/data-server/src/DataServer.cpp +++ b/data-server/src/DataServer.cpp @@ -137,10 +137,10 @@ void DataServer::readPendingDatagrams() { packetData[0] = PACKET_TYPE_DATA_SERVER_SEND; - const char MULTI_KEY_VALUE_SEPARATOR = '|'; - if (strcmp((char*) packetData + readBytes, "uuid") != 0) { + const char MULTI_KEY_VALUE_SEPARATOR = '|'; + // the user has sent one or more keys - make the associated requests for (int j = 0; j < numKeys; j++) { @@ -184,7 +184,7 @@ void DataServer::readPendingDatagrams() { } else { // user is asking for a UUID matching username, copy the UUID we found QString uuidString = uuidStringWithoutCurlyBraces(parsedUUID); - memcpy(packetData + numSendPacketBytes, uuidString.constData(), uuidString.size() + sizeof('\0')); + memcpy(packetData + numSendPacketBytes, qPrintable(uuidString), uuidString.size() + sizeof('\0')); numSendPacketBytes += uuidString.size() + sizeof('\0'); } diff --git a/examples/drumStick.js b/examples/drumStick.js index 5224900414..955fddbdee 100644 --- a/examples/drumStick.js +++ b/examples/drumStick.js @@ -18,8 +18,8 @@ function vMinus(a, b) { // First, load two percussion sounds to be used on the sticks -var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw"); -var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/MusicalInstruments/drums/snare.raw"); +var drum1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomHi.raw"); +var drum2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Drums/RackTomLo.raw"); // State Machine: // 0 = not triggered diff --git a/examples/fountain.js b/examples/fountain.js index 86c125a834..a095f91ed3 100644 --- a/examples/fountain.js +++ b/examples/fountain.js @@ -42,7 +42,7 @@ Voxels.setVoxel(position.x, 0, position.z, 0.5, 0, 0, 255); var totalParticles = 0; function makeFountain() { - if (Math.random() < 0.06) { + if (Math.random() < 0.10) { //print("Made particle!\n"); var properties = { position: position, @@ -51,9 +51,9 @@ function makeFountain() { velocity: { x: (Math.random() * 1.0 - 0.5), y: (1.0 + (Math.random() * 2.0)), z: (Math.random() * 1.0 - 0.5) }, - gravity: { x: 0, y: -0.5, z: 0 }, + gravity: { x: 0, y: -0.1, z: 0 }, damping: 0.25, - lifetime: 2 + lifetime: 1 } Particles.addParticle(properties); diff --git a/examples/toyball.js b/examples/toyball.js index 1682cbe3d4..6c40fc2932 100644 --- a/examples/toyball.js +++ b/examples/toyball.js @@ -134,8 +134,10 @@ function checkControllerSide(whichSide) { gravity: { x: 0, y: 0, z: 0}, inHand: true, radius: 0.05, + damping: 0.999, color: { red: 255, green: 0, blue: 0 }, - lifetime: 10 // 10 seconds + + lifetime: 10 // 10 seconds - same as default, not needed but here as an example }; newParticle = Particles.addParticle(properties); diff --git a/interface/resources/scripts/sphere.js b/interface/resources/scripts/sphere.js index 403374e812..b696021fe8 100644 --- a/interface/resources/scripts/sphere.js +++ b/interface/resources/scripts/sphere.js @@ -35,11 +35,11 @@ function setNormal(vector) { if (normalIndex != -1) { var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2])); if (length == 0.0) { - info.attributeValues[normalIndex] = 0x007F00; + info.inputValues[normalIndex] = 0x007F00; } else { var scale = 127.0 / length; - info.attributeValues[normalIndex] = + info.inputValues[normalIndex] = (Math.floor(vector[0] * scale) & 0xFF) << 16 | (Math.floor(vector[1] * scale) & 0xFF) << 8 | Math.floor(vector[2] * scale) & 0xFF; @@ -61,7 +61,7 @@ function guide(minimum, size, depth) { maximum[2] <= sphereCenter[2] - sphereRadius) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } visitor.visit(info); return; @@ -110,7 +110,7 @@ function guide(minimum, size, depth) { if (inside == 8) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); visitor.visit(info); @@ -122,13 +122,13 @@ function guide(minimum, size, depth) { info.isLeaf = true; if (inside >= 3) { if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); } else { if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } } visitor.visit(info); @@ -152,11 +152,11 @@ function guide(minimum, size, depth) { } (function(visitation) { - var attributes = visitation.visitor.getAttributes(); - colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute); - normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute); + var inputs = visitation.visitor.getInputs(); + colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute); + normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute); visitor = visitation.visitor; - info = { attributeValues: new Array(attributes.length) }; + info = { inputValues: new Array(inputs.length) }; // have the sphere orbit the center and pulse in size var time = new Date().getTime(); diff --git a/interface/resources/shaders/grid.frag b/interface/resources/shaders/grid.frag new file mode 100644 index 0000000000..b9e3baccd4 --- /dev/null +++ b/interface/resources/shaders/grid.frag @@ -0,0 +1,15 @@ +#version 120 + +// +// grid.frag +// fragment shader +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + // use the standard exponential fog calculation + const float FOG_DENSITY = 0.5; + gl_FragColor = vec4(gl_Color.rgb, exp(-FOG_DENSITY / gl_FragCoord.w)); +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ba0dcd3903..84444a65b4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -108,6 +108,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), + _nodeThread(new QThread(this)), + _datagramProcessor(), _frameCount(0), _fps(120.0f), _justStarted(true), @@ -143,12 +145,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _enableProcessVoxelsThread(true), _voxelProcessor(), _voxelHideShowThread(&_voxels), - _voxelEditSender(this), - _particleEditSender(this), - _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), - _bytesCount(0), _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), _swatch(NULL), @@ -173,11 +171,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : if (portStr) { listenPort = atoi(portStr); } - + + // start the nodeThread so its event loop is running + _nodeThread->start(); + + // make sure the node thread is given highest priority + _nodeThread->setPriority(QThread::TimeCriticalPriority); + + // put the NodeList and datagram processing on the node thread NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); - - // connect our processDatagrams slot to the QUDPSocket readyRead() signal - connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(processDatagrams())); + + nodeList->moveToThread(_nodeThread); + _datagramProcessor.moveToThread(_nodeThread); + + // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); @@ -192,7 +200,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - + // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); @@ -221,12 +229,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : #endif // tell the NodeList instance who to tell the domain server we care about - const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, - NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_METAVOXEL_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + nodeList->addSetOfNodeTypesToNodeInterestSet(QSet() << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER + << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_METAVOXEL_SERVER); + + // connect to the packet sent signal of the _voxelEditSender and the _particleEditSender + connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); + connect(&_particleEditSender, &ParticleEditPacketSender::packetSent, this, &Application::packetSent); - QTimer* silentNodeTimer = new QTimer(this); + // move the silentNodeTimer to the _nodeThread + QTimer* silentNodeTimer = new QTimer(); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); + silentNodeTimer->moveToThread(_nodeThread); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); @@ -270,13 +284,35 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : Application::~Application() { qInstallMessageHandler(NULL); - + // make sure we don't call the idle timer any more delete idleTimer; - + + Menu::getInstance()->saveSettings(); + + _rearMirrorTools->saveSettings(_settings); + _settings->sync(); + + // let the avatar mixer know we're out + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); + + // ask the datagram processing thread to quit and wait until it is done + _nodeThread->quit(); + _nodeThread->wait(); + // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); + + _voxelProcessor.terminate(); + _voxelHideShowThread.terminate(); + _voxelEditSender.terminate(); + _particleEditSender.terminate(); + if (_persistThread) { + _persistThread->terminate(); + _persistThread->deleteLater(); + _persistThread = NULL; + } storeSizeAndPosition(); saveScripts(); @@ -361,9 +397,6 @@ void Application::initializeGL() { qDebug("Voxel parsing thread created."); } - // call terminate before exiting - connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); - // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); @@ -612,21 +645,20 @@ void Application::resetProfile(const QString& username) { } void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const char* nodeTypes, int numNodeTypes) { - Application* self = getInstance(); - for (int i = 0; i < numNodeTypes; ++i) { - + const QSet& destinationNodeTypes) { + foreach(NODE_TYPE type, destinationNodeTypes) { // Intercept data to voxel server when voxels are disabled - if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { + if (type == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } - + // Perform the broadcast for one type - int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); - + int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, + QSet() << type); + // Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise) BandwidthMeter::ChannelIndex channel; - switch (nodeTypes[i]) { + switch (type) { case NODE_TYPE_AGENT: case NODE_TYPE_AVATAR_MIXER: channel = BandwidthMeter::AVATARS; @@ -637,7 +669,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ default: continue; } - self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + _bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } } @@ -1120,13 +1152,12 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _seenMouseMove = true; } + int deltaX = event->x() - _mouseX; + int deltaY = event->y() - _mouseY; + _mouseX = event->x(); + _mouseY = event->y(); + if (activeWindow() == _window) { - int deltaX = event->x() - _mouseX; - int deltaY = event->y() - _mouseY; - - _mouseX = event->x(); - _mouseY = event->y(); - // orbit behavior if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { if (_lookatTargetAvatar) { @@ -1292,15 +1323,13 @@ void Application::wheelEvent(QWheelEvent* event) { } void Application::sendPingPackets() { - - const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_METAVOXEL_SERVER}; - unsigned char pingPacket[MAX_PACKET_SIZE]; int length = NodeList::getInstance()->fillPingPacket(pingPacket); - getInstance()->controlledBroadcastToNodes(pingPacket, length, - nodesToPing, sizeof(nodesToPing)); + getInstance()->controlledBroadcastToNodes(pingPacket, length, QSet() + << NODE_TYPE_VOXEL_SERVER << NODE_TYPE_PARTICLE_SERVER + << NODE_TYPE_AUDIO_MIXER << NODE_TYPE_AVATAR_MIXER + << NODE_TYPE_METAVOXEL_SERVER); } // Every second, check the frame rates and other stuff @@ -1312,11 +1341,12 @@ void Application::timer() { } _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); - _bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + + _packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); + _bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _frameCount = 0; - _packetCount = 0; - _bytesCount = 0; + + _datagramProcessor.resetCounters(); gettimeofday(&_timerStart, NULL); @@ -1395,28 +1425,6 @@ void Application::idle() { } } -void Application::terminate() { - // Close serial port - // close(serial_fd); - - Menu::getInstance()->saveSettings(); - _rearMirrorTools->saveSettings(_settings); - _settings->sync(); - - // let the avatar mixer know we're out - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); - - _voxelProcessor.terminate(); - _voxelHideShowThread.terminate(); - _voxelEditSender.terminate(); - _particleEditSender.terminate(); - if (_persistThread) { - _persistThread->terminate(); - _persistThread->deleteLater(); - _persistThread = NULL; - } -} - void Application::checkBandwidthMeterClick() { // ... to be called upon button release @@ -1840,25 +1848,23 @@ const float HEAD_SPHERE_RADIUS = 0.07f; static QUuid DEFAULT_NODE_ID_REF; -void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition) { +void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); if (!_mousePressed) { - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + _lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF); } } -Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { +Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = (Avatar*)node->getLinkedData(); float distance; - if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) { + if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) { // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); @@ -1909,7 +1915,7 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) { glPopMatrix(); } -void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { +void Application::updateAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); @@ -1921,7 +1927,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: avatar->init(); } avatar->simulate(deltaTime, NULL); - avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); + avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection); } } @@ -1944,28 +1950,28 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: } } -void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection) { +void Application::updateMouseRay() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), - mouseRayOrigin, mouseRayDirection); + _mouseRayOrigin, _mouseRayDirection); // adjust for mirroring if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - glm::vec3 mouseRayOffset = mouseRayOrigin - _viewFrustum.getPosition(); - mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + + glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); + _mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset)); - mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayDirection) + - _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayDirection)); + _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection)); } // tell my avatar if the mouse is being pressed... _myAvatar.setMousePressed(_mousePressed); // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position - _myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection); + _myAvatar.setMouseRay(_mouseRayOrigin, _mouseRayDirection); } void Application::updateFaceshift() { @@ -1982,8 +1988,7 @@ void Application::updateFaceshift() { } } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, - glm::vec3& lookAtRayDirection) { +void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); @@ -1998,15 +2003,9 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _viewFrustum.computePickRay(0.5f, 0.5f, rayOrigin, rayDirection); lookAtSpot = rayOrigin + rayDirection * FAR_AWAY_STARE; - } else if (!_lookatTargetAvatar) { - if (_isHoverVoxel) { - // Look at the hovered voxel - lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel); - - } else { - // Just look in direction of the mouse ray - lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE; - } + } else { + // just look in direction of the mouse ray + lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE; } if (_faceshift.isActive()) { // deflect using Faceshift gaze data @@ -2020,8 +2019,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _myAvatar.getHead().setLookAtPosition(lookAtSpot); } -void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()"); @@ -2052,7 +2050,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); - _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); + _isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face); } if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) { @@ -2068,8 +2066,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } } -void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()"); @@ -2081,7 +2078,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { - if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) { + if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { if (distance < MAX_VOXEL_EDIT_DISTANCE) { // set the voxel scale to that of the first moused-over voxel if (!wasInitialized) { @@ -2101,7 +2098,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3 faceVector = getFaceVector(face); if (_mouseVoxelScale < _mouseVoxel.s) { // find the closest contained voxel - glm::vec3 pt = (mouseRayOrigin + mouseRayDirection * distance) / (float)TREE_SCALE - + glm::vec3 pt = (_mouseRayOrigin + _mouseRayDirection * distance) / (float)TREE_SCALE - faceVector * (_mouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale); @@ -2122,7 +2119,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, || Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { // place the voxel a fixed distance away float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE; - glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); + glm::vec3 pt = _mouseRayOrigin + _mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / worldMouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / worldMouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); @@ -2356,29 +2353,28 @@ void Application::update(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::update()"); // check what's under the mouse and update the mouse voxel - glm::vec3 mouseRayOrigin, mouseRayDirection; - updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection); + updateMouseRay(); // Set where I am looking based on my mouse ray (so that other people can see) glm::vec3 lookAtSpot; updateFaceshift(); - updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); - updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); + updateLookatTargetAvatar(lookAtSpot); + updateMyAvatarLookAtPosition(lookAtSpot); // Find the voxel we are hovering over, and respond if clicked float distance; BoxFace face; - updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds - updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels + updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds + updateMouseVoxels(deltaTime, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors updateLeap(deltaTime); // Leap finger-sensing device updateSixense(deltaTime); // Razer Hydra controllers updateSerialDevices(deltaTime); // Read serial port interface devices updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... + updateAvatars(deltaTime); //loop through all the other avatars and simulate them... updateMyAvatarSimulation(deltaTime); // Simulate myself updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels @@ -2449,9 +2445,8 @@ void Application::updateAvatar(float deltaTime) { endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - const char nodeTypesOfInterest[] = { NODE_TYPE_AVATAR_MIXER }; controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, - nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + QSet() << NODE_TYPE_AVATAR_MIXER); // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the @@ -2862,10 +2857,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (!selfAvatarOnly) { // draw a red sphere - float sphereRadius = 0.25f; + float originSphereRadius = 0.05f; glColor3f(1,0,0); glPushMatrix(); - glutSolidSphere(sphereRadius, 15, 15); + glutSolidSphere(originSphereRadius, 15, 15); glPopMatrix(); // disable specular lighting for ground and voxels @@ -3028,6 +3023,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glPopMatrix(); } + + // give external parties a change to hook in + emit renderingInWorldInterface(); } } @@ -4052,94 +4050,7 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen return statsMessageLength; } -// Receive packets from other nodes/servers and decide what to do with them! -void Application::processDatagrams() { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()"); - - HifiSockAddr senderSockAddr; - ssize_t bytesReceived; - - while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && - (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) _incomingPacket, - MAX_PACKET_SIZE, - senderSockAddr.getAddressPointer(), - senderSockAddr.getPortPointer()))) { - - _packetCount++; - _bytesCount += bytesReceived; - - if (packetVersionMatch(_incomingPacket)) { - // only process this packet if we have a match on the packet version - switch (_incomingPacket[0]) { - case PACKET_TYPE_TRANSMITTER_DATA_V2: - // V2 = IOS transmitter app - _myTransmitter.processIncomingData(_incomingPacket, bytesReceived); - - break; - case PACKET_TYPE_MIXED_AUDIO: - QMetaObject::invokeMethod(&_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) _incomingPacket, bytesReceived))); - break; - - case PACKET_TYPE_PARTICLE_ADD_RESPONSE: - // this will keep creatorTokenIDs to IDs mapped correctly - Particle::handleAddParticleResponse(_incomingPacket, bytesReceived); - break; - - case PACKET_TYPE_PARTICLE_DATA: - case PACKET_TYPE_PARTICLE_ERASE: - case PACKET_TYPE_VOXEL_DATA: - case PACKET_TYPE_VOXEL_ERASE: - case PACKET_TYPE_OCTREE_STATS: - case PACKET_TYPE_ENVIRONMENT_DATA: { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); - - bool wantExtraDebugging = getLogger()->extraDebugging(); - if (wantExtraDebugging && _incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { - int numBytesPacketHeader = numBytesForPacketHeader(_incomingPacket); - unsigned char* dataAt = _incomingPacket + numBytesPacketHeader; - dataAt += sizeof(VOXEL_PACKET_FLAGS); - VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SEQUENCE); - VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); - dataAt += sizeof(VOXEL_PACKET_SENT_TIME); - VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); - int flightTime = arrivedAt - sentAt; - - printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); - } - - // add this packet to our list of voxel packets and process them on the voxel processing - _voxelProcessor.queueReceivedPacket(senderSockAddr, _incomingPacket, bytesReceived); - break; - } - case PACKET_TYPE_METAVOXEL_DATA: - _metavoxels.processData(QByteArray((const char*) _incomingPacket, bytesReceived), - senderSockAddr); - break; - case PACKET_TYPE_BULK_AVATAR_DATA: - NodeList::getInstance()->processBulkNodeData(senderSockAddr, - _incomingPacket, - bytesReceived); - getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); - break; - case PACKET_TYPE_DATA_SERVER_GET: - case PACKET_TYPE_DATA_SERVER_PUT: - case PACKET_TYPE_DATA_SERVER_SEND: - case PACKET_TYPE_DATA_SERVER_CONFIRM: - DataServerClient::processMessageFromDataServer(_incomingPacket, bytesReceived); - break; - default: - NodeList::getInstance()->processNodeData(senderSockAddr, _incomingPacket, bytesReceived); - break; - } - } - } -} - -void Application::packetSentNotification(ssize_t length) { +void Application::packetSent(quint64 length) { _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 6be01db39f..203d74fa82 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -33,6 +33,7 @@ #include "BandwidthMeter.h" #include "Camera.h" #include "Cloud.h" +#include "DatagramProcessor.h" #include "Environment.h" #include "GLCanvas.h" #include "MetavoxelSystem.h" @@ -91,11 +92,12 @@ 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 PacketSenderNotify { +class Application : public QApplication { Q_OBJECT friend class VoxelPacketProcessor; friend class VoxelEditPacketSender; + friend class DatagramProcessor; public: static Application* getInstance() { return static_cast(QCoreApplication::instance()); } @@ -145,10 +147,13 @@ public: ViewFrustum* getViewFrustum() { return &_viewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } ParticleTreeRenderer* getParticles() { return &_particles; } + MetavoxelSystem* getMetavoxels() { return &_metavoxels; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } bool isMouseHidden() const { return _mouseHidden; } + const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } + const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } Faceshift* getFaceshift() { return &_faceshift; } SixenseManager* getSixenseManager() { return &_sixenseManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } @@ -169,8 +174,8 @@ public: Profile* getProfile() { return &_profile; } void resetProfile(const QString& username); - static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const char* nodeTypes, int numNodeTypes); + void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const QSet& destinationNodeTypes); void setupWorldLight(); @@ -186,9 +191,6 @@ public: void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal, float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const; - - virtual void packetSentNotification(ssize_t length); - VoxelShader& getVoxelShader() { return _voxelShader; } PointShader& getPointShader() { return _pointShader; } FileLogger* getLogger() { return _logger; } @@ -204,12 +206,16 @@ public: void skipVersion(QString latestVersion); +signals: + + /// Fired when we're rendering in-world interface elements; allows external parties to hook in. + void renderingInWorldInterface(); + public slots: void domainChanged(const QString& domainHostname); void nodeKilled(SharedNodePointer node); - - void processDatagrams(); - + void packetSent(quint64 length); + void exportVoxels(); void importVoxels(); void cutVoxels(); @@ -230,7 +236,6 @@ private slots: void timer(); void idle(); - void terminate(); void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); @@ -269,15 +274,12 @@ private: void update(float deltaTime); // Various helper functions called during update() - void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection); + void updateMouseRay(); void updateFaceshift(); - void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection); - void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition); + void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot); + void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); + void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); + void updateLookatTargetAvatar(glm::vec3& eyePosition); void updateHandAndTouch(float deltaTime); void updateLeap(float deltaTime); void updateSixense(float deltaTime); @@ -292,15 +294,14 @@ private: void updateAudio(float deltaTime); void updateCursor(float deltaTime); - Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid &nodeUUID); + Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest); void renderHighlightVoxel(VoxelDetail voxel); void updateAvatar(float deltaTime); - void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); + void updateAvatars(float deltaTime); void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -331,6 +332,9 @@ private: QGLWidget* _glWidget; BandwidthMeter _bandwidthMeter; + + QThread* _nodeThread; + DatagramProcessor _datagramProcessor; QNetworkAccessManager* _networkAccessManager; QSettings* _settings; @@ -402,6 +406,9 @@ private: bool _mouseHidden; bool _seenMouseMove; + glm::vec3 _mouseRayOrigin; + glm::vec3 _mouseRayDirection; + float _touchAvgX; float _touchAvgY; float _lastTouchAvgX; @@ -461,11 +468,8 @@ private: VoxelEditPacketSender _voxelEditSender; ParticleEditPacketSender _particleEditSender; - unsigned char _incomingPacket[MAX_PACKET_SIZE]; - int _packetCount; int _packetsPerSecond; int _bytesPerSecond; - int _bytesCount; int _recentMaxPackets; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 0709b40624..47ab8b0aba 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -415,7 +415,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { _totalPacketsReceived++; double timeDiff = diffclock(&_lastReceiveTime, ¤tReceiveTime); - + // Discard first few received packets for computing jitter (often they pile up on start) if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) { _stdev.addValue(timeDiff); @@ -443,6 +443,15 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { QByteArray outputBuffer; outputBuffer.resize(numRequiredOutputSamples * sizeof(int16_t)); + + if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) { + // we don't have any audio data left in the output buffer + // we just starved + qDebug() << "Audio output just starved."; + _ringBuffer.setIsStarved(true); + _numFramesDisplayStarve = 10; + } + // if there is anything in the ring buffer, decide what to do if (_ringBuffer.samplesAvailable() > 0) { if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO @@ -515,12 +524,6 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) { } } - } else if (_audioOutput->bytesFree() == _audioOutput->bufferSize()) { - // we don't have any audio data left in the output buffer, and the ring buffer from - // the network has nothing in it either - we just starved - qDebug() << "Audio output just starved."; - _ringBuffer.setIsStarved(true); - _numFramesDisplayStarve = 10; } Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size()); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp new file mode 100644 index 0000000000..9a6c1bea05 --- /dev/null +++ b/interface/src/DatagramProcessor.cpp @@ -0,0 +1,110 @@ +// +// DatagramProcessor.cpp +// hifi +// +// Created by Stephen Birarda on 1/23/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#include "Application.h" +#include "Menu.h" + +#include "DatagramProcessor.h" + +DatagramProcessor::DatagramProcessor(QObject* parent) : + QObject(parent) +{ + +} + +void DatagramProcessor::processDatagrams() { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "DatagramProcessor::processDatagrams()"); + + HifiSockAddr senderSockAddr; + ssize_t bytesReceived; + + static unsigned char incomingPacket[MAX_PACKET_SIZE]; + + Application* application = Application::getInstance(); + + while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && + (bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) incomingPacket, + MAX_PACKET_SIZE, + senderSockAddr.getAddressPointer(), + senderSockAddr.getPortPointer()))) { + + _packetCount++; + _byteCount += bytesReceived; + + if (packetVersionMatch(incomingPacket)) { + // only process this packet if we have a match on the packet version + switch (incomingPacket[0]) { + case PACKET_TYPE_TRANSMITTER_DATA_V2: + // V2 = IOS transmitter app + application->_myTransmitter.processIncomingData(incomingPacket, bytesReceived); + + break; + case PACKET_TYPE_MIXED_AUDIO: + QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, + Q_ARG(QByteArray, QByteArray((char*) incomingPacket, bytesReceived))); + break; + + case PACKET_TYPE_PARTICLE_ADD_RESPONSE: + // this will keep creatorTokenIDs to IDs mapped correctly + Particle::handleAddParticleResponse(incomingPacket, bytesReceived); + break; + + case PACKET_TYPE_PARTICLE_DATA: + case PACKET_TYPE_PARTICLE_ERASE: + case PACKET_TYPE_VOXEL_DATA: + case PACKET_TYPE_VOXEL_ERASE: + case PACKET_TYPE_OCTREE_STATS: + case PACKET_TYPE_ENVIRONMENT_DATA: { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); + + bool wantExtraDebugging = application->getLogger()->extraDebugging(); + if (wantExtraDebugging && incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { + int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); + unsigned char* dataAt = incomingPacket + numBytesPacketHeader; + dataAt += sizeof(VOXEL_PACKET_FLAGS); + VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt); + dataAt += sizeof(VOXEL_PACKET_SEQUENCE); + VOXEL_PACKET_SENT_TIME sentAt = (*(VOXEL_PACKET_SENT_TIME*)dataAt); + dataAt += sizeof(VOXEL_PACKET_SENT_TIME); + VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); + int flightTime = arrivedAt - sentAt; + + printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); + } + + // add this packet to our list of voxel packets and process them on the voxel processing + application->_voxelProcessor.queueReceivedPacket(senderSockAddr, incomingPacket, bytesReceived); + break; + } + case PACKET_TYPE_METAVOXEL_DATA: + application->_metavoxels.processData(QByteArray((const char*) incomingPacket, bytesReceived), + senderSockAddr); + break; + case PACKET_TYPE_BULK_AVATAR_DATA: + NodeList::getInstance()->processBulkNodeData(senderSockAddr, + incomingPacket, + bytesReceived); + application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); + break; + case PACKET_TYPE_DATA_SERVER_GET: + case PACKET_TYPE_DATA_SERVER_PUT: + case PACKET_TYPE_DATA_SERVER_SEND: + case PACKET_TYPE_DATA_SERVER_CONFIRM: + DataServerClient::processMessageFromDataServer(incomingPacket, bytesReceived); + break; + default: + NodeList::getInstance()->processNodeData(senderSockAddr, incomingPacket, bytesReceived); + break; + } + } + } +} \ No newline at end of file diff --git a/interface/src/DatagramProcessor.h b/interface/src/DatagramProcessor.h new file mode 100644 index 0000000000..722e5a9d41 --- /dev/null +++ b/interface/src/DatagramProcessor.h @@ -0,0 +1,31 @@ +// +// DatagramProcessor.h +// hifi +// +// Created by Stephen Birarda on 1/23/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__DatagramProcessor__ +#define __hifi__DatagramProcessor__ + +#include + +class DatagramProcessor : public QObject { + Q_OBJECT +public: + DatagramProcessor(QObject* parent = 0); + + int getPacketCount() const { return _packetCount; } + int getByteCount() const { return _byteCount; } + + void resetCounters() { _packetCount = 0; _byteCount = 0; } +public slots: + void processDatagrams(); + +private: + int _packetCount; + int _byteCount; +}; + +#endif /* defined(__hifi__DatagramProcessor__) */ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1723e10f61..c717957da7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,6 +32,7 @@ #include "Menu.h" #include "Util.h" #include "InfoView.h" +#include "ui/MetavoxelEditor.h" Menu* Menu::_instance = NULL; @@ -220,6 +221,8 @@ Menu::Menu() : SLOT(increaseVoxelSize())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); + QMenu* viewMenu = addMenu("View"); @@ -352,6 +355,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false); addDisabledActionAndSeparator(developerMenu, "Testing"); @@ -898,7 +902,7 @@ void Menu::goToDomain() { } // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); // give our nodeList the new domain-server hostname NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); @@ -940,7 +944,7 @@ void Menu::goToLocation() { if (newAvatarPos != avatarPos) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); qDebug("Going To Location: %f, %f, %f...", x, y, z); myAvatar->setPosition(newAvatarPos); @@ -1009,6 +1013,13 @@ void Menu::bandwidthDetails() { _bandwidthDialog->raise(); } +void Menu::showMetavoxelEditor() { + if (!_MetavoxelEditor) { + _MetavoxelEditor = new MetavoxelEditor(); + } + _MetavoxelEditor->raise(); +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); muteAction->setChecked(Application::getInstance()->getAudio()->getMuted()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b356f29a85..3f597bb4ad 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -36,8 +37,9 @@ struct ViewFrustumOffset { class QSettings; class BandwidthDialog; -class VoxelStatsDialog; class LodToolsDialog; +class MetavoxelEditor; +class VoxelStatsDialog; class Menu : public QMenuBar, public AbstractMenuInterface { Q_OBJECT @@ -107,6 +109,7 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); + void showMetavoxelEditor(); void audioMuteToggled(); private: @@ -140,6 +143,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; + QPointer _MetavoxelEditor; VoxelStatsDialog* _voxelStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; @@ -188,6 +192,7 @@ namespace MenuOption { const QString ExportVoxels = "Export Voxels"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString HeadMouse = "Head Mouse"; + const QString HandsCollideWithSelf = "Collide With Self"; const QString FaceshiftTCP = "Faceshift (TCP)"; const QString FalseColorByDistance = "FALSE Color By Distance"; const QString FalseColorBySource = "FALSE Color By Source"; @@ -218,6 +223,7 @@ namespace MenuOption { const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; const QString LookAtVectors = "Look-at Vectors"; + const QString MetavoxelEditor = "Metavoxel Editor..."; const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString MoveWithLean = "Move with Lean"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index fc72f7444e..258db6da00 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -6,8 +6,6 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include -#include #include #include @@ -40,14 +38,6 @@ void MetavoxelSystem::init() { connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); - - QFile scriptFile("resources/scripts/sphere.js"); - scriptFile.open(QIODevice::ReadOnly); - QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); - _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), - encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); - _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); } @@ -156,16 +146,17 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute()), + AttributeRegistry::getInstance()->getNormalAttribute(), + QVector()), _points(points) { } -bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { +bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return true; } - QRgb color = info.attributeValues.at(0).getInlineValue(); - QRgb normal = info.attributeValues.at(1).getInlineValue(); + QRgb color = info.inputValues.at(0).getInlineValue(); + QRgb normal = info.inputValues.at(1).getInlineValue(); int alpha = qAlpha(color); if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index a58729285d..708b4d0839 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -30,18 +29,23 @@ class MetavoxelSystem : public QObject { Q_OBJECT public: + MetavoxelSystem(); void init(); + MetavoxelData& getData() { return _data; } + void processData(const QByteArray& data, const HifiSockAddr& sender); void simulate(float deltaTime); void render(); public slots: + void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); + private: Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); @@ -58,7 +62,7 @@ private: class PointVisitor : public MetavoxelVisitor { public: PointVisitor(QVector& points); - virtual bool visit(const MetavoxelInfo& info); + virtual bool visit(MetavoxelInfo& info); private: QVector& _points; @@ -67,7 +71,6 @@ private: static ProgramObject _program; static int _pointScaleLocation; - QScriptEngine _scriptEngine; MetavoxelData _data; QVector _points; PointVisitor _pointVisitor; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index d557eb67b3..ffee4c7879 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -332,27 +332,29 @@ void renderWorldBox() { glVertex3f(TREE_SCALE, 0, TREE_SCALE); glVertex3f(TREE_SCALE, 0, 0); glEnd(); - // Draw marker dots at very end + // Draw meter markers along the 3 axis to help with measuring things + const float MARKER_DISTANCE = 1.f; + const float MARKER_RADIUS = 0.05f; glEnable(GL_LIGHTING); glPushMatrix(); - glTranslatef(TREE_SCALE, 0, 0); + glTranslatef(MARKER_DISTANCE, 0, 0); glColor3fv(red); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); - glTranslatef(0, TREE_SCALE, 0); + glTranslatef(0, MARKER_DISTANCE, 0); glColor3fv(green); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); - glTranslatef(0, 0, TREE_SCALE); + glTranslatef(0, 0, MARKER_DISTANCE); glColor3fv(blue); - glutSolidSphere(0.125, 10, 10); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); glPushMatrix(); glColor3fv(gray); - glTranslatef(TREE_SCALE, 0, TREE_SCALE); - glutSolidSphere(0.125, 10, 10); + glTranslatef(MARKER_DISTANCE, 0, MARKER_DISTANCE); + glutSolidSphere(MARKER_RADIUS, 10, 10); glPopMatrix(); } diff --git a/interface/src/VoxelHideShowThread.h b/interface/src/VoxelHideShowThread.h index 22df6c299c..2925888022 100644 --- a/interface/src/VoxelHideShowThread.h +++ b/interface/src/VoxelHideShowThread.h @@ -15,7 +15,7 @@ #include "VoxelSystem.h" /// Generalized threaded processor for handling received inbound packets. -class VoxelHideShowThread : public virtual GenericThread { +class VoxelHideShowThread : public GenericThread { public: VoxelHideShowThread(VoxelSystem* theSystem); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 51f1bc10de..e5aa33ad40 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -231,14 +231,16 @@ void Hand::updateCollisions() { } } - // and the current avatar (ignoring everything below the parent of the parent of the last free joint) - glm::vec3 owningPenetration; - const Model& skeletonModel = _owningAvatar->getSkeletonModel(); - int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( - skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : - (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); - if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) { - totalPenetration = addPenetrations(totalPenetration, owningPenetration); + if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { + // and the current avatar (ignoring everything below the parent of the parent of the last free joint) + glm::vec3 owningPenetration; + const Model& skeletonModel = _owningAvatar->getSkeletonModel(); + int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( + skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : + (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); + if (_owningAvatar->findSpherePenetration(palm.getPosition(), scaledPalmRadius, owningPenetration, skipIndex)) { + totalPenetration = addPenetrations(totalPenetration, owningPenetration); + } } // un-penetrate diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index dfd06daa4a..dd2b547c47 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -48,8 +48,12 @@ QString Profile::getUserString() const { void Profile::setUUID(const QUuid& uuid) { _uuid = uuid; - // when the UUID is changed we need set it appropriately on the NodeList instance - NodeList::getInstance()->setOwnerUUID(uuid); + if (!_uuid.isNull()) { + qDebug() << "Changing NodeList owner UUID to" << uuid; + + // when the UUID is changed we need set it appropriately on the NodeList instance + NodeList::getInstance()->setOwnerUUID(uuid); + } } void Profile::setFaceModelURL(const QUrl& faceModelURL) { @@ -194,7 +198,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString if (coordinateItems.size() == 3 && orientationItems.size() == 3) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + NodeList::getInstance()->sendKillNode(QSet() << NODE_TYPE_AVATAR_MIXER); qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << ", position to" << valueList[i + 1].toLocal8Bit().constData() << @@ -220,7 +224,7 @@ void Profile::processDataServerResponse(const QString& userString, const QString } else if (keyList[i] == DataServerKey::UUID) { // this is the user's UUID - set it on the profile - _uuid = QUuid(valueList[i]); + setUUID(QUuid(valueList[i])); } } } diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 71e3b54a3b..bdb3916694 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -7,8 +7,11 @@ #include +// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include -#include +#include #include "Application.h" #include "GeometryCache.h" @@ -242,6 +245,50 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } +void GeometryCache::renderGrid(int xDivisions, int yDivisions) { + QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)]; + int vertices = (xDivisions + 1 + yDivisions + 1) * 2; + if (!buffer.isCreated()) { + GLfloat* vertexData = new GLfloat[vertices * 2]; + GLfloat* vertex = vertexData; + for (int i = 0; i <= xDivisions; i++) { + float x = (float)i / xDivisions; + + *(vertex++) = x; + *(vertex++) = 0.0f; + + *(vertex++) = x; + *(vertex++) = 1.0f; + } + for (int i = 0; i <= yDivisions; i++) { + float y = (float)i / yDivisions; + + *(vertex++) = 0.0f; + *(vertex++) = y; + + *(vertex++) = 1.0f; + *(vertex++) = y; + } + buffer.create(); + buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffer.bind(); + buffer.allocate(vertexData, vertices * 2 * sizeof(GLfloat)); + delete[] vertexData; + + } else { + buffer.bind(); + } + glEnableClientState(GL_VERTEX_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, 0); + + glDrawArrays(GL_LINES, 0, vertices); + + glDisableClientState(GL_VERTEX_ARRAY); + + buffer.release(); +} + QSharedPointer GeometryCache::getGeometry(const QUrl& url) { QSharedPointer geometry = _networkGeometry.value(url); if (geometry.isNull()) { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 8a68917ba5..312d8bcd91 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -19,6 +19,7 @@ #include "InterfaceConfig.h" class QNetworkReply; +class QOpenGLBuffer; class NetworkGeometry; class NetworkMesh; @@ -33,6 +34,7 @@ public: void renderHemisphere(int slices, int stacks); void renderSquare(int xDivisions, int yDivisions); void renderHalfCylinder(int slices, int stacks); + void renderGrid(int xDivisions, int yDivisions); /// Loads geometry from the specified URL. QSharedPointer getGeometry(const QUrl& url); @@ -45,6 +47,7 @@ private: QHash _hemisphereVBOs; QHash _squareVBOs; QHash _halfCylinderVBOs; + QHash _gridBuffers; QHash > _networkGeometry; }; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp new file mode 100644 index 0000000000..df932a9933 --- /dev/null +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -0,0 +1,406 @@ +// +// MetavoxelEditor.cpp +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Application.h" +#include "MetavoxelEditor.h" + +enum GridPlane { + GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ +}; + +const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); + +MetavoxelEditor::MetavoxelEditor() : + QDialog(Application::getInstance()->getGLWidget()) { + + setWindowTitle("Metavoxel Editor"); + setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QGroupBox* attributeGroup = new QGroupBox(); + attributeGroup->setTitle("Attributes"); + topLayout->addWidget(attributeGroup); + + QVBoxLayout* attributeLayout = new QVBoxLayout(); + attributeGroup->setLayout(attributeLayout); + + attributeLayout->addWidget(_attributes = new QListWidget()); + connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor())); + + QPushButton* newAttribute = new QPushButton("New..."); + attributeLayout->addWidget(newAttribute); + connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + + QFormLayout* formLayout = new QFormLayout(); + topLayout->addLayout(formLayout); + + formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox()); + _gridPlane->addItem("X/Y"); + _gridPlane->addItem("X/Z"); + _gridPlane->addItem("Y/Z"); + _gridPlane->setCurrentIndex(GRID_PLANE_XZ); + + formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); + _gridSpacing->setValue(0.1); + _gridSpacing->setMaximum(FLT_MAX); + _gridSpacing->setSingleStep(0.01); + connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition())); + + formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); + _gridPosition->setSingleStep(0.1); + _gridPosition->setMinimum(-FLT_MAX); + _gridPosition->setMaximum(FLT_MAX); + + _value = new QGroupBox(); + _value->setTitle("Value"); + topLayout->addWidget(_value); + + QVBoxLayout* valueLayout = new QVBoxLayout(); + _value->setLayout(valueLayout); + + updateAttributes(); + + connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + + Application::getInstance()->getGLWidget()->installEventFilter(this); + + resetState(); + + show(); + + if (_gridProgram.isLinked()) { + return; + } + switchToResourcesParentIfRequired(); + _gridProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/grid.frag"); + _gridProgram.link(); +} + +bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { + switch (_state) { + case HOVERING_STATE: + if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { + _state = DRAGGING_STATE; + return true; + } + break; + + case DRAGGING_STATE: + if (event->type() == QEvent::MouseButtonRelease) { + _state = RAISING_STATE; + return true; + } + break; + + case RAISING_STATE: + if (event->type() == QEvent::MouseButtonPress) { + if (_height != 0) { + // find the start and end corners in X/Y + float base = _gridPosition->value(); + float top = base + _height; + glm::quat rotation = getGridRotation(); + glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top)); + float spacing = _gridSpacing->value(); + glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + + glm::vec2(spacing, spacing), glm::max(base, top)); + + // find the minimum and maximum extents after rotation + applyValue(glm::min(start, end), glm::max(start, end)); + } + resetState(); + return true; + } + break; + } + return false; +} + +void MetavoxelEditor::updateValueEditor() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + _value->setVisible(false); + return; + } + _value->setVisible(true); + + if (!_value->layout()->isEmpty()) { + delete _value->layout()->takeAt(0); + } + + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + QWidget* editor = attribute->createEditor(); + if (editor) { + _value->layout()->addWidget(editor); + } +} + +void MetavoxelEditor::createNewAttribute() { + QDialog dialog(this); + dialog.setWindowTitle("New Attribute"); + + QVBoxLayout layout; + dialog.setLayout(&layout); + + QFormLayout form; + layout.addLayout(&form); + + QLineEdit name; + form.addRow("Name:", &name); + + QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); + + layout.addWidget(&buttons); + + if (!dialog.exec()) { + return; + } + QString nameText = name.text().trimmed(); + AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText)); + + updateAttributes(nameText); +} + +void MetavoxelEditor::updateGridPosition() { + // make sure our grid position matches our grid spacing + double step = _gridSpacing->value(); + if (step > 0.0) { + _gridPosition->setSingleStep(step); + _gridPosition->setValue(step * floor(_gridPosition->value() / step)); + } +} + +void MetavoxelEditor::render() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + resetState(); + return; + } + + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getGridRotation(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); + float spacing = _gridSpacing->value(); + float position = _gridPosition->value(); + if (_state == RAISING_STATE) { + // find the plane at the mouse position, orthogonal to the plane, facing the eye position + glLineWidth(4.0f); + glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); + glm::vec3 mousePoint = glm::vec3(_mousePosition, position); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); + glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); + float divisor = glm::dot(normal, rayDirection); + if (fabs(divisor) > EPSILON) { + float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; + float projection = rayOrigin.z + distance * rayDirection.z; + _height = spacing * roundf(projection / spacing) - position; + } + } else if (fabs(rayDirection.z) > EPSILON) { + // find the intersection of the rotated mouse ray with the plane + float distance = (position - rayOrigin.z) / rayDirection.z; + _mousePosition = glm::vec2(rayOrigin + rayDirection * distance); + glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing); + + if (_state == HOVERING_STATE) { + _startPosition = _endPosition = snappedPosition; + glLineWidth(2.0f); + + } else if (_state == DRAGGING_STATE) { + _endPosition = snappedPosition; + glLineWidth(4.0f); + } + } else { + // cancel any operation in progress + resetState(); + } + + const float GRID_BRIGHTNESS = 0.5f; + if (_startPosition != INVALID_VECTOR) { + glm::vec2 minimum = glm::min(_startPosition, _endPosition); + glm::vec2 maximum = glm::max(_startPosition, _endPosition); + + glPushMatrix(); + glTranslatef(minimum.x, minimum.y, position); + glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height); + + glTranslatef(0.5f, 0.5f, 0.5f); + if (_state != HOVERING_STATE) { + const float BOX_ALPHA = 0.25f; + QColor color = getValue().value(); + if (color.isValid()) { + glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); + } else { + glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + } + glEnable(GL_CULL_FACE); + glutSolidCube(1.0); + glDisable(GL_CULL_FACE); + } + glutWireCube(1.0); + + glPopMatrix(); + } + + glLineWidth(1.0f); + + // center the grid around the camera position on the plane + glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition(); + const int GRID_DIVISIONS = 300; + glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position); + + float scale = GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + _gridProgram.bind(); + + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); +} + +void MetavoxelEditor::updateAttributes(const QString& select) { + // remember the selection in order to preserve it + QString selected = select.isNull() ? getSelectedAttribute() : select; + _attributes->clear(); + + // sort the names for consistent ordering + QList names = AttributeRegistry::getInstance()->getAttributes().keys(); + qSort(names); + + foreach (const QString& name, names) { + QListWidgetItem* item = new QListWidgetItem(name); + _attributes->addItem(item); + if (name == selected || selected.isNull()) { + item->setSelected(true); + selected = name; + } + } +} + +QString MetavoxelEditor::getSelectedAttribute() const { + QList selectedItems = _attributes->selectedItems(); + return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +} + +glm::quat MetavoxelEditor::getGridRotation() const { + // for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there + switch (_gridPlane->currentIndex()) { + case GRID_PLANE_XY: + return glm::quat(); + + case GRID_PLANE_XZ: + return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); + + case GRID_PLANE_YZ: + default: + return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); + } +} + +void MetavoxelEditor::resetState() { + _state = HOVERING_STATE; + _startPosition = INVALID_VECTOR; + _height = 0.0f; +} + +class Applier : public MetavoxelVisitor { +public: + + Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value); + + virtual bool visit(MetavoxelInfo& info); + +protected: + + glm::vec3 _minimum; + glm::vec3 _maximum; + float _granularity; + AttributeValue _value; +}; + +Applier::Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value) : + MetavoxelVisitor(QVector(), QVector() << value.getAttribute()), + _minimum(minimum), + _maximum(maximum), + _granularity(granularity), + _value(value) { +} + +bool Applier::visit(MetavoxelInfo& info) { + // find the intersection between volume and voxel + glm::vec3 minimum = glm::max(info.minimum, _minimum); + glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _maximum); + glm::vec3 size = maximum - minimum; + if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { + return false; // disjoint + } + float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); + if (volume >= 1.0f) { + info.outputValues[0] = _value; + return false; // entirely contained + } + if (info.size <= _granularity) { + if (volume > 0.5f) { + info.outputValues[0] = _value; + } + return false; // reached granularity limit; take best guess + } + return true; // subdivide +} + +void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute()); + if (!attribute) { + return; + } + OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); + + Applier applier(minimum, maximum, _gridSpacing->value(), value); + Application::getInstance()->getMetavoxels()->getData().guide(applier); +} + +QVariant MetavoxelEditor::getValue() const { + if (_value->layout()->isEmpty()) { + return QVariant(); + } + QWidget* editor = _value->layout()->itemAt(0)->widget(); + return editor->metaObject()->userProperty().read(editor); +} + +ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h new file mode 100644 index 0000000000..21c8478d95 --- /dev/null +++ b/interface/src/ui/MetavoxelEditor.h @@ -0,0 +1,67 @@ +// +// MetavoxelEditor.h +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelEditor__ +#define __interface__MetavoxelEditor__ + +#include + +#include "renderer/ProgramObject.h" + +class QComboBox; +class QDoubleSpinBox; +class QGroupBox; +class QListWidget; + +/// Allows editing metavoxels. +class MetavoxelEditor : public QDialog { + Q_OBJECT + +public: + + MetavoxelEditor(); + + virtual bool eventFilter(QObject* watched, QEvent* event); + +private slots: + + void updateValueEditor(); + void createNewAttribute(); + void updateGridPosition(); + + void render(); + +private: + + void updateAttributes(const QString& select = QString()); + QString getSelectedAttribute() const; + glm::quat getGridRotation() const; + void resetState(); + void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + QVariant getValue() const; + + QListWidget* _attributes; + QComboBox* _gridPlane; + QDoubleSpinBox* _gridSpacing; + QDoubleSpinBox* _gridPosition; + QGroupBox* _value; + + enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; + + State _state; + + glm::vec2 _mousePosition; ///< the position of the mouse in rotated space + + glm::vec2 _startPosition; ///< the first corner of the selection base + glm::vec2 _endPosition; ///< the second corner of the selection base + float _height; ///< the selection height + + static ProgramObject _gridProgram; +}; + +#endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9a1f220034..5b7a8859ca 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -6,7 +6,10 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include +#include #include +#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -69,12 +72,12 @@ bool AttributeValue::operator==(void* other) const { return _attribute && _attribute->equal(_value, other); } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : - AttributeValue(attribute, attribute ? attribute->create() : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : + AttributeValue(attribute, value) { } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : - AttributeValue(attribute, attribute ? attribute->create(value) : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : + AttributeValue(attribute, attribute ? attribute->create() : NULL) { } OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : @@ -92,7 +95,7 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) _attribute->destroy(_value); } if ((_attribute = other.getAttribute())) { - _value = _attribute->create(other.getValue()); + _value = other.copy(); } return *this; } @@ -132,6 +135,41 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* return encodeInline((QRgb)value.toUInt32()); } +void* QRgbAttribute::createFromVariant(const QVariant& value) const { + switch (value.userType()) { + case QMetaType::QColor: + return encodeInline(value.value().rgba()); + + default: + return encodeInline((QRgb)value.toUInt()); + } +} + +QWidget* QRgbAttribute::createEditor(QWidget* parent) const { + QRgbEditor* editor = new QRgbEditor(parent); + editor->setColor(QColor::fromRgba(_defaultValue)); + return editor; +} + +QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { + setLayout(new QVBoxLayout()); + layout()->addWidget(_button = new QPushButton()); + connect(_button, SIGNAL(clicked()), SLOT(selectColor())); +} + +void QRgbEditor::setColor(const QColor& color) { + QString name = (_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); + _button->setText(name); +} + +void QRgbEditor::selectColor() { + QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); + if (color.isValid()) { + setColor(color); + } +} + PolymorphicData::~PolymorphicData() { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 4f2f1d79b2..058b02d78f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -16,9 +16,11 @@ #include #include #include +#include #include "Bitstream.h" +class QPushButton; class QScriptContext; class QScriptEngine; class QScriptValue; @@ -52,6 +54,9 @@ public: /// Retrieves an attribute by name. AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + /// Returns a reference to the attribute hash. + const QHash& getAttributes() const { return _attributes; } + /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } @@ -113,11 +118,19 @@ protected: class OwnedAttributeValue : public AttributeValue { public: - OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + /// Assumes ownership of the specified value. It will be destroyed when this is destroyed or reassigned. OwnedAttributeValue(const AttributePointer& attribute, void* value); + + /// Creates an owned attribute with a copy of the specified attribute's default value. + OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + + /// Creates an owned attribute with a copy of the specified other value. OwnedAttributeValue(const AttributeValue& other); + + /// Destroys the current value, if any. ~OwnedAttributeValue(); + /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); }; @@ -153,6 +166,12 @@ public: virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } + + virtual void* createFromVariant(const QVariant& value) const { return create(); } + + /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. + /// The widget should have a single "user" property that will be used to get/set the value. + virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } }; /// A simple attribute class that stores its values inline. @@ -222,6 +241,33 @@ public: virtual bool merge(void*& parent, void* children[]) const; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; + + virtual void* createFromVariant(const QVariant& value) const; + + virtual QWidget* createEditor(QWidget* parent = NULL) const; +}; + +/// Editor for RGBA values. +class QRgbEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true) + +public: + + QRgbEditor(QWidget* parent); + +public slots: + + void setColor(const QColor& color); + +private slots: + + void selectColor(); + +private: + + QPushButton* _button; + QColor _color; }; /// An attribute class that stores pointers to its values. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 5b7e71ca20..ffbe5b4ff8 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -32,46 +32,36 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { void MetavoxelData::guide(MetavoxelVisitor& visitor) { // start with the root values/defaults (plus the guide attribute) const float TOP_LEVEL_SIZE = 1.0f; - const QVector& attributes = visitor.getAttributes(); - MetavoxelVisitation firstVisitation = { visitor, QVector(attributes.size() + 1), - { glm::vec3(), TOP_LEVEL_SIZE, QVector(attributes.size() + 1) } }; - for (int i = 0; i < attributes.size(); i++) { - MetavoxelNode* node = _roots.value(attributes[i]); - firstVisitation.nodes[i] = node; - firstVisitation.info.attributeValues[i] = node ? node->getAttributeValue(attributes[i]) : attributes[i]; + const QVector& inputs = visitor.getInputs(); + const QVector& outputs = visitor.getOutputs(); + MetavoxelVisitation firstVisitation = { this, NULL, visitor, QVector(inputs.size() + 1), + QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE, + QVector(inputs.size() + 1), QVector(outputs.size()) } }; + for (int i = 0; i < inputs.size(); i++) { + MetavoxelNode* node = _roots.value(inputs.at(i)); + firstVisitation.inputNodes[i] = node; + firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[i]; } AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); MetavoxelNode* node = _roots.value(guideAttribute); - firstVisitation.nodes.last() = node; - firstVisitation.info.attributeValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; - static_cast(firstVisitation.info.attributeValues.last().getInlineValue< + firstVisitation.inputNodes.last() = node; + firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; + for (int i = 0; i < outputs.size(); i++) { + MetavoxelNode* node = _roots.value(outputs.at(i)); + firstVisitation.outputNodes[i] = node; + } + static_cast(firstVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(firstVisitation); -} - -void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue) { - MetavoxelNode*& node = _roots[attributeValue.getAttribute()]; - if (node == NULL) { - node = new MetavoxelNode(attributeValue.getAttribute()); - } - if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) { - node->decrementReferenceCount(attributeValue.getAttribute()); - _roots.remove(attributeValue.getAttribute()); - } -} - -AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const { - MetavoxelNode* node = _roots.value(attribute); - if (node == NULL) { - return AttributeValue(attribute); - } - for (int i = 0, n = path.getSize(); i < n; i++) { - MetavoxelNode* child = node->getChild(path[i]); - if (child == NULL) { - return node->getAttributeValue(attribute); + for (int i = 0; i < outputs.size(); i++) { + AttributeValue& value = firstVisitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode* node = firstVisitation.outputNodes.at(i); + if (node->isLeaf() && value.isDefault()) { + node->decrementReferenceCount(value.getAttribute()); + _roots.remove(value.getAttribute()); + } } - node = child; } - return node->getAttributeValue(attribute); } void MetavoxelData::read(Bitstream& in) { @@ -80,7 +70,7 @@ void MetavoxelData::read(Bitstream& in) { _roots.clear(); // read in the new roots, reusing old ones where appropriate - qint32 rootCount; + int rootCount; in >> rootCount; for (int i = 0; i < rootCount; i++) { AttributePointer attribute; @@ -100,7 +90,7 @@ void MetavoxelData::read(Bitstream& in) { } void MetavoxelData::write(Bitstream& out) const { - out << (qint32)_roots.size(); + out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out.getAttributeStreamer() << it.key(); it.value()->write(it.key(), out); @@ -108,9 +98,70 @@ void MetavoxelData::write(Bitstream& out) const { } void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { + int changedCount; + in >> changedCount; + for (int i = 0; i < changedCount; i++) { + AttributePointer attribute; + in.getAttributeStreamer() >> attribute; + MetavoxelNode*& root = _roots[attribute]; + if (!root) { + root = new MetavoxelNode(attribute); + } + MetavoxelNode* referenceRoot = reference._roots.value(attribute); + if (referenceRoot) { + root->readDelta(attribute, *referenceRoot, in); + + } else { + root->read(attribute, in); + } + } + + int removedCount; + in >> removedCount; + for (int i = 0; i < removedCount; i++) { + AttributePointer attribute; + in.getAttributeStreamer() >> attribute; + + } } void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { + // count the number of roots added/changed, then write + int changedCount = 0; + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + if (it.value() != referenceRoot) { + changedCount++; + } + } + out << changedCount; + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + if (it.value() != referenceRoot) { + out.getAttributeStreamer() << it.key(); + if (referenceRoot) { + it.value()->writeDelta(it.key(), *referenceRoot, out); + } else { + it.value()->write(it.key(), out); + } + } + } + + // same with nodes removed + int removedCount = 0; + for (QHash::const_iterator it = reference._roots.constBegin(); + it != reference._roots.constEnd(); it++) { + if (!_roots.contains(it.key())) { + removedCount++; + } + } + out << removedCount; + for (QHash::const_iterator it = reference._roots.constBegin(); + it != reference._roots.constEnd(); it++) { + if (!_roots.contains(it.key())) { + out.getAttributeStreamer() << it.key(); + } + } } void MetavoxelData::incrementRootReferenceCounts() { @@ -128,10 +179,11 @@ void MetavoxelData::decrementRootReferenceCounts() { void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) { if (data == reference) { out << false; - return; + + } else { + out << true; + data->writeDelta(*reference, out); } - out << true; - data->writeDelta(*reference, out); } void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in) { @@ -140,6 +192,9 @@ void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference if (changed) { data.detach(); data->readDelta(*reference, in); + + } else { + data = reference; } } @@ -150,34 +205,6 @@ MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceC } } -bool MetavoxelNode::setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue) { - if (index == path.getSize()) { - setAttributeValue(attributeValue); - return true; - } - int element = path[index]; - if (_children[element] == NULL) { - AttributeValue ownAttributeValue = getAttributeValue(attributeValue.getAttribute()); - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = new MetavoxelNode(ownAttributeValue); - } - } - _children[element]->setAttributeValue(path, index + 1, attributeValue); - - void* childValues[CHILD_COUNT]; - bool allLeaves = true; - for (int i = 0; i < CHILD_COUNT; i++) { - childValues[i] = _children[i]->_attributeValue; - allLeaves &= _children[i]->isLeaf(); - } - if (attributeValue.getAttribute()->merge(_attributeValue, childValues) && allLeaves) { - clearChildren(attributeValue.getAttribute()); - return true; - } - - return false; -} - void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { attributeValue.getAttribute()->destroy(_attributeValue); _attributeValue = attributeValue.copy(); @@ -188,6 +215,18 @@ AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribut return AttributeValue(attribute, _attributeValue); } +void MetavoxelNode::mergeChildren(const AttributePointer& attribute) { + void* childValues[CHILD_COUNT]; + bool allLeaves = true; + for (int i = 0; i < CHILD_COUNT; i++) { + childValues[i] = _children[i]->_attributeValue; + allLeaves &= _children[i]->isLeaf(); + } + if (attribute->merge(_attributeValue, childValues) && allLeaves) { + clearChildren(attribute); + } +} + bool MetavoxelNode::isLeaf() const { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { @@ -229,11 +268,6 @@ void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) con } void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { - bool different; - in >> different; - if (!different) { - return; - } bool leaf; in >> leaf; attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); @@ -254,11 +288,6 @@ void MetavoxelNode::readDelta(const AttributePointer& attribute, const Metavoxel } void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const { - if (this == &reference) { - out << false; - return; - } - out << true; bool leaf = isLeaf(); out << leaf; attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf); @@ -300,18 +329,9 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { } } -int MetavoxelPath::operator[](int index) const { - return (int)_array.at(index * BITS_PER_ELEMENT) | ((int)_array.at(index * BITS_PER_ELEMENT + 1) << 1) | - ((int)_array.at(index * BITS_PER_ELEMENT + 2) << 2); -} - -MetavoxelPath& MetavoxelPath::operator+=(int element) { - int offset = _array.size(); - _array.resize(offset + BITS_PER_ELEMENT); - _array.setBit(offset, element & 0x01); - _array.setBit(offset + 1, (element >> 1) & 0x01); - _array.setBit(offset + 2, element >> 2); - return *this; +MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs) : + _inputs(inputs), + _outputs(outputs) { } PolymorphicData* DefaultMetavoxelGuide::clone() const { @@ -323,42 +343,84 @@ const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - visitation.info.isLeaf = visitation.allNodesLeaves(); - if (!visitation.visitor.visit(visitation.info) || visitation.info.isLeaf) { + visitation.info.isLeaf = visitation.allInputNodesLeaves(); + bool keepGoing = visitation.visitor.visit(visitation.info); + for (int i = 0; i < visitation.outputNodes.size(); i++) { + AttributeValue& value = visitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode*& node = visitation.outputNodes[i]; + if (!node) { + node = visitation.createOutputNode(i); + } + node->setAttributeValue(value); + } + } + if (!keepGoing) { return; } - MetavoxelVisitation nextVisitation = { visitation.visitor, QVector(visitation.nodes.size()), - { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.nodes.size()) } }; + MetavoxelVisitation nextVisitation = { visitation.data, &visitation, visitation.visitor, + QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), + { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), + QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - for (int j = 0; j < visitation.nodes.size(); j++) { - MetavoxelNode* node = visitation.nodes.at(j); + for (int j = 0; j < visitation.inputNodes.size(); j++) { + MetavoxelNode* node = visitation.inputNodes.at(j); MetavoxelNode* child = node ? node->getChild(i) : NULL; - nextVisitation.info.attributeValues[j] = ((nextVisitation.nodes[j] = child)) ? - child->getAttributeValue(visitation.info.attributeValues[j].getAttribute()) : - visitation.info.attributeValues[j]; + nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? + child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : + visitation.info.inputValues[j]; + } + for (int j = 0; j < visitation.outputNodes.size(); j++) { + MetavoxelNode* node = visitation.outputNodes.at(j); + MetavoxelNode* child = node ? node->getChild(i) : NULL; + nextVisitation.outputNodes[j] = child; } nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); - static_cast(nextVisitation.info.attributeValues.last().getInlineValue< + nextVisitation.childIndex = i; + static_cast(nextVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(nextVisitation); + for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { + AttributeValue& value = nextVisitation.info.outputValues[j]; + if (value.getAttribute()) { + visitation.info.outputValues[j] = value; + value = AttributeValue(); + } + } + } + for (int i = 0; i < visitation.outputNodes.size(); i++) { + AttributeValue& value = visitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode* node = visitation.outputNodes.at(i); + node->mergeChildren(value.getAttribute()); + value = node->getAttributeValue(value.getAttribute()); + } } } -QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); +static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, + const QVector& attributes) { - const QVector& attributes = guide->_visitation->visitor.getAttributes(); QScriptValue attributesValue = engine->newArray(attributes.size()); for (int i = 0; i < attributes.size(); i++) { attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); } - return attributesValue; } +QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getInputs()); +} + +QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs()); +} + QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); @@ -367,26 +429,26 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin QScriptValue minimum = infoValue.property(guide->_minimumHandle); MetavoxelInfo info = { glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), - infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.attributeValues, - infoValue.property(guide->_isLeafHandle).toBool() }; + infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, + guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; // extract and convert the values provided by the script - QScriptValue attributeValues = infoValue.property(guide->_attributeValuesHandle); - const QVector& attributes = guide->_visitation->visitor.getAttributes(); - for (int i = 0; i < attributes.size(); i++) { - QScriptValue attributeValue = attributeValues.property(i); + QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); + const QVector& inputs = guide->_visitation->visitor.getInputs(); + for (int i = 0; i < inputs.size(); i++) { + QScriptValue attributeValue = inputValues.property(i); if (attributeValue.isValid()) { - info.attributeValues[i] = AttributeValue(attributes.at(i), - attributes.at(i)->createFromScript(attributeValue, engine)); + info.inputValues[i] = AttributeValue(inputs.at(i), + inputs.at(i)->createFromScript(attributeValue, engine)); } } QScriptValue result = guide->_visitation->visitor.visit(info); // destroy any created values - for (int i = 0; i < attributes.size(); i++) { - if (attributeValues.property(i).isValid()) { - info.attributeValues[i].getAttribute()->destroy(info.attributeValues[i].getValue()); + for (int i = 0; i < inputs.size(); i++) { + if (inputValues.property(i).isValid()) { + info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue()); } } @@ -397,16 +459,19 @@ ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction _guideFunction(guideFunction), _minimumHandle(guideFunction.engine()->toStringHandle("minimum")), _sizeHandle(guideFunction.engine()->toStringHandle("size")), - _attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")), + _inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")), + _outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")), _isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")), - _getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 0)), + _getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)), + _getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)), _visitFunction(guideFunction.engine()->newFunction(visit, 1)), _info(guideFunction.engine()->newObject()), _minimum(guideFunction.engine()->newArray(3)) { _arguments.append(guideFunction.engine()->newObject()); QScriptValue visitor = guideFunction.engine()->newObject(); - visitor.setProperty("getAttributes", _getAttributesFunction); + visitor.setProperty("getInputs", _getInputsFunction); + visitor.setProperty("getOutputs", _getOutputsFunction); visitor.setProperty("visit", _visitFunction); _arguments[0].setProperty("visitor", visitor); _arguments[0].setProperty("info", _info); @@ -419,7 +484,7 @@ PolymorphicData* ScriptedMetavoxelGuide::clone() const { void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); - _getAttributesFunction.setData(data); + _getInputsFunction.setData(data); _visitFunction.setData(data); _minimum.setProperty(0, visitation.info.minimum.x); _minimum.setProperty(1, visitation.info.minimum.y); @@ -433,11 +498,29 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } -bool MetavoxelVisitation::allNodesLeaves() const { - foreach (MetavoxelNode* node, nodes) { +bool MetavoxelVisitation::allInputNodesLeaves() const { + foreach (MetavoxelNode* node, inputNodes) { if (node != NULL && !node->isLeaf()) { return false; } } return true; } + +MetavoxelNode* MetavoxelVisitation::createOutputNode(int index) { + const AttributePointer& attribute = visitor.getOutputs().at(index); + if (previous) { + MetavoxelNode*& parent = previous->outputNodes[index]; + if (!parent) { + parent = previous->createOutputNode(index); + } + AttributeValue value = parent->getAttributeValue(attribute); + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + parent->_children[i] = new MetavoxelNode(value); + } + return parent->_children[childIndex]; + + } else { + return data->_roots[attribute] = new MetavoxelNode(attribute); + } +} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index d588c8a687..a25e0bc9a8 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -24,7 +24,6 @@ class QScriptContext; class MetavoxelNode; -class MetavoxelPath; class MetavoxelVisitation; class MetavoxelVisitor; @@ -41,12 +40,6 @@ public: /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); - /// Sets the attribute value corresponding to the specified path. - void setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue); - - /// Retrieves the attribute value corresponding to the specified path. - AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const; - void read(Bitstream& in); void write(Bitstream& out) const; @@ -54,6 +47,8 @@ public: void writeDelta(const MetavoxelData& reference, Bitstream& out) const; private: + + friend class MetavoxelVisitation; void incrementRootReferenceCounts(); void decrementRootReferenceCounts(); @@ -74,17 +69,13 @@ public: static const int CHILD_COUNT = 8; MetavoxelNode(const AttributeValue& attributeValue); - - /// Descends the voxel tree in order to set the value of a node. - /// \param path the path to follow - /// \param index the position in the path - /// \return whether or not the node is entirely equal to the value - bool setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue); - + void setAttributeValue(const AttributeValue& attributeValue); AttributeValue getAttributeValue(const AttributePointer& attribute) const; + void mergeChildren(const AttributePointer& attribute); + MetavoxelNode* getChild(int index) const { return _children[index]; } void setChild(int index, MetavoxelNode* child) { _children[index] = child; } @@ -108,6 +99,8 @@ public: private: Q_DISABLE_COPY(MetavoxelNode) + friend class MetavoxelVisitation; + void clearChildren(const AttributePointer& attribute); int _referenceCount; @@ -115,31 +108,14 @@ private: MetavoxelNode* _children[CHILD_COUNT]; }; -/// A path down an octree. -class MetavoxelPath { -public: - - int getSize() const { return _array.size() / BITS_PER_ELEMENT; } - bool isEmpty() const { return _array.isEmpty(); } - - int operator[](int index) const; - - MetavoxelPath& operator+=(int element); - -private: - - static const int BITS_PER_ELEMENT = 3; - - QBitArray _array; -}; - /// Contains information about a metavoxel (explicit or procedural). class MetavoxelInfo { public: glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel float size; ///< the size of the voxel in all dimensions - QVector attributeValues; + QVector inputValues; + QVector outputValues; bool isLeaf; }; @@ -147,19 +123,23 @@ public: class MetavoxelVisitor { public: - MetavoxelVisitor(const QVector& attributes) : _attributes(attributes) { } - - /// Returns a reference to the list of attributes desired. - const QVector& getAttributes() const { return _attributes; } + MetavoxelVisitor(const QVector& inputs, const QVector& outputs); + + /// Returns a reference to the list of input attributes desired. + const QVector& getInputs() const { return _inputs; } + + /// Returns a reference to the list of output attributes provided. + const QVector& getOutputs() const { return _outputs; } /// Visits a metavoxel. - /// \param info the metavoxel ata - /// \param if true, continue descending; if false, stop - virtual bool visit(const MetavoxelInfo& info) = 0; + /// \param info the metavoxel data + /// \return if true, continue descending; if false, stop + virtual bool visit(MetavoxelInfo& info) = 0; protected: - QVector _attributes; + QVector _inputs; + QVector _outputs; }; /// Interface for objects that guide metavoxel visitors. @@ -191,16 +171,19 @@ public: private: - static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); QScriptValue _guideFunction; QScriptString _minimumHandle; QScriptString _sizeHandle; - QScriptString _attributeValuesHandle; + QScriptString _inputValuesHandle; + QScriptString _outputValuesHandle; QScriptString _isLeafHandle; QScriptValueList _arguments; - QScriptValue _getAttributesFunction; + QScriptValue _getInputsFunction; + QScriptValue _getOutputsFunction; QScriptValue _visitFunction; QScriptValue _info; QScriptValue _minimum; @@ -212,11 +195,16 @@ private: class MetavoxelVisitation { public: + MetavoxelData* data; + MetavoxelVisitation* previous; MetavoxelVisitor& visitor; - QVector nodes; + QVector inputNodes; + QVector outputNodes; MetavoxelInfo info; + int childIndex; - bool allNodesLeaves() const; + bool allInputNodesLeaves() const; + MetavoxelNode* createOutputNode(int index); }; #endif /* defined(__interface__MetavoxelData__) */ diff --git a/libraries/octree-server/src/OctreeSendThread.h b/libraries/octree-server/src/OctreeSendThread.h index a2de8fcb5d..c0936371f2 100644 --- a/libraries/octree-server/src/OctreeSendThread.h +++ b/libraries/octree-server/src/OctreeSendThread.h @@ -17,7 +17,7 @@ #include "OctreeServer.h" /// Threaded processor for sending voxel packets to a single client -class OctreeSendThread : public virtual GenericThread { +class OctreeSendThread : public GenericThread { public: OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 4a0ad0d827..cc362f58a0 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -569,8 +569,7 @@ void OctreeServer::run() { nodeList->setOwnerType(getMyNodeType()); // we need to ask the DS about agents so we can ping/reply with them - const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER}; - nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + nodeList->addNodeTypeToInterestSet(NODE_TYPE_AGENT); setvbuf(stdout, NULL, _IOLBF, 0); diff --git a/libraries/octree/src/JurisdictionListener.cpp b/libraries/octree/src/JurisdictionListener.cpp index d8919ac48e..e761a15e76 100644 --- a/libraries/octree/src/JurisdictionListener.cpp +++ b/libraries/octree/src/JurisdictionListener.cpp @@ -15,14 +15,17 @@ #include #include "JurisdictionListener.h" -JurisdictionListener::JurisdictionListener(NODE_TYPE type, PacketSenderNotify* notify) : - PacketSender(notify, JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) +JurisdictionListener::JurisdictionListener(NODE_TYPE type) : + _packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND) { _nodeType = type; ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to -// connect(nodeList, &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled); -// qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)\n", type); + connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled); + //qDebug("JurisdictionListener::JurisdictionListener(NODE_TYPE type=%c)", type); + + // tell our NodeList we want to hear about nodes with our node type + NodeList::getInstance()->addNodeTypeToInterestSet(type); } void JurisdictionListener::nodeKilled(SharedNodePointer node) { @@ -32,7 +35,7 @@ void JurisdictionListener::nodeKilled(SharedNodePointer node) { } bool JurisdictionListener::queueJurisdictionRequest() { - //qDebug() << "JurisdictionListener::queueJurisdictionRequest()\n"; + //qDebug() << "JurisdictionListener::queueJurisdictionRequest()"; static unsigned char buffer[MAX_PACKET_SIZE]; unsigned char* bufferOut = &buffer[0]; @@ -45,15 +48,15 @@ bool JurisdictionListener::queueJurisdictionRequest() { if (nodeList->getNodeActiveSocketOrPing(node.data()) && node->getType() == getNodeType()) { const HifiSockAddr* nodeAddress = node->getActiveSocket(); - PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut); + _packetSender.queuePacketForSending(*nodeAddress, bufferOut, sizeOut); nodeCount++; } } if (nodeCount > 0){ - setPacketsPerSecond(nodeCount); + _packetSender.setPacketsPerSecond(nodeCount); } else { - setPacketsPerSecond(NO_SERVER_CHECK_RATE); + _packetSender.setPacketsPerSecond(NO_SERVER_CHECK_RATE); } // keep going if still running @@ -61,6 +64,7 @@ bool JurisdictionListener::queueJurisdictionRequest() { } void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { + //qDebug() << "JurisdictionListener::processPacket()"; if (packetData[0] == PACKET_TYPE_JURISDICTION) { SharedNodePointer node = NodeList::getInstance()->nodeWithAddress(senderAddress); if (node) { @@ -73,12 +77,17 @@ void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsi } bool JurisdictionListener::process() { + //qDebug() << "JurisdictionListener::process()"; bool continueProcessing = isStillRunning(); // If we're still running, and we don't have any requests waiting to be sent, then queue our jurisdiction requests - if (continueProcessing && !hasPacketsToSend()) { + if (continueProcessing && !_packetSender.hasPacketsToSend()) { queueJurisdictionRequest(); - continueProcessing = PacketSender::process(); + } + + if (continueProcessing) { + //qDebug() << "JurisdictionListener::process() calling _packetSender.process()"; + continueProcessing = _packetSender.process(); } if (continueProcessing) { // NOTE: This will sleep if there are no pending packets to process diff --git a/libraries/octree/src/JurisdictionListener.h b/libraries/octree/src/JurisdictionListener.h index 393ca80420..110b2f0d98 100644 --- a/libraries/octree/src/JurisdictionListener.h +++ b/libraries/octree/src/JurisdictionListener.h @@ -22,12 +22,12 @@ /// the PACKET_TYPE_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions /// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets /// and adding them to the processing queue by calling queueReceivedPacket() -class JurisdictionListener : public PacketSender, public ReceivedPacketProcessor { +class JurisdictionListener : public ReceivedPacketProcessor { public: static const int DEFAULT_PACKETS_PER_SECOND = 1; static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps - JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL); + JurisdictionListener(NODE_TYPE type = NODE_TYPE_VOXEL_SERVER); virtual bool process(); @@ -55,5 +55,7 @@ private: NODE_TYPE _nodeType; bool queueJurisdictionRequest(); + + PacketSender _packetSender; }; #endif // __shared__JurisdictionListener__ diff --git a/libraries/octree/src/JurisdictionMap.cpp b/libraries/octree/src/JurisdictionMap.cpp index 53cc955553..bf64e19959 100644 --- a/libraries/octree/src/JurisdictionMap.cpp +++ b/libraries/octree/src/JurisdictionMap.cpp @@ -331,16 +331,20 @@ int JurisdictionMap::unpackFromMessage(unsigned char* sourceBuffer, int availabl // increment to push past the packet header int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); sourceBuffer += numBytesPacketHeader; + int remainingBytes = availableBytes - numBytesPacketHeader; // read the root jurisdiction int bytes = 0; memcpy(&bytes, sourceBuffer, sizeof(bytes)); sourceBuffer += sizeof(bytes); + remainingBytes -= sizeof(bytes); - if (bytes > 0) { + if (bytes > 0 && bytes <= remainingBytes) { _rootOctalCode = new unsigned char[bytes]; memcpy(_rootOctalCode, sourceBuffer, bytes); sourceBuffer += bytes; + remainingBytes -= bytes; + // if and only if there's a root jurisdiction, also include the end nodes int endNodeCount = 0; memcpy(&endNodeCount, sourceBuffer, sizeof(endNodeCount)); @@ -349,13 +353,18 @@ int JurisdictionMap::unpackFromMessage(unsigned char* sourceBuffer, int availabl int bytes = 0; memcpy(&bytes, sourceBuffer, sizeof(bytes)); sourceBuffer += sizeof(bytes); - unsigned char* endNodeCode = new unsigned char[bytes]; - memcpy(endNodeCode, sourceBuffer, bytes); - sourceBuffer += bytes; + remainingBytes -= sizeof(bytes); - // if the endNodeCode was 0 length then don't add it - if (bytes > 0) { - _endNodes.push_back(endNodeCode); + if (bytes <= remainingBytes) { + unsigned char* endNodeCode = new unsigned char[bytes]; + memcpy(endNodeCode, sourceBuffer, bytes); + sourceBuffer += bytes; + remainingBytes -= bytes; + + // if the endNodeCode was 0 length then don't add it + if (bytes > 0) { + _endNodes.push_back(endNodeCode); + } } } } diff --git a/libraries/octree/src/JurisdictionSender.cpp b/libraries/octree/src/JurisdictionSender.cpp index ab31f9356b..e1bbf78506 100644 --- a/libraries/octree/src/JurisdictionSender.cpp +++ b/libraries/octree/src/JurisdictionSender.cpp @@ -16,10 +16,10 @@ #include "JurisdictionSender.h" -JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type, PacketSenderNotify* notify) : - PacketSender(notify, JurisdictionSender::DEFAULT_PACKETS_PER_SECOND), +JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NODE_TYPE type) : ReceivedPacketProcessor(), - _jurisdictionMap(map) + _jurisdictionMap(map), + _packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND) { _nodeType = type; } @@ -66,16 +66,16 @@ bool JurisdictionSender::process() { if (node->getActiveSocket() != NULL) { const HifiSockAddr* nodeAddress = node->getActiveSocket(); - queuePacketForSending(*nodeAddress, bufferOut, sizeOut); + _packetSender.queuePacketForSending(*nodeAddress, bufferOut, sizeOut); nodeCount++; } } unlockRequestingNodes(); // set our packets per second to be the number of nodes - setPacketsPerSecond(nodeCount); + _packetSender.setPacketsPerSecond(nodeCount); - continueProcessing = PacketSender::process(); + continueProcessing = _packetSender.process(); } return continueProcessing; } diff --git a/libraries/octree/src/JurisdictionSender.h b/libraries/octree/src/JurisdictionSender.h index 441bb5ee1e..e70070b21d 100644 --- a/libraries/octree/src/JurisdictionSender.h +++ b/libraries/octree/src/JurisdictionSender.h @@ -21,12 +21,12 @@ /// Will process PACKET_TYPE_JURISDICTION_REQUEST packets and send out PACKET_TYPE_JURISDICTION packets /// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets /// and adding them to the processing queue by calling queueReceivedPacket() -class JurisdictionSender : public PacketSender, public ReceivedPacketProcessor { +class JurisdictionSender : public ReceivedPacketProcessor { Q_OBJECT public: static const int DEFAULT_PACKETS_PER_SECOND = 1; - JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER, PacketSenderNotify* notify = NULL); + JurisdictionSender(JurisdictionMap* map, NODE_TYPE type = NODE_TYPE_VOXEL_SERVER); ~JurisdictionSender(); void setJurisdiction(JurisdictionMap* map) { _jurisdictionMap = map; } @@ -51,5 +51,7 @@ private: JurisdictionMap* _jurisdictionMap; std::queue _nodesRequestingJurisdictions; NODE_TYPE _nodeType; + + PacketSender _packetSender; }; #endif // __shared__JurisdictionSender__ diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index dad97b18cd..5d25f0c164 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -592,6 +592,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { + // NOTE: it is possible for this penetration accumulation algorithm to produce a final penetration vector with zero length. args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE)); args->found = true; } @@ -1390,9 +1391,8 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) { bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); unlock(); - // if bytesWritten == 0, then it means that the subTree couldn't fit, and so we should reset the packet - // and reinsert the node in our bag and try again... - if (bytesWritten == 0) { + // if the subTree couldn't fit, and so we should reset the packet and reinsert the node in our bag and try again... + if (bytesWritten == 0 && (params.stopReason == EncodeBitstreamParams::DIDNT_FIT)) { if (packetData.hasContent()) { file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); lastPacketWritten = true; diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index a3d4e7862b..d4c1051747 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -27,8 +27,8 @@ EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssiz const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::DEFAULT_PACKETS_PER_SECOND; -OctreeEditPacketSender::OctreeEditPacketSender(PacketSenderNotify* notify) : - PacketSender(notify), +OctreeEditPacketSender::OctreeEditPacketSender() : + PacketSender(), _shouldSend(true), _maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES), _releaseQueuedMessagesPending(false), diff --git a/libraries/octree/src/OctreeEditPacketSender.h b/libraries/octree/src/OctreeEditPacketSender.h index 9539f309fd..f214796f9d 100644 --- a/libraries/octree/src/OctreeEditPacketSender.h +++ b/libraries/octree/src/OctreeEditPacketSender.h @@ -28,8 +28,9 @@ public: /// Utility for processing, packing, queueing and sending of outbound edit messages. class OctreeEditPacketSender : public PacketSender { + Q_OBJECT public: - OctreeEditPacketSender(PacketSenderNotify* notify = NULL); + OctreeEditPacketSender(); ~OctreeEditPacketSender(); /// Queues a single edit message. Will potentially send a pending multi-command packet. Determines which server diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index d72f2e7443..553ab961df 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + #include "OctreeScriptingInterface.h" OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender, @@ -16,18 +18,21 @@ OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packe } OctreeScriptingInterface::~OctreeScriptingInterface() { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface()\n"); + cleanupManagedObjects(); +} + +void OctreeScriptingInterface::cleanupManagedObjects() { if (_managedJurisdictionListener) { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _jurisdictionListener->terminate()\n"); _jurisdictionListener->terminate(); - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... deleting _jurisdictionListener\n"); - delete _jurisdictionListener; + _jurisdictionListener->deleteLater(); + _managedJurisdictionListener = false; + _jurisdictionListener = NULL; } if (_managedPacketSender) { - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedJurisdictionListener... _packetSender->terminate()\n"); _packetSender->terminate(); - //printf("OctreeScriptingInterface::~OctreeScriptingInterface() _managedPacketSender... deleting _packetSender\n"); - delete _packetSender; + _packetSender->deleteLater(); + _managedPacketSender = false; + _packetSender = NULL; } } @@ -40,13 +45,11 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur } void OctreeScriptingInterface::init() { - //printf("OctreeScriptingInterface::init()\n"); if (_jurisdictionListener) { _managedJurisdictionListener = false; } else { _managedJurisdictionListener = true; _jurisdictionListener = new JurisdictionListener(getServerNodeType()); - //printf("OctreeScriptingInterface::init() _managedJurisdictionListener=true, creating _jurisdictionListener=%p\n", _jurisdictionListener); _jurisdictionListener->initialize(true); } @@ -55,7 +58,11 @@ void OctreeScriptingInterface::init() { } else { _managedPacketSender = true; _packetSender = createPacketSender(); - //printf("OctreeScriptingInterface::init() _managedPacketSender=true, creating _packetSender=%p\n", _packetSender); _packetSender->setServerJurisdictions(_jurisdictionListener->getJurisdictions()); } + + if (QCoreApplication::instance()) { + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects())); + } + } diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index b9466fb9e9..1158f21438 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -84,6 +84,9 @@ public slots: /// returns the total bytes queued by this object over its lifetime long long unsigned int getLifetimeBytesQueued() const { return _packetSender->getLifetimeBytesQueued(); } + // TODO: hmmm... we don't want this called from JS, how to handle that? + void cleanupManagedObjects(); + protected: /// attached OctreeEditPacketSender that handles queuing and sending of packets to VS OctreeEditPacketSender* _packetSender; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 5570797ab4..5cf5e9248d 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -682,7 +682,7 @@ void Particle::update(const uint64_t& now) { const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds bool isReallyOld = ((now - _created) > REALLY_OLD); bool isInHand = getInHand(); - bool shouldDie = getShouldDie() || (!isInHand && isStopped && isReallyOld); + bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld); setShouldDie(shouldDie); runUpdateScript(); // allow the javascript to alter our state diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 160eee9db2..e534c7b418 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -42,7 +42,7 @@ const uint16_t PACKET_CONTAINS_INHAND = 128; const uint16_t PACKET_CONTAINS_SCRIPT = 256; const uint16_t PACKET_CONTAINS_SHOULDDIE = 512; -const float DEFAULT_LIFETIME = 60.0f * 60.0f * 24.0f; // particles live for 1 day by default +const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default const float DEFAULT_DAMPING = 0.99f; const float DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container @@ -136,6 +136,9 @@ public: ParticleID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { }; + ParticleID(uint32_t id) : + id(id), creatorTokenID(UNKNOWN_TOKEN), isKnownID(true) { }; + uint32_t id; uint32_t creatorTokenID; bool isKnownID; diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 61417aa735..8eb525a388 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -16,7 +16,6 @@ #include "Particle.h" #include "ParticleCollisionSystem.h" -#include "ParticleEditHandle.h" #include "ParticleEditPacketSender.h" #include "ParticleTree.h" @@ -80,8 +79,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { // let the particles run their collision scripts if they have them particle->collisionWithVoxel(voxelDetails); - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo); delete voxelDetails; // cleanup returned details @@ -117,21 +116,24 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) float massB = (particleB->getInHand()) ? MAX_MASS : particleB->getMass(); float totalMass = massA + massB; + // handle A particle particleA->setVelocity(particleA->getVelocity() - axialVelocity * (2.0f * massB / totalMass)); + ParticleProperties propertiesA; + ParticleID particleAid(particleA->getID()); + propertiesA.copyFromParticle(*particleA); + propertiesA.setVelocity(particleA->getVelocity() * (float)TREE_SCALE); + _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleAid, propertiesA); - ParticleEditHandle particleEditHandle(_packetSender, _particles, particleA->getID()); - particleEditHandle.updateParticle(particleA->getPosition(), particleA->getRadius(), particleA->getXColor(), - particleA->getVelocity(), particleA->getGravity(), particleA->getDamping(), particleA->getLifetime(), - particleA->getInHand(), particleA->getScript()); - + // handle B particle particleB->setVelocity(particleB->getVelocity() + axialVelocity * (2.0f * massA / totalMass)); + ParticleProperties propertiesB; + ParticleID particleBid(particleB->getID()); + propertiesB.copyFromParticle(*particleB); + propertiesB.setVelocity(particleB->getVelocity() * (float)TREE_SCALE); + _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleBid, propertiesB); - ParticleEditHandle penetratedparticleEditHandle(_packetSender, _particles, particleB->getID()); - penetratedparticleEditHandle.updateParticle(particleB->getPosition(), particleB->getRadius(), - particleB->getXColor(), particleB->getVelocity(), particleB->getGravity(), particleB->getDamping(), - particleB->getLifetime(), particleB->getInHand(), particleB->getScript()); + _packetSender->releaseQueuedMessages(); - penetration /= (float)(TREE_SCALE); updateCollisionSound(particleA, penetration, COLLISION_FREQUENCY); } } @@ -181,8 +183,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } // HACK END - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, elasticity, damping, collisionInfo); } } @@ -213,8 +215,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } // HACK END - collisionInfo._penetration /= (float)(TREE_SCALE); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._penetration /= (float)(TREE_SCALE); applyHardCollision(particle, ELASTICITY, damping, collisionInfo); } } @@ -256,17 +258,24 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elast particle->getID(), velocity.x, velocity.y, velocity.z, debug::valueOf(particle->getInHand())); } - ParticleEditHandle particleEditHandle(_packetSender, _particles, particle->getID()); - particleEditHandle.updateParticle(position, particle->getRadius(), particle->getXColor(), velocity, - particle->getGravity(), particle->getDamping(), particle->getLifetime(), - particle->getInHand(), particle->getScript()); + // send off the result to the particle server + ParticleProperties properties; + ParticleID particleID(particle->getID()); + properties.copyFromParticle(*particle); + properties.setPosition(position * (float)TREE_SCALE); + properties.setVelocity(velocity * (float)TREE_SCALE); + _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + + // change the local particle too... + particle->setPosition(position); + particle->setVelocity(velocity); } void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { // consider whether to have the collision make a sound - const float AUDIBLE_COLLISION_THRESHOLD = 0.1f; + const float AUDIBLE_COLLISION_THRESHOLD = 0.3f; const float COLLISION_LOUDNESS = 1.f; const float DURATION_SCALING = 0.004f; const float NOISE_SCALING = 0.1f; @@ -283,18 +292,20 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); } */ - float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration)); - float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision; + float normalSpeed = glm::dot(velocity, glm::normalize(penetration)); + // NOTE: it is possible for normalSpeed to be NaN at this point + // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized) + // however the check below will fail (NaN comparisons always fail) and everything will be fine. - if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) { + if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) { // Volume is proportional to collision velocity // Base frequency is modified upward by the angle of the collision // Noise is a function of the angle of collision // Duration of the sound is a function of both base frequency and velocity of impact - _audio->startCollisionSound( - std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.f), - frequency * (1.f + velocityTangentToCollision / velocityTowardCollision), - std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false); + float tangentialSpeed = glm::length(velocity) - normalSpeed; + _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), + frequency * (1.f + tangentialSpeed / normalSpeed), + std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); } } diff --git a/libraries/particles/src/ParticleEditHandle.cpp b/libraries/particles/src/ParticleEditHandle.cpp deleted file mode 100644 index 30ff71951d..0000000000 --- a/libraries/particles/src/ParticleEditHandle.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// ParticleEditHandle.cpp -// hifi -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// - -#include "Particle.h" -#include "ParticleEditHandle.h" -#include "ParticleEditPacketSender.h" -#include "ParticleTree.h" - -std::map ParticleEditHandle::_allHandles; - -ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id) { - if (id == NEW_PARTICLE) { - _creatorTokenID = Particle::getNextCreatorTokenID(); - _id = NEW_PARTICLE; - _isKnownID = false; - _allHandles[_creatorTokenID] = this; - } else { - _creatorTokenID = UNKNOWN_TOKEN; - _id = id; - _isKnownID = true; - // don't add to _allHandles because we already know it... - } - _packetSender = packetSender; - _localTree = localTree; - -} - -ParticleEditHandle::~ParticleEditHandle() { - // remove us from our _allHandles map - if (_creatorTokenID != UNKNOWN_TOKEN) { - _allHandles.erase(_allHandles.find(_creatorTokenID)); - } -} - -void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, - glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript) { - - // setup a ParticleDetail struct with the data -/**** - uint64_t now = usecTimestampNow(); - ParticleDetail addParticleDetail = { NEW_PARTICLE, now, - position, radius, {color.red, color.green, color.blue }, - velocity, gravity, damping, lifetime, inHand, updateScript, _creatorTokenID }; - - // queue the packet - _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &addParticleDetail); - - // release them - _packetSender->releaseQueuedMessages(); - - // if we have a local tree, also update it... - if (_localTree) { - // we can't really do this here, because if we create a particle locally, we'll get a ghost particle - // because we can't really handle updating/deleting it locally - } -****/ - -} - -bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, - glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript) { - - if (!isKnownID()) { - return false; // not allowed until we know the id - } - - // setup a ParticleDetail struct with the data -/**** - uint64_t now = usecTimestampNow(); - ParticleDetail newParticleDetail = { _id, now, - position, radius, {color.red, color.green, color.blue }, - velocity, gravity, damping, lifetime, inHand, updateScript, _creatorTokenID }; - - // queue the packet - _packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &newParticleDetail); - - // release them - _packetSender->releaseQueuedMessages(); - - // if we have a local tree, also update it... - if (_localTree) { - rgbColor rcolor = {color.red, color.green, color.blue }; - Particle tempParticle(position, radius, rcolor, velocity, gravity, damping, lifetime, inHand, updateScript, _id); - _localTree->storeParticle(tempParticle); - } -***/ - - return true; -} - -void ParticleEditHandle::handleAddResponse(unsigned char* packetData , int packetLength) { - unsigned char* dataAt = packetData; - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - dataAt += numBytesPacketHeader; - - uint32_t creatorTokenID; - memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); - dataAt += sizeof(creatorTokenID); - - uint32_t particleID; - memcpy(&particleID, dataAt, sizeof(particleID)); - dataAt += sizeof(particleID); - - // find this particle in the _allHandles map - if (_allHandles.find(creatorTokenID) != _allHandles.end()) { - ParticleEditHandle* theHandle = _allHandles[creatorTokenID]; - theHandle->_id = particleID; - theHandle->_isKnownID = true; - } -} diff --git a/libraries/particles/src/ParticleEditHandle.h b/libraries/particles/src/ParticleEditHandle.h deleted file mode 100644 index 5343b29343..0000000000 --- a/libraries/particles/src/ParticleEditHandle.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// ParticleEditHandle.h -// hifi -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// - -#ifndef __hifi__ParticleEditHandle__ -#define __hifi__ParticleEditHandle__ - -#include -#include - -#include -#include - -#include -#include - -#include "Particle.h" - -class ParticleEditPacketSender; -class ParticleTree; - -class ParticleEditHandle { -public: - ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree, uint32_t id = NEW_PARTICLE); - ~ParticleEditHandle(); - - uint32_t getCreatorTokenID() const { return _creatorTokenID; } - uint32_t getID() const { return _id; } - - bool isKnownID() const { return _isKnownID; } - - void createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, - glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript); - - bool updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, - glm::vec3 gravity, float damping, float lifetime, bool inHand, QString updateScript); - - static void handleAddResponse(unsigned char* packetData , int packetLength); -private: - uint32_t _creatorTokenID; - uint32_t _id; - bool _isKnownID; - static std::map _allHandles; - ParticleEditPacketSender* _packetSender; - ParticleTree* _localTree; -}; - -#endif /* defined(__hifi__ParticleEditHandle__) */ \ No newline at end of file diff --git a/libraries/particles/src/ParticleEditPacketSender.cpp b/libraries/particles/src/ParticleEditPacketSender.cpp index f745adc05b..3206a0d200 100644 --- a/libraries/particles/src/ParticleEditPacketSender.cpp +++ b/libraries/particles/src/ParticleEditPacketSender.cpp @@ -42,7 +42,8 @@ void ParticleEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeC } -void ParticleEditPacketSender::queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties) { +void ParticleEditPacketSender::queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, + const ParticleProperties& properties) { if (!_shouldSend) { return; // bail early } diff --git a/libraries/particles/src/ParticleEditPacketSender.h b/libraries/particles/src/ParticleEditPacketSender.h index c45a3ef8c4..51f0fd9b96 100644 --- a/libraries/particles/src/ParticleEditPacketSender.h +++ b/libraries/particles/src/ParticleEditPacketSender.h @@ -16,16 +16,16 @@ /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class ParticleEditPacketSender : public OctreeEditPacketSender { + Q_OBJECT public: - ParticleEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { } - ~ParticleEditPacketSender() { } - /// Send particle add message immediately + /// NOTE: ParticleProperties assumes that all distances are in meter units void sendEditParticleMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties); /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known. + /// NOTE: ParticleProperties assumes that all distances are in meter units void queueParticleEditMessage(PACKET_TYPE type, ParticleID particleID, const ParticleProperties& properties); // My server type is the particle server diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a3c8072b3a..73e8dd6711 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -390,17 +390,29 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha // called by the server when it knows all nodes have been sent deleted packets void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) { + //qDebug() << "forgetParticlesDeletedBefore()"; + QSet keysToRemove; + _recentlyDeletedParticlesLock.lockForWrite(); - QMultiMap::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin(); - while (iterator != _recentlyDeletedParticleIDs.constEnd()) { + QMultiMap::iterator iterator = _recentlyDeletedParticleIDs.begin(); + // First find all the keys in the map that are older and need to be deleted + while (iterator != _recentlyDeletedParticleIDs.end()) { //qDebug() << "considering... time/key:" << iterator.key(); if (iterator.key() <= sinceTime) { //qDebug() << "YES older... time/key:" << iterator.key(); - _recentlyDeletedParticleIDs.remove(iterator.key()); + keysToRemove << iterator.key(); } ++iterator; } + + // Now run through the keysToRemove and remove them + foreach (uint64_t value, keysToRemove) { + //qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value; + _recentlyDeletedParticleIDs.remove(value); + } + _recentlyDeletedParticlesLock.unlock(); + //qDebug() << "DONE forgetParticlesDeletedBefore()"; } diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index edd2ac43dc..d61f8289f4 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -11,7 +11,6 @@ #include -#include #include #include "ParticleEditPacketSender.h" diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a1c1f602fb..f77b428764 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -250,7 +250,7 @@ void ScriptEngine::run() { int numAvatarPacketBytes = _avatarData->getBroadcastData(avatarPacket + numAvatarHeaderBytes) + numAvatarHeaderBytes; - nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, &NODE_TYPE_AVATAR_MIXER, 1); + nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, QSet() << NODE_TYPE_AVATAR_MIXER); } if (willSendVisualDataCallBack) { diff --git a/libraries/shared/src/GenericThread.cpp b/libraries/shared/src/GenericThread.cpp index a2dcf1004d..186366c606 100644 --- a/libraries/shared/src/GenericThread.cpp +++ b/libraries/shared/src/GenericThread.cpp @@ -20,7 +20,10 @@ GenericThread::GenericThread() : } GenericThread::~GenericThread() { - terminate(); + // we only need to call terminate() if we're actually threaded and still running + if (isStillRunning() && isThreaded()) { + terminate(); + } } void GenericThread::initialize(bool isThreaded) { @@ -45,6 +48,7 @@ void GenericThread::terminate() { if (_thread) { _thread->wait(); _thread->deleteLater(); + _thread = NULL; } } } diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 271629ff10..fa8bf0ef8f 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -74,8 +74,6 @@ const char* Node::getTypeName() const { return NODE_TYPE_NAME_AUDIO_MIXER; case NODE_TYPE_AVATAR_MIXER: return NODE_TYPE_NAME_AVATAR_MIXER; - case NODE_TYPE_AUDIO_INJECTOR: - return NODE_TYPE_NAME_AUDIO_INJECTOR; case NODE_TYPE_ANIMATION_SERVER: return NODE_TYPE_NAME_ANIMATION_SERVER; case NODE_TYPE_UNASSIGNED: diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index f37f2b48dc..3107d4e620 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -56,12 +56,12 @@ NodeList* NodeList::getInstance() { NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _nodeHash(), - _nodeHashMutex(), + _nodeHashMutex(QMutex::Recursive), _domainHostname(DEFAULT_DOMAIN_HOSTNAME), _domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), - _nodeSocket(), + _nodeSocket(this), _ownerType(newOwnerType), - _nodeTypesOfInterest(NULL), + _nodeTypesOfInterest(), _ownerUUID(QUuid::createUuid()), _numNoReplyDomainCheckIns(0), _assignmentServerSocket(), @@ -75,8 +75,6 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : NodeList::~NodeList() { - delete _nodeTypesOfInterest; - clear(); } @@ -235,8 +233,12 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, node->setLastHeardMicrostamp(usecTimestampNow()); - if (!senderSockAddr.isNull()) { - activateSocketFromNodeCommunication(senderSockAddr); + if (!senderSockAddr.isNull() && !node->getActiveSocket()) { + if (senderSockAddr == node->getPublicSocket()) { + node->activatePublicSocket(); + } else if (senderSockAddr == node->getLocalSocket()) { + node->activateLocalSocket(); + } } if (node->getActiveSocket() || senderSockAddr.isNull()) { @@ -257,7 +259,7 @@ int NodeList::updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, SharedNodePointer NodeList::nodeWithAddress(const HifiSockAddr &senderSockAddr) { // naively returns the first node that has a matching active HifiSockAddr // note that there can be multiple nodes that have a matching active socket, so this isn't a good way to uniquely identify - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (node->getActiveSocket() && *node->getActiveSocket() == senderSockAddr) { return node; } @@ -293,19 +295,18 @@ void NodeList::reset() { clear(); _numNoReplyDomainCheckIns = 0; - delete _nodeTypesOfInterest; - _nodeTypesOfInterest = NULL; + _nodeTypesOfInterest.clear(); // refresh the owner UUID _ownerUUID = QUuid::createUuid(); } -void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) { - delete _nodeTypesOfInterest; +void NodeList::addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd) { + _nodeTypesOfInterest << nodeTypeToAdd; +} - _nodeTypesOfInterest = new char[numNodeTypesOfInterest + sizeof(char)]; - memcpy(_nodeTypesOfInterest, nodeTypesOfInterest, numNodeTypesOfInterest); - _nodeTypesOfInterest[numNodeTypesOfInterest] = '\0'; +void NodeList::addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes) { + _nodeTypesOfInterest.unite(setOfNodeTypes); } const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; @@ -459,7 +460,7 @@ NodeHash::iterator NodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItem return _nodeHash.erase(nodeItemToKill); } -void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) { +void NodeList::sendKillNode(const QSet& destinationNodeTypes) { unsigned char packet[MAX_PACKET_SIZE]; unsigned char* packetPosition = packet; @@ -469,7 +470,7 @@ void NodeList::sendKillNode(const char* nodeTypes, int numNodeTypes) { memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); packetPosition += rfcUUID.size(); - broadcastToNodes(packet, packetPosition - packet, nodeTypes, numNodeTypes); + broadcastToNodes(packet, packetPosition - packet, destinationNodeTypes); } void NodeList::processKillNode(unsigned char* packetData, size_t dataBytes) { @@ -522,7 +523,7 @@ void NodeList::sendDomainServerCheckIn() { sendSTUNRequest(); } else { // construct the DS check in packet if we need to - int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0; + int numBytesNodesOfInterest = _nodeTypesOfInterest.size(); const int IP_ADDRESS_BYTES = 4; @@ -563,11 +564,8 @@ void NodeList::sendDomainServerCheckIn() { *(packetPosition++) = numBytesNodesOfInterest; // copy over the bytes for node types of interest, if required - if (numBytesNodesOfInterest > 0) { - memcpy(packetPosition, - _nodeTypesOfInterest, - numBytesNodesOfInterest); - packetPosition += numBytesNodesOfInterest; + foreach (NODE_TYPE nodeTypeOfInterest, _nodeTypesOfInterest) { + *(packetPosition++) = nodeTypeOfInterest; } _nodeSocket.writeDatagram((char*) checkInPacket, packetPosition - checkInPacket, @@ -735,12 +733,13 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, } } -unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { +unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const QSet& destinationNodeTypes) { unsigned n = 0; - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { // only send to the NodeTypes we are asked to send to. - if (memchr(nodeTypes, node->getType(), numNodeTypes)) { + if (destinationNodeTypes.contains(node->getType())) { if (getNodeActiveSocketOrPing(node.data())) { // we know which socket is good for this node, send there _nodeSocket.writeDatagram((char*) broadcastData, dataBytes, @@ -754,7 +753,7 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt } void NodeList::pingInactiveNodes() { - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (!node->getActiveSocket()) { // we don't have an active link to this node, ping it to set that up pingPublicAndLocalSocketsForInactiveNode(node.data()); @@ -791,7 +790,7 @@ void NodeList::activateSocketFromNodeCommunication(const HifiSockAddr& nodeAddre SharedNodePointer NodeList::soloNodeOfType(char nodeType) { if (memchr(SOLO_NODE_TYPES, nodeType, sizeof(SOLO_NODE_TYPES)) != NULL) { - foreach (const SharedNodePointer& node, _nodeHash) { + foreach (const SharedNodePointer& node, getNodeHash()) { if (node->getType() == nodeType) { return node; } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 9ac6c5970c..13f9485069 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -22,6 +22,7 @@ #endif #include +#include #include #include #include @@ -87,10 +88,11 @@ public: int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; } - void clear(); void reset(); - - void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); + + const QSet& getNodeInterestSet() const { return _nodeTypesOfInterest; } + void addNodeTypeToInterestSet(NODE_TYPE nodeTypeToAdd); + void addSetOfNodeTypesToNodeInterestSet(const QSet& setOfNodeTypes); int processDomainServerList(unsigned char *packetData, size_t dataBytes); @@ -103,7 +105,7 @@ public: int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer); void pingPublicAndLocalSocketsForInactiveNode(Node* node); - void sendKillNode(const char* nodeTypes, int numNodeTypes); + void sendKillNode(const QSet& destinationNodeTypes); SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr); SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); @@ -115,7 +117,7 @@ public: int updateNodeWithData(Node *node, const HifiSockAddr& senderSockAddr, unsigned char *packetData, int dataBytes); - unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes); + unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const QSet& destinationNodeTypes); SharedNodePointer soloNodeOfType(char nodeType); void loadData(QSettings* settings); @@ -151,7 +153,7 @@ private: HifiSockAddr _domainSockAddr; QUdpSocket _nodeSocket; char _ownerType; - char* _nodeTypesOfInterest; + QSet _nodeTypesOfInterest; QUuid _ownerUUID; int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; @@ -163,6 +165,7 @@ private: void timePingReply(const HifiSockAddr& nodeAddress, unsigned char *packetData); void resetDomainData(char domainField[], const char* domainData); void domainLookup(); + void clear(); }; #endif /* defined(__hifi__NodeList__) */ diff --git a/libraries/shared/src/NodeTypes.h b/libraries/shared/src/NodeTypes.h index 37e0503bab..6e8523c7d7 100644 --- a/libraries/shared/src/NodeTypes.h +++ b/libraries/shared/src/NodeTypes.h @@ -25,7 +25,6 @@ const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E'; const NODE_TYPE NODE_TYPE_AGENT = 'I'; const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M'; const NODE_TYPE NODE_TYPE_AVATAR_MIXER = 'W'; -const NODE_TYPE NODE_TYPE_AUDIO_INJECTOR = 'A'; const NODE_TYPE NODE_TYPE_ANIMATION_SERVER = 'a'; const NODE_TYPE NODE_TYPE_UNASSIGNED = 1; diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index ce720bf447..52274d9e9c 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -27,13 +27,12 @@ const int PacketSender::MINIMAL_SLEEP_INTERVAL = (USECS_PER_SECOND / TARGET_FPS) const int AVERAGE_CALL_TIME_SAMPLES = 10; -PacketSender::PacketSender(PacketSenderNotify* notify, int packetsPerSecond) : +PacketSender::PacketSender(int packetsPerSecond) : _packetsPerSecond(packetsPerSecond), _usecsPerProcessCallHint(0), _lastProcessCallTime(0), _averageProcessCallTime(AVERAGE_CALL_TIME_SAMPLES), _lastSendTime(0), // Note: we set this to 0 to indicate we haven't yet sent something - _notify(notify), _lastPPSCheck(0), _packetsOverCheckInterval(0), _started(usecTimestampNow()), @@ -271,10 +270,9 @@ bool PacketSender::nonThreadedProcess() { _packetsOverCheckInterval++; _totalPacketsSent++; _totalBytesSent += temporary.getLength(); - - if (_notify) { - _notify->packetSentNotification(temporary.getLength()); - } + + emit packetSent(temporary.getLength()); + _lastSendTime = now; } return isStillRunning(); diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 9005a2b254..4d87a62e5a 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -15,15 +15,9 @@ #include "NetworkPacket.h" #include "SharedUtil.h" -/// Notification Hook for packets being sent by a PacketSender -class PacketSenderNotify { -public: - virtual void packetSentNotification(ssize_t length) = 0; -}; - - /// Generalized threaded processor for queueing and sending of outbound packets. -class PacketSender : public virtual GenericThread { +class PacketSender : public GenericThread { + Q_OBJECT public: static const uint64_t USECS_PER_SECOND; @@ -35,7 +29,7 @@ public: static const int MINIMUM_PACKETS_PER_SECOND; static const int MINIMAL_SLEEP_INTERVAL; - PacketSender(PacketSenderNotify* notify = NULL, int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND); + PacketSender(int packetsPerSecond = DEFAULT_PACKETS_PER_SECOND); ~PacketSender(); /// Add packet to outbound queue. @@ -48,9 +42,6 @@ public: void setPacketsPerSecond(int packetsPerSecond); int getPacketsPerSecond() const { return _packetsPerSecond; } - void setPacketSenderNotify(PacketSenderNotify* notify) { _notify = notify; } - PacketSenderNotify* getPacketSenderNotify() const { return _notify; } - virtual bool process(); /// are there packets waiting in the send queue to be sent @@ -97,7 +88,8 @@ public: /// returns the total bytes queued by this object over its lifetime uint64_t getLifetimeBytesQueued() const { return _totalBytesQueued; } - +signals: + void packetSent(quint64); protected: int _packetsPerSecond; int _usecsPerProcessCallHint; @@ -107,7 +99,6 @@ protected: private: std::vector _packets; uint64_t _lastSendTime; - PacketSenderNotify* _notify; bool threadedProcess(); bool nonThreadedProcess(); diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h index 621204025e..567434a49e 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.h +++ b/libraries/shared/src/ReceivedPacketProcessor.h @@ -15,7 +15,7 @@ #include "NetworkPacket.h" /// Generalized threaded processor for handling received inbound packets. -class ReceivedPacketProcessor : public virtual GenericThread { +class ReceivedPacketProcessor : public GenericThread { public: ReceivedPacketProcessor(); diff --git a/libraries/voxel-server/src/VoxelServer.cpp b/libraries/voxel-server/src/VoxelServer.cpp index a1f5eaf55e..c8aee246e3 100644 --- a/libraries/voxel-server/src/VoxelServer.cpp +++ b/libraries/voxel-server/src/VoxelServer.cpp @@ -68,4 +68,6 @@ void VoxelServer::beforeRun() { qDebug("Using Minimal Environment=%s", debug::valueOf(_sendMinimalEnvironment)); } qDebug("Sending environments=%s", debug::valueOf(_sendEnvironments)); + + NodeList::getInstance()->addNodeTypeToInterestSet(NODE_TYPE_ANIMATION_SERVER); } diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index d0ce018d95..ec9b74dff8 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -15,10 +15,8 @@ /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class VoxelEditPacketSender : public OctreeEditPacketSender { + Q_OBJECT public: - VoxelEditPacketSender(PacketSenderNotify* notify = NULL) : OctreeEditPacketSender(notify) { } - ~VoxelEditPacketSender() { } - /// Send voxel edit message immediately void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail); diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index 6bbe21d601..97bdfb2c59 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -11,7 +11,6 @@ #include -#include #include #include "VoxelConstants.h" @@ -20,7 +19,7 @@ /// handles scripting of voxel commands from JS passed to assigned clients class VoxelsScriptingInterface : public OctreeScriptingInterface { Q_OBJECT -public: +public: VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); } virtual NODE_TYPE getServerNodeType() const { return NODE_TYPE_VOXEL_SERVER; }