diff --git a/CMakeLists.txt b/CMakeLists.txt index 279f6a6776..7964413d2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,6 @@ add_subdirectory(animation-server) add_subdirectory(assignment-client) add_subdirectory(domain-server) add_subdirectory(interface) -add_subdirectory(injector) add_subdirectory(pairing-server) add_subdirectory(space-server) add_subdirectory(voxel-edit) \ No newline at end of file diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 8e8835de45..c85eb5e4c1 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -140,8 +140,6 @@ void Agent::run() { int thisFrame = 0; - bool firstDomainCheckIn = false; - while (!_shouldStop) { // if we're not hearing from the domain-server we should stop running @@ -155,24 +153,34 @@ void Agent::run() { NodeList::getInstance()->sendDomainServerCheckIn(); } - if (firstDomainCheckIn) { - // find the audio-mixer in the NodeList so we can inject audio at it - Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - + // find the audio-mixer in the NodeList so we can inject audio at it + Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); + + if (audioMixer && audioMixer->getActiveSocket()) { emit willSendAudioDataCallback(); - if (audioMixer && scriptedAudioInjector.hasSamplesToInject()) { + if (scriptedAudioInjector.hasSamplesToInject()) { int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } - scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getPublicSocket()); + scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket()); // clear out the audio injector so that it doesn't re-send what we just sent scriptedAudioInjector.clear(); } + } else if (audioMixer) { + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); + if (usecToSleep > 0) { + usleep(usecToSleep); + } + // don't have an active socket for the audio-mixer, ping it now + NodeList::getInstance()->pingPublicAndLocalSocketsForInactiveNode(audioMixer); + } + + if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) { // allow the scripter's call back to setup visual data emit willSendVisualDataCallback(); @@ -182,18 +190,15 @@ void Agent::run() { // since we're in non-threaded mode, call process so that the packets are sent voxelScripter.getVoxelPacketSender()->process(); - } + } if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; } - while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { - if (!firstDomainCheckIn && receivedData[0] == PACKET_TYPE_DOMAIN) { - firstDomainCheckIn = true; - } - + while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) + && packetVersionMatch(receivedData)) { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } } diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2e54988eec..1b44c2e905 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "AudioRingBuffer.h" @@ -129,7 +130,7 @@ void AudioMixer::run() { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(_uuid.toRfc4122().constData()); + NodeList::getInstance()->sendDomainServerCheckIn(); if (Logging::shouldSendStats() && numStatCollections > 0) { // if we should be sending stats to Logstash send the appropriate average now @@ -168,167 +169,166 @@ void AudioMixer::run() { && (otherNode != node || (otherNode == node && nodeRingBuffer->shouldLoopbackForNode()))) { PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) otherNode->getLinkedData(); // based on our listen mode we will do this mixing... - if (nodeRingBuffer->isListeningToNode(*otherNode)) { - float bearingRelativeAngleToSource = 0.0f; - float attenuationCoefficient = 1.0f; - int numSamplesDelay = 0; - float weakChannelAmplitudeRatio = 1.0f; + + float bearingRelativeAngleToSource = 0.0f; + float attenuationCoefficient = 1.0f; + int numSamplesDelay = 0; + float weakChannelAmplitudeRatio = 1.0f; + + stk::TwoPole* otherNodeTwoPole = NULL; + + if (otherNode != node) { - stk::TwoPole* otherNodeTwoPole = NULL; + glm::vec3 listenerPosition = nodeRingBuffer->getPosition(); + glm::vec3 relativePosition = otherNodeBuffer->getPosition() - nodeRingBuffer->getPosition(); + glm::quat inverseOrientation = glm::inverse(nodeRingBuffer->getOrientation()); - // only do axis/distance attenuation when in normal mode - if (otherNode != node && nodeRingBuffer->getListeningMode() == AudioRingBuffer::NORMAL) { - - glm::vec3 listenerPosition = nodeRingBuffer->getPosition(); - glm::vec3 relativePosition = otherNodeBuffer->getPosition() - nodeRingBuffer->getPosition(); - glm::quat inverseOrientation = glm::inverse(nodeRingBuffer->getOrientation()); - - float distanceSquareToSource = glm::dot(relativePosition, relativePosition); - float radius = 0.0f; - - if (otherNode->getType() == NODE_TYPE_AUDIO_INJECTOR) { - InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) otherNodeBuffer; - radius = injectedBuffer->getRadius(); - attenuationCoefficient *= injectedBuffer->getAttenuationRatio(); - } - - if (radius == 0 || (distanceSquareToSource > radius * radius)) { - // this is either not a spherical source, or the listener is outside the sphere - - if (radius > 0) { - // this is a spherical source - the distance used for the coefficient - // needs to be the closest point on the boundary to the source - - // ovveride the distance to the node with the distance to the point on the - // boundary of the sphere - distanceSquareToSource -= (radius * radius); - - } else { - // calculate the angle delivery for off-axis attenuation - glm::vec3 rotatedListenerPosition = glm::inverse(otherNodeBuffer->getOrientation()) - * relativePosition; - - float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), - glm::normalize(rotatedListenerPosition)); - - const float MAX_OFF_AXIS_ATTENUATION = 0.2f; - const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; - - float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + - (OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f)); - - // multiply the current attenuation coefficient by the calculated off axis coefficient - attenuationCoefficient *= offAxisCoefficient; - } - - glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; - - const float DISTANCE_SCALE = 2.5f; - const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f; - const float DISTANCE_LOG_BASE = 2.5f; - const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE); - - // calculate the distance coefficient using the distance to this node - float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR, - DISTANCE_SCALE_LOG + - (0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1); - distanceCoefficient = std::min(1.0f, distanceCoefficient); - - // multiply the current attenuation coefficient by the distance coefficient - attenuationCoefficient *= distanceCoefficient; - - // project the rotated source position vector onto the XZ plane - rotatedSourcePosition.y = 0.0f; - - // produce an oriented angle about the y-axis - bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), - glm::normalize(rotatedSourcePosition), - glm::vec3(0.0f, 1.0f, 0.0f)); - - const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5; - - // figure out the number of samples of delay and the ratio of the amplitude - // in the weak channel for audio spatialization - float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource))); - numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; - weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); - - // grab the TwoPole object for this source, add it if it doesn't exist - TwoPoleNodeMap& nodeTwoPoles = nodeRingBuffer->getTwoPoles(); - TwoPoleNodeMap::iterator twoPoleIterator = nodeTwoPoles.find(otherNode->getNodeID()); - - if (twoPoleIterator == nodeTwoPoles.end()) { - // setup the freeVerb effect for this source for this client - otherNodeTwoPole = nodeTwoPoles[otherNode->getNodeID()] = new stk::TwoPole; - } else { - otherNodeTwoPole = twoPoleIterator->second; - } - - // calculate the reasonance for this TwoPole based on angle to source - float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f; - float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f; - - otherNodeTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY, - TWO_POLE_MAX_FILTER_STRENGTH - * fabsf(bearingRelativeAngleToSource) / 180.0f, - true); - } + float distanceSquareToSource = glm::dot(relativePosition, relativePosition); + float radius = 0.0f; + + if (otherNode->getType() == NODE_TYPE_AUDIO_INJECTOR) { + InjectedAudioRingBuffer* injectedBuffer = (InjectedAudioRingBuffer*) otherNodeBuffer; + radius = injectedBuffer->getRadius(); + attenuationCoefficient *= injectedBuffer->getAttenuationRatio(); } - int16_t* sourceBuffer = otherNodeBuffer->getNextOutput(); - - int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f) - ? clientSamples - : clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - int16_t* delayedChannel = (bearingRelativeAngleToSource > 0.0f) - ? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL - : clientSamples; - - int16_t* delaySamplePointer = otherNodeBuffer->getNextOutput() == otherNodeBuffer->getBuffer() - ? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay - : otherNodeBuffer->getNextOutput() - numSamplesDelay; - - for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { - // load up the stkFrameBuffer with this source's samples - stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s]; - } - - // perform the TwoPole effect on the stkFrameBuffer - if (otherNodeTwoPole) { - otherNodeTwoPole->tick(stkFrameBuffer); - } - - for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { - if (s < numSamplesDelay) { - // pull the earlier sample for the delayed channel - int earlierSample = delaySamplePointer[s] * attenuationCoefficient * weakChannelAmplitudeRatio; + if (radius == 0 || (distanceSquareToSource > radius * radius)) { + // this is either not a spherical source, or the listener is outside the sphere + + if (radius > 0) { + // this is a spherical source - the distance used for the coefficient + // needs to be the closest point on the boundary to the source - delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample, - MIN_SAMPLE_VALUE, - MAX_SAMPLE_VALUE); + // ovveride the distance to the node with the distance to the point on the + // boundary of the sphere + distanceSquareToSource -= (radius * radius); + + } else { + // calculate the angle delivery for off-axis attenuation + glm::vec3 rotatedListenerPosition = glm::inverse(otherNodeBuffer->getOrientation()) + * relativePosition; + + float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), + glm::normalize(rotatedListenerPosition)); + + const float MAX_OFF_AXIS_ATTENUATION = 0.2f; + const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f; + + float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + + (OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f)); + + // multiply the current attenuation coefficient by the calculated off axis coefficient + attenuationCoefficient *= offAxisCoefficient; } - int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient; + glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; - goodChannel[s] = glm::clamp(goodChannel[s] + currentSample, - MIN_SAMPLE_VALUE, - MAX_SAMPLE_VALUE); + const float DISTANCE_SCALE = 2.5f; + const float GEOMETRIC_AMPLITUDE_SCALAR = 0.3f; + const float DISTANCE_LOG_BASE = 2.5f; + const float DISTANCE_SCALE_LOG = logf(DISTANCE_SCALE) / logf(DISTANCE_LOG_BASE); - if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - int sumSample = delayedChannel[s + numSamplesDelay] - + (currentSample * weakChannelAmplitudeRatio); - delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample, - MIN_SAMPLE_VALUE, - MAX_SAMPLE_VALUE); + // calculate the distance coefficient using the distance to this node + float distanceCoefficient = powf(GEOMETRIC_AMPLITUDE_SCALAR, + DISTANCE_SCALE_LOG + + (0.5f * logf(distanceSquareToSource) / logf(DISTANCE_LOG_BASE)) - 1); + distanceCoefficient = std::min(1.0f, distanceCoefficient); + + // multiply the current attenuation coefficient by the distance coefficient + attenuationCoefficient *= distanceCoefficient; + + // project the rotated source position vector onto the XZ plane + rotatedSourcePosition.y = 0.0f; + + // produce an oriented angle about the y-axis + bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), + glm::normalize(rotatedSourcePosition), + glm::vec3(0.0f, 1.0f, 0.0f)); + + const float PHASE_AMPLITUDE_RATIO_AT_90 = 0.5; + + // figure out the number of samples of delay and the ratio of the amplitude + // in the weak channel for audio spatialization + float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource))); + numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio; + weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio); + + // grab the TwoPole object for this source, add it if it doesn't exist + TwoPoleNodeMap& nodeTwoPoles = nodeRingBuffer->getTwoPoles(); + TwoPoleNodeMap::iterator twoPoleIterator = nodeTwoPoles.find(otherNode->getUUID()); + + if (twoPoleIterator == nodeTwoPoles.end()) { + // setup the freeVerb effect for this source for this client + otherNodeTwoPole = nodeTwoPoles[otherNode->getUUID()] = new stk::TwoPole; + } else { + otherNodeTwoPole = twoPoleIterator->second; } - if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) { - // this could be a delayed sample on the next pass - // so store the affected back in the ARB - otherNodeBuffer->getNextOutput()[s] = (int16_t) stkFrameBuffer[s]; - } + // calculate the reasonance for this TwoPole based on angle to source + float TWO_POLE_CUT_OFF_FREQUENCY = 800.0f; + float TWO_POLE_MAX_FILTER_STRENGTH = 0.4f; + + otherNodeTwoPole->setResonance(TWO_POLE_CUT_OFF_FREQUENCY, + TWO_POLE_MAX_FILTER_STRENGTH + * fabsf(bearingRelativeAngleToSource) / 180.0f, + true); } } + + int16_t* sourceBuffer = otherNodeBuffer->getNextOutput(); + + int16_t* goodChannel = (bearingRelativeAngleToSource > 0.0f) + ? clientSamples + : clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + int16_t* delayedChannel = (bearingRelativeAngleToSource > 0.0f) + ? clientSamples + BUFFER_LENGTH_SAMPLES_PER_CHANNEL + : clientSamples; + + int16_t* delaySamplePointer = otherNodeBuffer->getNextOutput() == otherNodeBuffer->getBuffer() + ? otherNodeBuffer->getBuffer() + RING_BUFFER_LENGTH_SAMPLES - numSamplesDelay + : otherNodeBuffer->getNextOutput() - numSamplesDelay; + + for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { + // load up the stkFrameBuffer with this source's samples + stkFrameBuffer[s] = (stk::StkFloat) sourceBuffer[s]; + } + + // perform the TwoPole effect on the stkFrameBuffer + if (otherNodeTwoPole) { + otherNodeTwoPole->tick(stkFrameBuffer); + } + + for (int s = 0; s < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; s++) { + if (s < numSamplesDelay) { + // pull the earlier sample for the delayed channel + int earlierSample = delaySamplePointer[s] * attenuationCoefficient * weakChannelAmplitudeRatio; + + delayedChannel[s] = glm::clamp(delayedChannel[s] + earlierSample, + MIN_SAMPLE_VALUE, + MAX_SAMPLE_VALUE); + } + + int16_t currentSample = stkFrameBuffer[s] * attenuationCoefficient; + + goodChannel[s] = glm::clamp(goodChannel[s] + currentSample, + MIN_SAMPLE_VALUE, + MAX_SAMPLE_VALUE); + + if (s + numSamplesDelay < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + int sumSample = delayedChannel[s + numSamplesDelay] + + (currentSample * weakChannelAmplitudeRatio); + delayedChannel[s + numSamplesDelay] = glm::clamp(sumSample, + MIN_SAMPLE_VALUE, + MAX_SAMPLE_VALUE); + } + + if (s >= BUFFER_LENGTH_SAMPLES_PER_CHANNEL - PHASE_DELAY_AT_90) { + // this could be a delayed sample on the next pass + // so store the affected back in the ARB + otherNodeBuffer->getNextOutput()[s] = (int16_t) stkFrameBuffer[s]; + } + } + } } @@ -357,13 +357,17 @@ void AudioMixer::run() { packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) { unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData); - uint16_t sourceID; - memcpy(&sourceID, currentBuffer, sizeof(sourceID)); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) currentBuffer, NUM_BYTES_RFC4122_UUID)); - Node* avatarNode = nodeList->addOrUpdateNode(nodeAddress, - nodeAddress, + Node* avatarNode = nodeList->addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, - sourceID); + nodeAddress, + nodeAddress); + + // temp activation of public socket before server ping/reply is setup + if (!avatarNode->getActiveSocket()) { + avatarNode->activatePublicSocket(); + } nodeList->updateNodeWithData(nodeAddress, packetData, receivedBytes); @@ -372,36 +376,18 @@ void AudioMixer::run() { avatarNode->setAlive(false); } } else if (packetData[0] == PACKET_TYPE_INJECT_AUDIO) { - Node* matchingInjector = NULL; + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), + NUM_BYTES_RFC4122_UUID)); - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData()) { - - InjectedAudioRingBuffer* ringBuffer = (InjectedAudioRingBuffer*) node->getLinkedData(); - if (memcmp(ringBuffer->getStreamIdentifier(), - packetData + numBytesForPacketHeader(packetData), - STREAM_IDENTIFIER_NUM_BYTES) == 0) { - // this is the matching stream, assign to matchingInjector and stop looking - matchingInjector = &*node; - break; - } - } - } - - if (!matchingInjector) { - matchingInjector = nodeList->addOrUpdateNode(NULL, - NULL, - NODE_TYPE_AUDIO_INJECTOR, - nodeList->getLastNodeID()); - nodeList->increaseNodeID(); - - } + Node* matchingInjector = nodeList->addOrUpdateNode(nodeUUID, + NODE_TYPE_AUDIO_INJECTOR, + NULL, + NULL); // give the new audio data to the matching injector node nodeList->updateNodeWithData(matchingInjector, packetData, receivedBytes); - } else if (packetData[0] == PACKET_TYPE_PING || packetData[0] == PACKET_TYPE_DOMAIN) { - - // If the packet is a ping, let processNodeData handle it. + } else { + // let processNodeData handle it. nodeList->processNodeData(nodeAddress, packetData, receivedBytes); } } diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.h b/assignment-client/src/audio/AvatarAudioRingBuffer.h index cfb6d7717c..a3e39073de 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.h +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.h @@ -12,9 +12,11 @@ #include #include +#include + #include "PositionalAudioRingBuffer.h" -typedef std::map TwoPoleNodeMap; +typedef std::map TwoPoleNodeMap; class AvatarAudioRingBuffer : public PositionalAudioRingBuffer { public: diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 5c7e47a42a..b515f1400c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "AvatarData.h" @@ -22,7 +23,9 @@ const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer"; unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) { - currentPosition += packNodeId(currentPosition, nodeToAdd->getNodeID()); + QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122(); + memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); + currentPosition += rfcUUID.size(); AvatarData *nodeData = (AvatarData *)nodeToAdd->getLinkedData(); currentPosition += nodeData->getBroadcastData(currentPosition); @@ -43,7 +46,7 @@ void attachAvatarDataToNode(Node* newNode) { // 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to // determine which avatars are included in the packet stream // 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful). -void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { +void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr* receiverAddress) { static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE]; static unsigned char avatarDataBuffer[MAX_PACKET_SIZE]; unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0]; @@ -54,7 +57,7 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { // send back a packet with other active node data to this node for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getLinkedData() && !socketMatch(nodeAddress, node->getActiveSocket())) { + if (node->getLinkedData() && node->getUUID() != receiverUUID) { unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node); int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer; @@ -65,7 +68,7 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { } else { packetsSent++; //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); + nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket); // reset the packet currentBufferPosition = broadcastPacket + numHeaderBytes; @@ -80,7 +83,7 @@ void broadcastAvatarData(NodeList* nodeList, sockaddr* nodeAddress) { } packetsSent++; //printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength); - nodeList->getNodeSocket()->send(nodeAddress, broadcastPacket, currentBufferPosition - broadcastPacket); + nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket); } AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) { @@ -103,7 +106,7 @@ void AvatarMixer::run() { unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; - uint16_t nodeID = 0; + QUuid nodeUUID; Node* avatarNode = NULL; timeval lastDomainServerCheckIn = {}; @@ -117,32 +120,31 @@ void AvatarMixer::run() { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(_uuid.toRfc4122().constData()); + NodeList::getInstance()->sendDomainServerCheckIn(); } if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { switch (packetData[0]) { case PACKET_TYPE_HEAD_DATA: - // grab the node ID from the packet - unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); + nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), + NUM_BYTES_RFC4122_UUID)); // add or update the node in our list - avatarNode = nodeList->addOrUpdateNode(&nodeAddress, &nodeAddress, NODE_TYPE_AGENT, nodeID); + avatarNode = nodeList->addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, &nodeAddress, &nodeAddress); // parse positional data from an node nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes); case PACKET_TYPE_INJECT_AUDIO: - broadcastAvatarData(nodeList, &nodeAddress); + broadcastAvatarData(nodeList, nodeUUID, &nodeAddress); break; case PACKET_TYPE_AVATAR_URLS: case PACKET_TYPE_AVATAR_FACE_VIDEO: - // grab the node ID from the packet - unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); - + nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), + NUM_BYTES_RFC4122_UUID)); // let everyone else know about the update for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - if (node->getActiveSocket() && node->getNodeID() != nodeID) { + if (node->getActiveSocket() && node->getUUID() != nodeUUID) { nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes); } } diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 03578c4e8a..7597ea47f5 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -91,6 +91,8 @@ void childClient() { nodeList->setDomainIP(QHostAddress((sockaddr*) &senderSocket)); nodeList->setDomainPort(ntohs(senderSocket.sin_port)); + nodeList->setOwnerUUID(deployedAssignment->getUUID()); + qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str()); // run the deployed assignment diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 270608d07c..39c68d550f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -30,6 +30,18 @@ void DomainServer::setDomainServerInstance(DomainServer* domainServer) { domainServerInstance = domainServer; } +QJsonObject jsonForSocket(sockaddr* socket) { + QJsonObject socketJSON; + + if (socket->sa_family == AF_INET) { + sockaddr_in* socketIPv4 = (sockaddr_in*) socket; + socketJSON["ip"] = QString(inet_ntoa(socketIPv4->sin_addr)); + socketJSON["port"] = (int) ntohs(socketIPv4->sin_port); + } + + return socketJSON; +} + int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { const struct mg_request_info* ri = mg_get_request_info(connection); @@ -67,14 +79,9 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID()); assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID; - QJsonObject nodePublicSocketJSON; - - // add the public socket information - sockaddr_in* nodePublicSocket = (sockaddr_in*) node->getPublicSocket(); - nodePublicSocketJSON["ip"] = QString(inet_ntoa(nodePublicSocket->sin_addr)); - nodePublicSocketJSON["port"] = (int) ntohs(nodePublicSocket->sin_port); - - assignedNodeJSON["public"] = nodePublicSocketJSON; + // add the node socket information + assignedNodeJSON["public"] = jsonForSocket(node->getPublicSocket()); + assignedNodeJSON["local"] = jsonForSocket(node->getLocalSocket()); // re-format the type name so it matches the target name QString nodeTypeName(node->getTypeName()); @@ -154,7 +161,7 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const } void DomainServer::nodeAdded(Node* node) { - NodeList::getInstance()->increaseNodeID(); + } void DomainServer::nodeKilled(Node* node) { @@ -187,7 +194,11 @@ void DomainServer::nodeKilled(Node* node) { unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd) { *currentPosition++ = nodeToAdd->getType(); - currentPosition += packNodeId(currentPosition, nodeToAdd->getNodeID()); + + QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122(); + memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size()); + currentPosition += rfcUUID.size(); + currentPosition += packSocket(currentPosition, nodeToAdd->getPublicSocket()); currentPosition += packSocket(currentPosition, nodeToAdd->getLocalSocket()); @@ -291,11 +302,8 @@ void DomainServer::prepopulateStaticAssignmentFile() { _staticAssignmentFile.close(); } -Assignment* DomainServer::matchingStaticAssignmentForCheckIn(NODE_TYPE nodeType, const uchar* checkInData) { +Assignment* DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType) { // pull the UUID passed with the check in - QUuid checkInUUID = QUuid::fromRfc4122(QByteArray((const char*) checkInData + numBytesForPacketHeader(checkInData) + - sizeof(NODE_TYPE), - NUM_BYTES_RFC4122_UUID)); if (_hasCompletedRestartHold) { _assignmentQueueMutex.lock(); @@ -395,19 +403,14 @@ void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) { bool DomainServer::checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, - const uchar* checkInData) { - // pull the UUID passed with the check in - QUuid checkInUUID = QUuid::fromRfc4122(QByteArray((const char*) checkInData + numBytesForPacketHeader(checkInData) + - sizeof(NODE_TYPE), - NUM_BYTES_RFC4122_UUID)); - + const QUuid& checkInUUID) { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() && socketMatch(node->getPublicSocket(), nodePublicSocket) && socketMatch(node->getLocalSocket(), nodeLocalSocket) - && ((Assignment*) node->getLinkedData())->getUUID() == checkInUUID) { + && node->getUUID() == checkInUUID) { // this is a matching existing node if the public socket, local socket, and UUID match return true; } @@ -480,11 +483,10 @@ int DomainServer::run() { unsigned char* currentBufferPos; unsigned char* startPointer; - sockaddr_in nodePublicAddress, nodeLocalAddress, replyDestinationSocket; + sockaddr_in senderAddress, nodePublicAddress, nodeLocalAddress; + nodePublicAddress.sin_family = AF_INET; nodeLocalAddress.sin_family = AF_INET; - in_addr_t serverLocalAddress = getLocalAddress(); - nodeList->startSilentNodeRemovalThread(); if (!_staticAssignmentFile.exists() || _voxelServerConfig) { @@ -507,7 +509,7 @@ int DomainServer::run() { gettimeofday(&startTime, NULL); while (true) { - while (nodeList->getNodeSocket()->receive((sockaddr *)&nodePublicAddress, packetData, &receivedBytes) && + while (nodeList->getNodeSocket()->receive((sockaddr *)&senderAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) { // this is an RFD or domain list request packet, and there is a version match @@ -515,20 +517,30 @@ int DomainServer::run() { int numBytesSenderHeader = numBytesForPacketHeader(packetData); nodeType = *(packetData + numBytesSenderHeader); - int numBytesSocket = unpackSocket(packetData + numBytesSenderHeader + sizeof(NODE_TYPE), - (sockaddr*) &nodeLocalAddress); - replyDestinationSocket = nodePublicAddress; + int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID)); + packetIndex += NUM_BYTES_RFC4122_UUID; - // check the node public address - // if it matches our local address - // or if it's the loopback address we're on the same box - if (nodePublicAddress.sin_addr.s_addr == serverLocalAddress || - nodePublicAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress); + packetIndex += numBytesPrivateSocket; + + if (nodePublicAddress.sin_addr.s_addr == 0) { + // this node wants to use us its STUN server + // so set the node public address to whatever we perceive the public address to be - nodePublicAddress.sin_addr.s_addr = 0; + nodePublicAddress = senderAddress; + + // if the sender is on our box then leave its public address to 0 so that + // other users attempt to reach it on the same address they have for the domain-server + if (senderAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + nodePublicAddress.sin_addr.s_addr = 0; + } } + int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress); + packetIndex += numBytesPublicSocket; + const char STATICALLY_ASSIGNED_NODES[3] = { NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, @@ -537,16 +549,16 @@ int DomainServer::run() { Assignment* matchingStaticAssignment = NULL; - if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL || - ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeType, packetData)) || - checkInWithUUIDMatchesExistingNode((sockaddr*) &nodePublicAddress, - (sockaddr*) &nodeLocalAddress, - packetData))) { - - Node* checkInNode = nodeList->addOrUpdateNode((sockaddr*) &nodePublicAddress, - (sockaddr*) &nodeLocalAddress, + if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL + || ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType)) + || checkInWithUUIDMatchesExistingNode((sockaddr*) &nodePublicAddress, + (sockaddr*) &nodeLocalAddress, + nodeUUID))) + { + Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID, nodeType, - nodeList->getLastNodeID()); + (sockaddr*) &nodePublicAddress, + (sockaddr*) &nodeLocalAddress); if (matchingStaticAssignment) { // this was a newly added node with a matching static assignment @@ -568,12 +580,7 @@ int DomainServer::run() { currentBufferPos = broadcastPacket + numHeaderBytes; startPointer = currentBufferPos; - int numBytesUUID = (nodeType == NODE_TYPE_AUDIO_MIXER || nodeType == NODE_TYPE_AVATAR_MIXER) - ? NUM_BYTES_RFC4122_UUID - : 0; - - unsigned char* nodeTypesOfInterest = packetData + numBytesSenderHeader + numBytesUUID + - sizeof(NODE_TYPE) + numBytesSocket + sizeof(unsigned char); + unsigned char* nodeTypesOfInterest = packetData + packetIndex + sizeof(unsigned char); int numInterestTypes = *(nodeTypesOfInterest - 1); if (numInterestTypes > 0) { @@ -595,11 +602,8 @@ int DomainServer::run() { uint64_t timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); - // add the node ID to the end of the pointer - currentBufferPos += packNodeId(currentBufferPos, checkInNode->getNodeID()); - // send the constructed list back to this node - nodeList->getNodeSocket()->send((sockaddr*)&replyDestinationSocket, + nodeList->getNodeSocket()->send((sockaddr*)&senderAddress, broadcastPacket, (currentBufferPos - startPointer) + numHeaderBytes); } @@ -623,7 +627,7 @@ int DomainServer::run() { int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT); int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes); - nodeList->getNodeSocket()->send((sockaddr*) &nodePublicAddress, + nodeList->getNodeSocket()->send((sockaddr*) &senderAddress, broadcastPacket, numHeaderBytes + numAssignmentBytes); } @@ -637,20 +641,11 @@ int DomainServer::run() { qDebug() << "Received a create assignment -" << *createAssignment << "\n"; - // check the node public address - // if it matches our local address - // or if it's the loopback address we're on the same box - if (nodePublicAddress.sin_addr.s_addr == serverLocalAddress || - nodePublicAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { - - nodePublicAddress.sin_addr.s_addr = 0; - } - // make sure we have a matching node with the UUID packed with the assignment // if the node has sent no types of interest, assume they want nothing but their own ID back for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() - && socketMatch((sockaddr*) &nodePublicAddress, node->getPublicSocket()) + && socketMatch((sockaddr*) &senderAddress, node->getPublicSocket()) && ((Assignment*) node->getLinkedData())->getUUID() == createAssignment->getUUID()) { // give the create assignment a new UUID diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 540ca67acd..205d9e8f6e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -42,10 +42,10 @@ private: static DomainServer* domainServerInstance; void prepopulateStaticAssignmentFile(); - Assignment* matchingStaticAssignmentForCheckIn(NODE_TYPE nodeType, const uchar* checkInUUID); + Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType); Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); void removeAssignmentFromQueue(Assignment* removableAssignment); - bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const uchar* checkInData); + bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI); void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime); void cleanup(); diff --git a/injector/CMakeLists.txt b/injector/CMakeLists.txt deleted file mode 100644 index ae9ed3d8ad..0000000000 --- a/injector/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -set(ROOT_DIR ..) -set(MACRO_DIR ${ROOT_DIR}/cmake/macros) - -# setup for find modules -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") - -set(TARGET_NAME injector) - -include(${MACRO_DIR}/SetupHifiProject.cmake) -setup_hifi_project(${TARGET_NAME} TRUE) - -# set up the external glm library -include(${MACRO_DIR}/IncludeGLM.cmake) -include_glm(${TARGET_NAME} ${ROOT_DIR}) - -# link the shared hifi library -include(${MACRO_DIR}/LinkHifiLibrary.cmake) -link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) \ No newline at end of file diff --git a/injector/src/main.cpp b/injector/src/main.cpp deleted file mode 100644 index 9caad19e60..0000000000 --- a/injector/src/main.cpp +++ /dev/null @@ -1,244 +0,0 @@ -// -// main.cpp -// Audio Injector -// -// Created by Leonardo Murillo on 3/5/13. -// Copyright (c) 2013 Leonardo Murillo. All rights reserved. -// - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -const int AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS = 15; - -const int DEFAULT_INJECTOR_VOLUME = 0xFF; - -enum { - INJECTOR_POSITION_X, - INJECTOR_POSITION_Y, - INJECTOR_POSITION_Z, - INJECTOR_YAW -}; - -// Command line parameter defaults -bool shouldLoopAudio = true; -bool hasInjectedAudioOnce = false; -float sleepIntervalMin = 1.00; -float sleepIntervalMax = 2.00; -char *sourceAudioFile = NULL; -const char *allowedParameters = ":sc::a::f::t::r:l"; -float floatArguments[4] = {0.0f, 0.0f, 0.0f, 0.0f}; -unsigned char volume = DEFAULT_INJECTOR_VOLUME; -float triggerDistance = 0.0f; -float radius = 0.0f; -bool wantsLocalDomain = false; - - -void usage(void) { - std::cout << "High Fidelity - Interface audio injector" << std::endl; - std::cout << " -s Single play mode. If not specified will default to constant loop." << std::endl; - std::cout << " -c FLOAT,FLOAT,FLOAT,FLOAT X,Y,Z,YAW position in universe where audio will be originating from and direction. Defaults to 0,0,0,0" << std::endl; - std::cout << " -a 0-255 Attenuation curve modifier, defaults to 255" << std::endl; - std::cout << " -f FILENAME Name of audio source file. Required - RAW format, 22050hz 16bit signed mono" << std::endl; - std::cout << " -t FLOAT Trigger distance for injection. If not specified will loop constantly" << std::endl; - std::cout << " -r FLOAT Radius for spherical source. If not specified injected audio is point source" << std::endl; - std::cout << " -l Local domain mode." << std::endl; -} - -bool processParameters(int parameterCount, char* parameterData[]) { - int p; - while ((p = getopt(parameterCount, parameterData, allowedParameters)) != -1) { - switch (p) { - case 's': - ::shouldLoopAudio = false; - std::cout << "[DEBUG] Single play mode enabled" << std::endl; - break; - case 'f': - ::sourceAudioFile = optarg; - std::cout << "[DEBUG] Opening file: " << sourceAudioFile << std::endl; - break; - case 'c': - { - std::istringstream ss(optarg); - std::string token; - - int i = 0; - while (std::getline(ss, token, ',')) { - ::floatArguments[i] = atof(token.c_str()); - ++i; - if (i == 4) { - break; - } - } - - break; - } - case 'a': - ::volume = atoi(optarg); - std::cout << "[DEBUG] Attenuation modifier: " << optarg << std::endl; - break; - case 't': - ::triggerDistance = atof(optarg); - std::cout << "[DEBUG] Trigger distance: " << optarg << std::endl; - break; - case 'r': - ::radius = atof(optarg); - std::cout << "[DEBUG] Injector radius: " << optarg << std::endl; - break; - case 'l': - ::wantsLocalDomain = true; - break; - default: - usage(); - return false; - } - } - return true; -}; - -void createAvatarDataForNode(Node* node) { - if (!node->getLinkedData()) { - node->setLinkedData(new AvatarData(node)); - } -} - -int main(int argc, char* argv[]) { - - // new seed for random audio sleep times - srand(time(0)); - - int AUDIO_UDP_SEND_PORT = 1500 + (rand() % (int)(1500 - 2000 + 1)); - - if (processParameters(argc, argv)) { - if (::sourceAudioFile == NULL) { - std::cout << "[FATAL] Source audio file not specified" << std::endl; - exit(-1); - } else { - AudioInjector injector(sourceAudioFile); - - // create an NodeList instance to handle communication with other nodes - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AUDIO_INJECTOR, AUDIO_UDP_SEND_PORT); - - if (::wantsLocalDomain) { - printf("Local Domain MODE!\n"); - nodeList->setDomainIPToLocalhost(); - } - - // start the node list thread that will kill off nodes when they stop talking - nodeList->startSilentNodeRemovalThread(); - - injector.setPosition(glm::vec3(::floatArguments[INJECTOR_POSITION_X], - ::floatArguments[INJECTOR_POSITION_Y], - ::floatArguments[INJECTOR_POSITION_Z])); - injector.setOrientation(glm::quat(glm::vec3(0.0f, ::floatArguments[INJECTOR_YAW], 0.0f))); - injector.setVolume(::volume); - - if (::radius > 0) { - // if we were passed a cube side length, give that to the injector - injector.setRadius(::radius); - } - - // register the callback for node data creation - nodeList->linkedDataCreateCallback = createAvatarDataForNode; - - timeval lastSend = {}; - int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO); - unsigned char* broadcastPacket = new unsigned char[numBytesPacketHeader]; - - timeval lastDomainServerCheckIn = {}; - - sockaddr senderAddress; - ssize_t bytesReceived; - unsigned char incomingPacket[MAX_PACKET_SIZE]; - - // the audio injector needs to know about the avatar mixer and the audio mixer - const char INJECTOR_NODES_OF_INTEREST[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; - - int bytesNodesOfInterest = (::triggerDistance > 0) - ? sizeof(INJECTOR_NODES_OF_INTEREST) - : sizeof(INJECTOR_NODES_OF_INTEREST) - 1; - - NodeList::getInstance()->setNodeTypesOfInterest(INJECTOR_NODES_OF_INTEREST, bytesNodesOfInterest); - - while (true) { - // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed - if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { - gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(); - } - - while (nodeList->getNodeSocket()->receive(&senderAddress, incomingPacket, &bytesReceived) && - packetVersionMatch(incomingPacket)) { - switch (incomingPacket[0]) { - case PACKET_TYPE_BULK_AVATAR_DATA: // this is the positional data for other nodes - // pass that off to the nodeList processBulkNodeData method - nodeList->processBulkNodeData(&senderAddress, incomingPacket, bytesReceived); - break; - default: - // have the nodeList handle list of nodes from DS, replies from other nodes, etc. - nodeList->processNodeData(&senderAddress, incomingPacket, bytesReceived); - break; - } - } - - if (::triggerDistance) { - if (!injector.isInjectingAudio()) { - // enumerate the other nodes to decide if one is close enough that we should inject - for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - AvatarData* avatarData = (AvatarData*) node->getLinkedData(); - - if (avatarData) { - glm::vec3 tempVector = injector.getPosition() - avatarData->getPosition(); - - if (glm::dot(tempVector, tempVector) <= ::triggerDistance) { - // use the AudioInjectionManager to thread this injector - AudioInjectionManager::threadInjector(&injector); - } - } - } - } - - // find the current avatar mixer - Node* avatarMixer = nodeList->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); - - // make sure we actually have an avatar mixer with an active socket - if (avatarMixer && avatarMixer->getActiveSocket() != NULL - && (usecTimestampNow() - usecTimestamp(&lastSend) > AVATAR_MIXER_DATA_SEND_INTERVAL_MSECS)) { - - // update the lastSend timeval to the current time - gettimeofday(&lastSend, NULL); - - // use the UDPSocket instance attached to our node list to ask avatar mixer for a list of avatars - nodeList->getNodeSocket()->send(avatarMixer->getActiveSocket(), - broadcastPacket, - numBytesPacketHeader); - } - } else { - if (!injector.isInjectingAudio() && (::shouldLoopAudio || !::hasInjectedAudioOnce)) { - // use the AudioInjectionManager to thread this injector - AudioInjectionManager::threadInjector(&injector); - ::hasInjectedAudioOnce = true; - } - } - } - - // stop the node list's threads - nodeList->stopSilentNodeRemovalThread(); - } - } -} - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8113057210..a157bcdedf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -54,7 +54,7 @@ #include #include #include - +#include #include #include "Application.h" @@ -500,7 +500,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ if (nodeTypes[i] == 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); @@ -1160,8 +1160,9 @@ void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& d packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_AVATAR_FACE_VIDEO); - *(uint16_t*)packetPosition = NodeList::getInstance()->getOwnerID(); - packetPosition += sizeof(uint16_t); + QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); + memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); + packetPosition += rfcUUID.size(); *(uint32_t*)packetPosition = frameCount; packetPosition += sizeof(uint32_t); @@ -1295,12 +1296,13 @@ static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& da dataBytes -= numBytesPacketHeader; // read the node id - uint16_t nodeID = *(uint16_t*)packetData; - packetData += sizeof(nodeID); - dataBytes -= sizeof(nodeID); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData, NUM_BYTES_RFC4122_UUID)); + + packetData += NUM_BYTES_RFC4122_UUID; + dataBytes -= NUM_BYTES_RFC4122_UUID; // make sure the node exists - Node* node = NodeList::getInstance()->nodeWithID(nodeID); + Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); if (!node || !node->getLinkedData()) { return NULL; } @@ -1393,13 +1395,13 @@ void Application::increaseVoxelSize() { const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500; struct SendVoxelsOperationArgs { - unsigned char* newBaseOctCode; + const unsigned char* newBaseOctCode; }; bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData; if (node->isColored()) { - unsigned char* nodeOctalCode = node->getOctalCode(); + const unsigned char* nodeOctalCode = node->getOctalCode(); unsigned char* codeColorBuffer = NULL; int codeLength = 0; @@ -1561,29 +1563,6 @@ void Application::deleteVoxels() { deleteVoxelUnderCursor(); } -void Application::setListenModeNormal() { - _audio.setListenMode(AudioRingBuffer::NORMAL); -} - -void Application::setListenModePoint() { - _audio.setListenMode(AudioRingBuffer::OMNI_DIRECTIONAL_POINT); - _audio.setListenRadius(1.0); -} - -void Application::setListenModeSingleSource() { - _audio.setListenMode(AudioRingBuffer::SELECTED_SOURCES); - _audio.clearListenSources(); - - glm::vec3 mouseRayOrigin = _myAvatar.getMouseRayOrigin(); - glm::vec3 mouseRayDirection = _myAvatar.getMouseRayDirection(); - glm::vec3 eyePositionIgnored; - uint16_t nodeID; - - if (findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeID)) { - _audio.addListenSource(nodeID); - } -} - void Application::initDisplay() { glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); @@ -1690,7 +1669,7 @@ const float MAX_AVATAR_EDIT_VELOCITY = 1.0f; const float MAX_VOXEL_EDIT_DISTANCE = 20.0f; const float HEAD_SPHERE_RADIUS = 0.07; -static uint16_t DEFAULT_NODE_ID_REF = 1; +static QUuid DEFAULT_NODE_ID_REF; void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition) { @@ -1699,7 +1678,7 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons } Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, uint16_t& nodeID = DEFAULT_NODE_ID_REF) { + glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { @@ -1712,7 +1691,7 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con eyePosition = avatar->getHead().getEyePosition(); _lookatIndicatorScale = avatar->getHead().getScale(); _lookatOtherPosition = headPosition; - nodeID = avatar->getOwningNode()->getNodeID(); + nodeUUID = avatar->getOwningNode()->getUUID(); return avatar; } } @@ -1761,12 +1740,12 @@ void Application::renderFollowIndicator() { Avatar* avatar = (Avatar *) node->getLinkedData(); Avatar* leader = NULL; - if (avatar->getLeaderID() != UNKNOWN_NODE_ID) { - if (avatar->getLeaderID() == NodeList::getInstance()->getOwnerID()) { + if (!avatar->getLeaderUUID().isNull()) { + if (avatar->getLeaderUUID() == NodeList::getInstance()->getOwnerUUID()) { leader = &_myAvatar; } else { for (NodeList::iterator it = nodeList->begin(); it != nodeList->end(); ++it) { - if(it->getNodeID() == avatar->getLeaderID() + if(it->getUUID() == avatar->getLeaderUUID() && it->getType() == NODE_TYPE_AGENT) { leader = (Avatar*) it->getLinkedData(); } @@ -2246,26 +2225,27 @@ void Application::updateAvatar(float deltaTime) { _myAvatar.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); NodeList* nodeList = NodeList::getInstance(); - if (nodeList->getOwnerID() != UNKNOWN_NODE_ID) { - // if I know my ID, send head/hand data to the avatar mixer and voxel server - unsigned char broadcastString[MAX_PACKET_SIZE]; - unsigned char* endOfBroadcastStringWrite = broadcastString; - - endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA); - - endOfBroadcastStringWrite += packNodeId(endOfBroadcastStringWrite, nodeList->getOwnerID()); - - endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - - const char nodeTypesOfInterest[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AVATAR_MIXER }; - controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, - nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - - // once in a while, send my urls - const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds - if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) { - Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL()); - } + + // send head/hand data to the avatar mixer and voxel server + unsigned char broadcastString[MAX_PACKET_SIZE]; + unsigned char* endOfBroadcastStringWrite = broadcastString; + + endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA); + + QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); + memcpy(endOfBroadcastStringWrite, ownerUUID.constData(), ownerUUID.size()); + endOfBroadcastStringWrite += ownerUUID.size(); + + endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); + + const char nodeTypesOfInterest[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AVATAR_MIXER }; + controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, + nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); + + // once in a while, send my urls + const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds + if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) { + Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL()); } } @@ -2806,24 +2786,40 @@ void Application::displayOverlay() { if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse) && !Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && USING_INVENSENSE_MPU9150) { - // Display small target box at center or head mouse target that can also be used to measure LOD - glColor3f(1.0, 1.0, 1.0); + // Display small target box at center or head mouse target that can also be used to measure LOD + glColor3f(1.0, 1.0, 1.0); + glDisable(GL_LINE_SMOOTH); + const int PIXEL_BOX = 16; + glBegin(GL_LINES); + glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); + glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); + glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); + glEnd(); + glEnable(GL_LINE_SMOOTH); + glColor3f(1.f, 0.f, 0.f); + glPointSize(3.0f); + glDisable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + glVertex2f(_headMouseX - 1, _headMouseY + 1); + glEnd(); + // If Faceshift is active, show eye pitch and yaw as separate pointer + if (_faceshift.isActive()) { + const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; + int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; + int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; + + glColor3f(0.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); - const int PIXEL_BOX = 16; glBegin(GL_LINES); - glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); - glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); - glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); - glEnd(); - glEnable(GL_LINE_SMOOTH); - glColor3f(1.f, 0.f, 0.f); - glPointSize(3.0f); - glDisable(GL_POINT_SMOOTH); - glBegin(GL_POINTS); - glVertex2f(_headMouseX - 1, _headMouseY + 1); + glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX + PIXEL_BOX/2, eyeTargetY); + glVertex2f(eyeTargetX, eyeTargetY - PIXEL_BOX/2); + glVertex2f(eyeTargetX, eyeTargetY + PIXEL_BOX/2); glEnd(); + } + } // Show detected levels from the serial I/O ADC channel sensors if (_displayLevels) _serialHeadSensor.renderLevels(_glWidget->width(), _glWidget->height()); @@ -3559,8 +3555,8 @@ void Application::toggleFollowMode() { _pieMenu.getY() / (float)_glWidget->height(), mouseRayOrigin, mouseRayDirection); glm::vec3 eyePositionIgnored; - uint16_t nodeIDIgnored; - Avatar* leadingAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeIDIgnored); + QUuid nodeUUIDIgnored; + Avatar* leadingAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePositionIgnored, nodeUUIDIgnored); _myAvatar.follow(leadingAvatar); } @@ -3628,10 +3624,10 @@ void Application::nodeAdded(Node* node) { void Application::nodeKilled(Node* node) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { - uint16_t nodeID = node->getNodeID(); + QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... - if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) { - unsigned char* rootCode = _voxelServerJurisdictions[nodeID].getRootOctalCode(); + if (_voxelServerJurisdictions.find(nodeUUID) != _voxelServerJurisdictions.end()) { + unsigned char* rootCode = _voxelServerJurisdictions[nodeUUID].getRootOctalCode(); VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -3661,13 +3657,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng // quick fix for crash... why would voxelServer be NULL? if (voxelServer) { - uint16_t nodeID = voxelServer->getNodeID(); + QUuid nodeUUID = voxelServer->getUUID(); VoxelPositionSize rootDetails; voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), rootDetails); // see if this is the first we've heard of this node... - if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) { + if (_voxelServerJurisdictions.find(nodeUUID) == _voxelServerJurisdictions.end()) { printf("stats from new voxel server... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); @@ -3684,7 +3680,7 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng // details from the VoxelSceneStats to construct the JurisdictionMap JurisdictionMap jurisdictionMap; jurisdictionMap.copyContents(_voxelSceneStats.getJurisdictionRoot(), _voxelSceneStats.getJurisdictionEndNodes()); - _voxelServerJurisdictions[nodeID] = jurisdictionMap; + _voxelServerJurisdictions[nodeUUID] = jurisdictionMap; } return statsMessageLength; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 595f337bf5..0aae683844 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -171,9 +171,6 @@ public slots: void doKillLocalVoxels(); void decreaseVoxelSize(); void increaseVoxelSize(); - void setListenModeNormal(); - void setListenModePoint(); - void setListenModeSingleSource(); private slots: @@ -213,7 +210,7 @@ private: void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition); Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, uint16_t& nodeID); + glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index bea12af25e..0f3b047a3f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -84,93 +84,74 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o memset(outputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); memset(outputRight, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); + // If Mute button is pressed, clear the input buffer + if (_muted) { + memset(inputLeft, 0, PACKET_LENGTH_BYTES_PER_CHANNEL); + } + // Add Procedural effects to input samples addProceduralSounds(inputLeft, outputLeft, outputRight, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); if (nodeList && inputLeft) { - if (!_muted) { - // Measure the loudness of the signal from the microphone and store in audio object - float loudness = 0; - for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { - loudness += abs(inputLeft[i]); - } - - loudness /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL; - _lastInputLoudness = loudness; - - // add input (@microphone) data to the scope - _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + // Measure the loudness of the signal from the microphone and store in audio object + float loudness = 0; + for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { + loudness += abs(inputLeft[i]); } + + loudness /= BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + _lastInputLoudness = loudness; + + // add input (@microphone) data to the scope + _scope->addSamples(0, inputLeft, BUFFER_LENGTH_SAMPLES_PER_CHANNEL); Node* audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); if (audioMixer) { - audioMixer->lock(); - sockaddr_in audioSocket = *(sockaddr_in*) audioMixer->getActiveSocket(); - audioMixer->unlock(); - - glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition(); - glm::quat headOrientation = interfaceAvatar->getHead().getOrientation(); - - int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO); - int leadingBytes = numBytesPacketHeader + sizeof(headPosition) + sizeof(headOrientation); - - // we need the amount of bytes in the buffer + 1 for type - // + 12 for 3 floats for position + float for bearing + 1 attenuation byte - unsigned char dataPacket[MAX_PACKET_SIZE]; - - PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoAudio) - ? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO - : PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO; - - unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, packetType); - - // pack Source Data - uint16_t ownerID = NodeList::getInstance()->getOwnerID(); - memcpy(currentPacketPtr, &ownerID, sizeof(ownerID)); - currentPacketPtr += (sizeof(ownerID)); - leadingBytes += (sizeof(ownerID)); - - // pack Listen Mode Data - memcpy(currentPacketPtr, &_listenMode, sizeof(_listenMode)); - currentPacketPtr += (sizeof(_listenMode)); - leadingBytes += (sizeof(_listenMode)); - - if (_listenMode == AudioRingBuffer::OMNI_DIRECTIONAL_POINT) { - memcpy(currentPacketPtr, &_listenRadius, sizeof(_listenRadius)); - currentPacketPtr += (sizeof(_listenRadius)); - leadingBytes += (sizeof(_listenRadius)); - } else if (_listenMode == AudioRingBuffer::SELECTED_SOURCES) { - int listenSourceCount = _listenSources.size(); - memcpy(currentPacketPtr, &listenSourceCount, sizeof(listenSourceCount)); - currentPacketPtr += (sizeof(listenSourceCount)); - leadingBytes += (sizeof(listenSourceCount)); - for (int i = 0; i < listenSourceCount; i++) { - memcpy(currentPacketPtr, &_listenSources[i], sizeof(_listenSources[i])); - currentPacketPtr += sizeof(_listenSources[i]); - leadingBytes += sizeof(_listenSources[i]); - } + if (audioMixer->getActiveSocket()) { + glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition(); + glm::quat headOrientation = interfaceAvatar->getHead().getOrientation(); + + int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO); + int leadingBytes = numBytesPacketHeader + sizeof(headPosition) + sizeof(headOrientation); + + // we need the amount of bytes in the buffer + 1 for type + // + 12 for 3 floats for position + float for bearing + 1 attenuation byte + unsigned char dataPacket[MAX_PACKET_SIZE]; + + PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoAudio) + ? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO + : PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO; + + unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, packetType); + + // pack Source Data + QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); + memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size()); + currentPacketPtr += rfcUUID.size(); + leadingBytes += rfcUUID.size(); + + // memcpy the three float positions + memcpy(currentPacketPtr, &headPosition, sizeof(headPosition)); + currentPacketPtr += (sizeof(headPosition)); + + // memcpy our orientation + memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation)); + currentPacketPtr += sizeof(headOrientation); + + // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet + memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL); + + nodeList->getNodeSocket()->send(audioMixer->getActiveSocket(), + dataPacket, + BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); + + interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO).updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL + + leadingBytes); + } else { + nodeList->pingPublicAndLocalSocketsForInactiveNode(audioMixer); } - - // memcpy the three float positions - memcpy(currentPacketPtr, &headPosition, sizeof(headPosition)); - currentPacketPtr += (sizeof(headPosition)); - - // memcpy our orientation - memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation)); - currentPacketPtr += sizeof(headOrientation); - - // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet - memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL); - - nodeList->getNodeSocket()->send((sockaddr*) &audioSocket, - dataPacket, - BUFFER_LENGTH_BYTES_PER_CHANNEL + leadingBytes); - - interface->getBandwidthMeter()->outputStream(BandwidthMeter::AUDIO).updateValue(BUFFER_LENGTH_BYTES_PER_CHANNEL - + leadingBytes); - } } @@ -264,8 +245,8 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o if (flangeIndex < 0) { // we need to grab the flange sample from earlier in the buffer flangeFrame = ringBuffer->getNextOutput() != ringBuffer->getBuffer() - ? ringBuffer->getNextOutput() - PACKET_LENGTH_SAMPLES - : ringBuffer->getNextOutput() + RING_BUFFER_LENGTH_SAMPLES - PACKET_LENGTH_SAMPLES; + ? ringBuffer->getNextOutput() - PACKET_LENGTH_SAMPLES + : ringBuffer->getNextOutput() + RING_BUFFER_LENGTH_SAMPLES - PACKET_LENGTH_SAMPLES; flangeIndex = PACKET_LENGTH_SAMPLES_PER_CHANNEL + (s - sampleFlangeDelay); } @@ -348,24 +329,6 @@ void Audio::reset() { _ringBuffer.reset(); } -void Audio::addListenSource(int sourceID) { - _listenSources.push_back(sourceID); -} - -void Audio::clearListenSources() { - _listenSources.clear(); -} - -void Audio::removeListenSource(int sourceID) { - for (int i = 0; i < _listenSources.size(); i++) { - if (_listenSources[i] == sourceID) { - _listenSources.erase(_listenSources.begin() + i); - return; - } - } -} - - Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : _stream(NULL), _ringBuffer(true), @@ -396,9 +359,7 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : _collisionSoundDuration(0.0f), _proceduralEffectSample(0), _heartbeatMagnitude(0.0f), - _muted(false), - _listenMode(AudioRingBuffer::NORMAL), - _listenRadius(0.0f) + _muted(false) { outputPortAudioError(Pa_Initialize()); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 7c66c7e375..6dad16f366 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -68,13 +68,6 @@ public: // in which case 'true' is returned - otherwise the return value is 'false'. // The results of the analysis are written to the log. bool eventuallyAnalyzePing(); - - void setListenMode(AudioRingBuffer::ListenMode mode) { _listenMode = mode; } - void setListenRadius(float radius) { _listenRadius = radius; } - void addListenSource(int sourceID); - void removeListenSource(int sourceID); - void clearListenSources(); - private: PaStream* _stream; @@ -117,10 +110,6 @@ private: GLuint _muteTextureId; QRect _iconBounds; - AudioRingBuffer::ListenMode _listenMode; - float _listenRadius; - std::vector _listenSources; - // Audio callback in class context. inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 013f1d461b..7f2afe2fa1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -442,21 +442,6 @@ Menu::Menu() : QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio); - addActionToQMenuAndActionHash(audioDebugMenu, - MenuOption::ListenModeNormal, - Qt::CTRL | Qt::Key_1, - appInstance, - SLOT(setListenModeNormal())); - addActionToQMenuAndActionHash(audioDebugMenu, - MenuOption::ListenModePoint, - Qt::CTRL | Qt::Key_2, - appInstance, - SLOT(setListenModePoint())); - addActionToQMenuAndActionHash(audioDebugMenu, - MenuOption::ListenModeSingleSource, - Qt::CTRL | Qt::Key_3, - appInstance, - SLOT(setListenModeSingleSource())); QMenu* voxelProtoOptionsMenu = developerMenu->addMenu("Voxel Server Protocol Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 81d213c026..86e40ee928 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -176,9 +176,6 @@ namespace MenuOption { const QString Gravity = "Use Gravity"; const QString GroundPlane = "Ground Plane"; const QString ParticleCloud = "Particle Cloud"; - const QString ListenModeNormal = "Listen Mode Normal"; - const QString ListenModePoint = "Listen Mode Point"; - const QString ListenModeSingleSource = "Listen Mode Single Source"; const QString Log = "Log"; const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index e33f377edc..9e189aac64 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -47,13 +47,12 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { - int nodeID = voxelServer->getNodeID(); if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { app->_environment.parseData(&senderAddress, packetData, messageLength); } else { - app->_voxels.setDataSourceID(nodeID); + app->_voxels.setDataSourceUUID(voxelServer->getUUID()); app->_voxels.parseData(packetData, messageLength); - app->_voxels.setDataSourceID(UNKNOWN_NODE_ID); + app->_voxels.setDataSourceUUID(QUuid()); } } } diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 574615d113..bc2d353bf9 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -82,7 +82,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) VoxelNode::addUpdateHook(this); _abandonedVBOSlots = 0; _falseColorizeBySource = false; - _dataSourceID = UNKNOWN_NODE_ID; + _dataSourceUUID = QUuid(); _voxelServerCount = 0; _viewFrustum = Application::getInstance()->getViewFrustum(); @@ -576,7 +576,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the bitstream into the tree - ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); + ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); pthread_mutex_lock(&_treeLock); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); pthread_mutex_unlock(&_treeLock); @@ -586,7 +586,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the MONOCHROME bitstream into the tree - ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); + ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); pthread_mutex_lock(&_treeLock); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); pthread_mutex_unlock(&_treeLock); @@ -1417,8 +1417,8 @@ bool VoxelSystem::falseColorizeBySourceOperation(VoxelNode* node, void* extraDat _nodeCount++; if (node->isColored()) { // pick a color based on the source - we want each source to be obviously different - uint16_t nodeID = node->getSourceID(); - node->setFalseColor(args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); + uint16_t nodeIDKey = node->getSourceUUIDKey(); + node->setFalseColor(args->colors[nodeIDKey].red, args->colors[nodeIDKey].green, args->colors[nodeIDKey].blue); } return true; // keep going! } @@ -1442,7 +1442,7 @@ void VoxelSystem::falseColorizeBySource() { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { - uint16_t nodeID = node->getNodeID(); + uint16_t nodeID = VoxelNode::getSourceNodeUUIDKey(node->getUUID()); int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS; args.colors[nodeID] = groupColors[groupColor]; @@ -2299,19 +2299,17 @@ void VoxelSystem::falseColorizeOccludedV2() { void VoxelSystem::nodeAdded(Node* node) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { - uint16_t nodeID = node->getNodeID(); - qDebug("VoxelSystem... voxel server %u added...\n", nodeID); + qDebug("VoxelSystem... voxel server %s added...\n", node->getUUID().toString().toLocal8Bit().constData()); _voxelServerCount++; } } bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) { - uint16_t killedNodeID = *(uint16_t*)extraData; + QUuid killedNodeID = *(QUuid*)extraData; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { - uint16_t childNodeID = childNode->getSourceID(); - if (childNodeID == killedNodeID) { + if (childNode->matchesSourceUUID(killedNodeID)) { node->safeDeepDeleteChildAtIndex(i); } } @@ -2322,13 +2320,16 @@ bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) { void VoxelSystem::nodeKilled(Node* node) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { _voxelServerCount--; - uint16_t nodeID = node->getNodeID(); - qDebug("VoxelSystem... voxel server %u removed...\n", nodeID); + + QUuid nodeUUID = node->getUUID(); + + qDebug("VoxelSystem... voxel server %s removed...\n", nodeUUID.toString().toLocal8Bit().constData()); if (_voxelServerCount > 0) { // Kill any voxels from the local tree that match this nodeID + // commenting out for removal of 16 bit node IDs pthread_mutex_lock(&_treeLock); - _tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeID); + _tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeUUID); pthread_mutex_unlock(&_treeLock); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 1af6b19ca7..238b9f6d96 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -44,8 +44,8 @@ public: VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); - void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; } - int getDataSourceID() const { return _dataSourceID; } + void setDataSourceUUID(const QUuid& dataSourceUUID) { _dataSourceUUID = dataSourceUUID; } + const QUuid& getDataSourceUUID() const { return _dataSourceUUID; } int parseData(unsigned char* sourceBuffer, int numBytes); @@ -279,7 +279,7 @@ private: glBufferIndex getNextBufferIndex(); bool _falseColorizeBySource; - int _dataSourceID; + QUuid _dataSourceUUID; int _voxelServerCount; unsigned long _memoryUsageRAM; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f027df2c25..f704449aad 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -61,19 +61,13 @@ const float chatMessageScale = 0.0015; const float chatMessageHeight = 0.20; void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL) { - uint16_t ownerID = NodeList::getInstance()->getOwnerID(); - - if (ownerID == UNKNOWN_NODE_ID) { - return; // we don't yet know who we are - } - QByteArray message; char packetHeader[MAX_PACKET_HEADER_BYTES]; int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_URLS); message.append(packetHeader, numBytesPacketHeader); - message.append((const char*)&ownerID, sizeof(ownerID)); + message.append(NodeList::getInstance()->getOwnerUUID().toRfc4122()); QDataStream out(&message, QIODevice::WriteOnly | QIODevice::Append); out << voxelURL; @@ -284,13 +278,13 @@ void Avatar::follow(Avatar* leadingAvatar) { _leadingAvatar = leadingAvatar; if (_leadingAvatar != NULL) { - _leaderID = leadingAvatar->getOwningNode()->getNodeID(); + _leaderUUID = leadingAvatar->getOwningNode()->getUUID(); _stringLength = glm::length(_position - _leadingAvatar->getPosition()) / _scale; if (_stringLength > MAX_STRING_LENGTH) { _stringLength = MAX_STRING_LENGTH; } } else { - _leaderID = UNKNOWN_NODE_ID; + _leaderUUID = QUuid(); } } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 452037f436..b6be6cf1d3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -329,16 +329,7 @@ void Head::setScale (float scale) { void Head::createMohawk() { - uint16_t nodeId = UNKNOWN_NODE_ID; - if (_owningAvatar->getOwningNode()) { - nodeId = _owningAvatar->getOwningNode()->getNodeID(); - } else { - nodeId = NodeList::getInstance()->getOwnerID(); - if (nodeId == UNKNOWN_NODE_ID) { - return; - } - } - srand(nodeId); + srand(time(NULL)); float height = _scale * (0.08f + randFloat() * 0.05f); float variance = 0.03 + randFloat() * 0.03f; const float RAD_PER_TRIANGLE = (2.3f + randFloat() * 0.2f) / (float)MOHAWK_TRIANGLES; diff --git a/libraries/audio/src/AudioInjectionManager.cpp b/libraries/audio/src/AudioInjectionManager.cpp index 79de62e747..afa99b0413 100644 --- a/libraries/audio/src/AudioInjectionManager.cpp +++ b/libraries/audio/src/AudioInjectionManager.cpp @@ -59,7 +59,7 @@ void* AudioInjectionManager::injectAudioViaThread(void* args) { // if we don't have an explicit destination socket then pull active socket for current audio mixer from node list if (!_isDestinationSocketExplicit) { Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - if (audioMixer) { + if (audioMixer && audioMixer->getActiveSocket()) { _destinationSocket = *audioMixer->getActiveSocket(); } else { pthread_exit(0); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index e3b137fe4d..d7fd003515 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -10,8 +10,10 @@ #include #include +#include #include #include +#include #include "AudioInjector.h" @@ -23,8 +25,6 @@ AudioInjector::AudioInjector(const char* filename) : _indexOfNextSlot(0), _isInjectingAudio(false) { - loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); - std::fstream sourceFile; sourceFile.open(filename, std::ios::in | std::ios::binary); @@ -53,8 +53,6 @@ AudioInjector::AudioInjector(int maxNumSamples) : _indexOfNextSlot(0), _isInjectingAudio(false) { - loadRandomIdentifier(_streamIdentifier, STREAM_IDENTIFIER_NUM_BYTES); - _audioSampleArray = new int16_t[maxNumSamples]; memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t)); } @@ -71,7 +69,7 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination // calculate the number of bytes required for additional data int leadingBytes = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO) - + sizeof(_streamIdentifier) + + NUM_BYTES_RFC4122_UUID + sizeof(_position) + sizeof(_orientation) + sizeof(_radius) @@ -82,8 +80,9 @@ void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destination unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, PACKET_TYPE_INJECT_AUDIO); // copy the identifier for this injector - memcpy(currentPacketPtr, &_streamIdentifier, sizeof(_streamIdentifier)); - currentPacketPtr += sizeof(_streamIdentifier); + QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); + memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size()); + currentPacketPtr += rfcUUID.size(); memcpy(currentPacketPtr, &_position, sizeof(_position)); currentPacketPtr += sizeof(_position); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 8847ce14b9..be265fe57f 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -19,8 +19,6 @@ #include "AudioRingBuffer.h" -const int STREAM_IDENTIFIER_NUM_BYTES = 8; - const int MAX_INJECTOR_VOLUME = 0xFF; const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000); @@ -61,7 +59,6 @@ public slots: int16_t& sampleAt(const int index); void insertSample(const int index, int sample); private: - unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES]; int16_t* _audioSampleArray; int _numTotalSamples; glm::vec3 _position; diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 7e93c7716c..fc5479fa88 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -9,13 +9,13 @@ #include #include +#include #include "InjectedAudioRingBuffer.h" InjectedAudioRingBuffer::InjectedAudioRingBuffer() : _radius(0.0f), - _attenuationRatio(0), - _streamIdentifier() + _attenuationRatio(0) { } @@ -23,9 +23,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer() : int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer); - // pull stream identifier from the packet - memcpy(&_streamIdentifier, currentBuffer, sizeof(_streamIdentifier)); - currentBuffer += sizeof(_streamIdentifier); + // push past the UUID for this injector + currentBuffer += NUM_BYTES_RFC4122_UUID; // use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); diff --git a/libraries/audio/src/InjectedAudioRingBuffer.h b/libraries/audio/src/InjectedAudioRingBuffer.h index e1df9ac5b9..28284cf404 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.h +++ b/libraries/audio/src/InjectedAudioRingBuffer.h @@ -21,7 +21,6 @@ public: float getRadius() const { return _radius; } float getAttenuationRatio() const { return _attenuationRatio; } - const unsigned char* getStreamIdentifier() const { return _streamIdentifier; } private: // disallow copying of InjectedAudioRingBuffer objects InjectedAudioRingBuffer(const InjectedAudioRingBuffer&); @@ -29,7 +28,6 @@ private: float _radius; float _attenuationRatio; - unsigned char _streamIdentifier[STREAM_IDENTIFIER_NUM_BYTES]; }; #endif /* defined(__hifi__InjectedAudioRingBuffer__) */ diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 9980b48a3e..3cb4acbf94 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "PositionalAudioRingBuffer.h" @@ -17,9 +18,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer() : AudioRingBuffer(false), _position(0.0f, 0.0f, 0.0f), _orientation(0.0f, 0.0f, 0.0f, 0.0f), - _willBeAddedToMix(false), - _listenMode(AudioRingBuffer::NORMAL), - _listenRadius(0.0f) + _willBeAddedToMix(false) { } @@ -27,65 +26,15 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer() : PositionalAudioRingBuffer::~PositionalAudioRingBuffer() { } -bool PositionalAudioRingBuffer::isListeningToNode(Node& other) const { - switch (_listenMode) { - default: - case AudioRingBuffer::NORMAL: - return true; - break; - - case AudioRingBuffer::OMNI_DIRECTIONAL_POINT: { - PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) other.getLinkedData(); - float distance = glm::distance(_position, otherNodeBuffer->_position); - return distance <= _listenRadius; - break; - } - case AudioRingBuffer::SELECTED_SOURCES: - for (int i = 0; i < _listenSources.size(); i++) { - if (other.getNodeID() == _listenSources[i]) { - return true; - } - } - return false; - break; - } -} - - int PositionalAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer); - currentBuffer += sizeof(uint16_t); // the source ID - currentBuffer += parseListenModeData(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); + currentBuffer += NUM_BYTES_RFC4122_UUID; // the source UUID currentBuffer += parsePositionalData(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); currentBuffer += parseAudioSamples(currentBuffer, numBytes - (currentBuffer - sourceBuffer)); return currentBuffer - sourceBuffer; } -int PositionalAudioRingBuffer::parseListenModeData(unsigned char* sourceBuffer, int numBytes) { - unsigned char* currentBuffer = sourceBuffer; - - memcpy(&_listenMode, currentBuffer, sizeof(_listenMode)); - currentBuffer += sizeof(_listenMode); - - if (_listenMode == AudioRingBuffer::OMNI_DIRECTIONAL_POINT) { - memcpy(&_listenRadius, currentBuffer, sizeof(_listenRadius)); - currentBuffer += sizeof(_listenRadius); - } else if (_listenMode == AudioRingBuffer::SELECTED_SOURCES) { - int listenSourcesCount; - memcpy(&listenSourcesCount, currentBuffer, sizeof(listenSourcesCount)); - currentBuffer += sizeof(listenSourcesCount); - for (int i = 0; i < listenSourcesCount; i++) { - int sourceID; - memcpy(&sourceID, currentBuffer, sizeof(sourceID)); - currentBuffer += sizeof(sourceID); - _listenSources.push_back(sourceID); - } - } - - return currentBuffer - sourceBuffer; -} - int PositionalAudioRingBuffer::parsePositionalData(unsigned char* sourceBuffer, int numBytes) { unsigned char* currentBuffer = sourceBuffer; diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index b43cd60660..1cd0751e9b 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -30,9 +30,6 @@ public: const glm::vec3& getPosition() const { return _position; } const glm::quat& getOrientation() const { return _orientation; } - - bool isListeningToNode(Node& other) const; - ListenMode getListeningMode() const { return _listenMode; } protected: // disallow copying of PositionalAudioRingBuffer objects @@ -42,10 +39,6 @@ protected: glm::vec3 _position; glm::quat _orientation; bool _willBeAddedToMix; - - ListenMode _listenMode; - float _listenRadius; - std::vector _listenSources; }; #endif /* defined(__hifi__PositionalAudioRingBuffer__) */ diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 46aad2d86f..e9a4034b09 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -13,9 +13,10 @@ #include #include #include +#include +#include #include "AvatarData.h" -#include using namespace std; @@ -29,7 +30,7 @@ AvatarData::AvatarData(Node* owningNode) : _bodyPitch(0.0), _bodyRoll(0.0), _newScale(1.0f), - _leaderID(UNKNOWN_NODE_ID), + _leaderUUID(), _handState(0), _cameraPosition(0,0,0), _cameraOrientation(), @@ -53,22 +54,6 @@ AvatarData::~AvatarData() { delete _handData; } -void AvatarData::sendData() { - - // called from Agent visual loop to send data - if (Node* avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER)) { - unsigned char packet[MAX_PACKET_SIZE]; - - unsigned char* endOfPacket = packet; - endOfPacket += populateTypeAndVersion(endOfPacket, PACKET_TYPE_HEAD_DATA); - endOfPacket += packNodeId(endOfPacket, NodeList::getInstance()->getOwnerID()); - - int numPacketBytes = (endOfPacket - packet) + getBroadcastData(endOfPacket); - - NodeList::getInstance()->getNodeSocket()->send(avatarMixer->getActiveSocket(), packet, numPacketBytes); - } -} - int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { unsigned char* bufferStart = destinationBuffer; @@ -103,8 +88,8 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _newScale); // Follow mode info - memcpy(destinationBuffer, &_leaderID, sizeof(uint16_t)); - destinationBuffer += sizeof(uint16_t); + memcpy(destinationBuffer, _leaderUUID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); + destinationBuffer += NUM_BYTES_RFC4122_UUID; // Head rotation (NOTE: This needs to become a quaternion to save two bytes) destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_yaw); @@ -220,11 +205,10 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char* startPosition = sourceBuffer; - // push past the node ID - sourceBuffer += sizeof(uint16_t); + // push past the node session UUID + sourceBuffer += NUM_BYTES_RFC4122_UUID; - // UUID - const int NUM_BYTES_RFC4122_UUID = 16; + // user UUID _uuid = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID)); sourceBuffer += NUM_BYTES_RFC4122_UUID; @@ -241,8 +225,8 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _newScale); // Follow mode info - memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t)); - sourceBuffer += sizeof(uint16_t); + _leaderUUID = QUuid::fromRfc4122(QByteArray((char*) sourceBuffer, NUM_BYTES_RFC4122_UUID)); + sourceBuffer += NUM_BYTES_RFC4122_UUID; // Head rotation (NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index f3ccab1504..c621fbbe63 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -120,13 +120,12 @@ public: bool getWantDelta() const { return _wantDelta; } bool getWantLowResMoving() const { return _wantLowResMoving; } bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } - uint16_t getLeaderID() const { return _leaderID; } + const QUuid& getLeaderUUID() const { return _leaderUUID; } void setHeadData(HeadData* headData) { _headData = headData; } void setHandData(HandData* handData) { _handData = handData; } public slots: - void sendData(); void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; } void setWantColor(bool wantColor) { _wantColor = wantColor; } void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } @@ -147,7 +146,7 @@ protected: float _newScale; // Following mode infos - uint16_t _leaderID; + QUuid _leaderUUID; // Hand state (are we grabbing something or not) char _handState; diff --git a/libraries/shared/src/Assignment.cpp b/libraries/shared/src/Assignment.cpp index 6a7f62c91b..83f1e4087c 100644 --- a/libraries/shared/src/Assignment.cpp +++ b/libraries/shared/src/Assignment.cpp @@ -8,6 +8,7 @@ #include "PacketHeaders.h" #include "SharedUtil.h" +#include "UUID.h" #include diff --git a/libraries/shared/src/Assignment.h b/libraries/shared/src/Assignment.h index cc528e9bd4..749622db46 100644 --- a/libraries/shared/src/Assignment.h +++ b/libraries/shared/src/Assignment.h @@ -15,7 +15,6 @@ #include "NodeList.h" -const int NUM_BYTES_RFC4122_UUID = 16; const int MAX_PAYLOAD_BYTES = 1024; /// Holds information used for request, creation, and deployment of assignments diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 01002a3d61..9d9c9e9ae4 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -23,19 +23,9 @@ #include -int unpackNodeId(unsigned char* packedData, uint16_t* nodeId) { - memcpy(nodeId, packedData, sizeof(uint16_t)); - return sizeof(uint16_t); -} - -int packNodeId(unsigned char* packStore, uint16_t nodeId) { - memcpy(packStore, &nodeId, sizeof(uint16_t)); - return sizeof(uint16_t); -} - -Node::Node(sockaddr* publicSocket, sockaddr* localSocket, char type, uint16_t nodeID) : +Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket) : _type(type), - _nodeID(nodeID), + _uuid(uuid), _wakeMicrostamp(usecTimestampNow()), _lastHeardMicrostamp(usecTimestampNow()), _activeSocket(NULL), @@ -106,10 +96,12 @@ const char* Node::getTypeName() const { } void Node::activateLocalSocket() { + qDebug() << "Activating local socket for node" << *this << "\n"; _activeSocket = _localSocket; } void Node::activatePublicSocket() { + qDebug() << "Activating public socket for node" << *this << "\n"; _activeSocket = _publicSocket; } @@ -152,10 +144,12 @@ QDebug operator<<(QDebug debug, const Node &node) { char publicAddressBuffer[16] = {'\0'}; unsigned short publicAddressPort = loadBufferWithSocketInfo(publicAddressBuffer, node.getPublicSocket()); - //char localAddressBuffer[16] = {'\0'}; - //unsigned short localAddressPort = loadBufferWithSocketInfo(localAddressBuffer, node.localSocket); + char localAddressBuffer[16] = {'\0'}; + unsigned short localAddressPort = loadBufferWithSocketInfo(localAddressBuffer, node.getLocalSocket()); - debug << "#" << node.getNodeID() << node.getTypeName() << node.getType(); + debug.nospace() << node.getTypeName() << " (" << node.getType() << ")"; + debug << " " << node.getUUID().toString().toLocal8Bit().constData() << " "; debug.nospace() << publicAddressBuffer << ":" << publicAddressPort; + debug.nospace() << " / " << localAddressBuffer << ":" << localAddressPort; return debug.nospace(); } diff --git a/libraries/shared/src/Node.h b/libraries/shared/src/Node.h index 2de75bcec1..1a204f569b 100644 --- a/libraries/shared/src/Node.h +++ b/libraries/shared/src/Node.h @@ -19,13 +19,14 @@ #endif #include +#include #include "NodeData.h" #include "SimpleMovingAverage.h" class Node { public: - Node(sockaddr* publicSocket, sockaddr* localSocket, char type, uint16_t nodeID); + Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket); ~Node(); bool operator==(const Node& otherNode); @@ -36,8 +37,8 @@ public: void setType(char type) { _type = type; } const char* getTypeName() const; - uint16_t getNodeID() const { return _nodeID; } - void setNodeID(uint16_t nodeID) { _nodeID = nodeID;} + const QUuid& getUUID() const { return _uuid; } + void setUUID(const QUuid& uuid) { _uuid = uuid; } uint64_t getWakeMicrostamp() const { return _wakeMicrostamp; } void setWakeMicrostamp(uint64_t wakeMicrostamp) { _wakeMicrostamp = wakeMicrostamp; } @@ -78,7 +79,7 @@ private: Node& operator=(Node otherNode); char _type; - uint16_t _nodeID; + QUuid _uuid; uint64_t _wakeMicrostamp; uint64_t _lastHeardMicrostamp; sockaddr* _publicSocket; diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 328e1f0faa..8d8d48dcdc 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -20,6 +20,7 @@ #include "NodeTypes.h" #include "PacketHeaders.h" #include "SharedUtil.h" +#include "UUID.h" #ifdef _WIN32 #include "Syssocket.h" @@ -67,12 +68,14 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) : _nodeSocket(newSocketListenPort), _ownerType(newOwnerType), _nodeTypesOfInterest(NULL), - _ownerID(UNKNOWN_NODE_ID), - _lastNodeID(UNKNOWN_NODE_ID + 1), + _ownerUUID(QUuid::createUuid()), _numNoReplyDomainCheckIns(0), _assignmentServerSocket(NULL), _checkInPacket(NULL), - _numBytesCheckInPacket(0) + _numBytesCheckInPacket(0), + _publicAddress(), + _publicPort(0), + _shouldUseDomainServerAsSTUN(0) { } @@ -115,8 +118,6 @@ void NodeList::setDomainHostname(const QString& domainHostname) { _domainIP.clear(); notifyDomainChanged(); } - - } void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) { @@ -143,16 +144,25 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat break; } case PACKET_TYPE_PING: { - char pingPacket[dataBytes]; - memcpy(pingPacket, packetData, dataBytes); - populateTypeAndVersion((unsigned char*) pingPacket, PACKET_TYPE_PING_REPLY); - _nodeSocket.send(senderAddress, pingPacket, dataBytes); + // send it right back + populateTypeAndVersion(packetData, PACKET_TYPE_PING_REPLY); + _nodeSocket.send(senderAddress, packetData, dataBytes); break; } case PACKET_TYPE_PING_REPLY: { + // activate the appropriate socket for this node, if not yet updated + activateSocketFromPingReply(senderAddress); + + // set the ping time for this node for stat collection timePingReply(senderAddress, packetData); break; } + case PACKET_TYPE_STUN_RESPONSE: { + // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch + // pass it along so it can be processed into our public address and port + processSTUNResponse(packetData, dataBytes); + break; + } } } @@ -174,19 +184,18 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe // we've already verified packet version for the bulk packet, so all head data in the packet is also up to date populateTypeAndVersion(packetHolder, PACKET_TYPE_HEAD_DATA); - uint16_t nodeID = -1; - while ((currentPosition - startPosition) < numTotalBytes) { - unpackNodeId(currentPosition, &nodeID); + memcpy(packetHolder + numBytesPacketHeader, currentPosition, numTotalBytes - (currentPosition - startPosition)); - Node* matchingNode = nodeWithID(nodeID); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)currentPosition, NUM_BYTES_RFC4122_UUID)); + Node* matchingNode = nodeWithUUID(nodeUUID); if (!matchingNode) { // we're missing this node, we need to add it to the list - matchingNode = addOrUpdateNode(NULL, NULL, NODE_TYPE_AGENT, nodeID); + matchingNode = addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, NULL, NULL); } currentPosition += updateNodeWithData(matchingNode, @@ -238,9 +247,9 @@ Node* NodeList::nodeWithAddress(sockaddr *senderAddress) { return NULL; } -Node* NodeList::nodeWithID(uint16_t nodeID) { +Node* NodeList::nodeWithUUID(const QUuid& nodeUUID) { for(NodeList::iterator node = begin(); node != end(); node++) { - if (node->getNodeID() == nodeID) { + if (node->getUUID() == nodeUUID) { return &(*node); } } @@ -288,6 +297,9 @@ void NodeList::reset() { delete _nodeTypesOfInterest; _nodeTypesOfInterest = NULL; + + // refresh the owner UUID + _ownerUUID = QUuid::createUuid(); } void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) { @@ -298,7 +310,137 @@ void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNo _nodeTypesOfInterest[numNodeTypesOfInterest] = '\0'; } -void NodeList::sendDomainServerCheckIn(const char* assignmentUUID) { +const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; +const int NUM_BYTES_STUN_HEADER = 20; +const int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5; + +void NodeList::sendSTUNRequest() { + const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io"; + const unsigned short STUN_SERVER_PORT = 3478; + + static int failedStunRequests = 0; + + if (failedStunRequests < NUM_STUN_REQUESTS_BEFORE_FALLBACK) { + unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER]; + + int packetIndex = 0; + + const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE); + + // leading zeros + message type + const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001); + memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE)); + packetIndex += sizeof(REQUEST_MESSAGE_TYPE); + + // message length (no additional attributes are included) + uint16_t messageLength = 0; + memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength)); + packetIndex += sizeof(messageLength); + + memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)); + packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); + + // transaction ID (random 12-byte unsigned integer) + const uint NUM_TRANSACTION_ID_BYTES = 12; + unsigned char transactionID[NUM_TRANSACTION_ID_BYTES]; + loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES); + memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID)); + + // lookup the IP for the STUN server + static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME); + + for (int i = 0; i < stunInfo.addresses().size(); i++) { + if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { + QString stunIPAddress = stunInfo.addresses()[i].toString(); + + qDebug("Sending a stun request to %s\n", stunIPAddress.toLocal8Bit().constData()); + + _nodeSocket.send(stunIPAddress.toLocal8Bit().constData(), + STUN_SERVER_PORT, + stunRequestPacket, + sizeof(stunRequestPacket)); + + break; + } + } + + failedStunRequests++; + + return; + } + + // if we're here this was the last failed STUN request + // use our DS as our stun server + qDebug("Failed to lookup public address via STUN server at %s:%hu. Using DS for STUN.\n", + STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); + _shouldUseDomainServerAsSTUN = true; +} + +void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) { + // check the cookie to make sure this is actually a STUN response + // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS + const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4; + const uint16_t XOR_MAPPED_ADDRESS_TYPE = htons(0x0020); + + const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE); + + int attributeStartIndex = NUM_BYTES_STUN_HEADER; + + if (memcmp(packetData + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH, + &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, + sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) { + + // enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE + while (attributeStartIndex < dataBytes) { + if (memcmp(packetData + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) { + const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4; + const int NUM_BYTES_FAMILY_ALIGN = 1; + const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8; + + int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; + + uint8_t addressFamily = 0; + memcpy(&addressFamily, packetData + byteIndex, sizeof(addressFamily)); + + byteIndex += sizeof(addressFamily); + + if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) { + // grab the X-Port + uint16_t xorMappedPort = 0; + memcpy(&xorMappedPort, packetData + byteIndex, sizeof(xorMappedPort)); + + _publicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16); + + byteIndex += sizeof(xorMappedPort); + + // grab the X-Address + uint32_t xorMappedAddress = 0; + memcpy(&xorMappedAddress, packetData + byteIndex, sizeof(xorMappedAddress)); + + uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER); + _publicAddress = QHostAddress(stunAddress); + + qDebug("Public socket received from STUN server is %s:%hu\n", + _publicAddress.toString().toLocal8Bit().constData(), + _publicPort); + + break; + } + } else { + // push forward attributeStartIndex by the length of this attribute + const int NUM_BYTES_ATTRIBUTE_TYPE = 2; + + uint16_t attributeLength = 0; + memcpy(&attributeLength, packetData + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE, sizeof(attributeLength)); + attributeLength = ntohs(attributeLength); + + attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength; + } + } + } +} + +void NodeList::sendDomainServerCheckIn() { static bool printedDomainServerIP = false; // Lookup the IP address of the domain server if we need to @@ -329,56 +471,66 @@ void NodeList::sendDomainServerCheckIn(const char* assignmentUUID) { printedDomainServerIP = true; } - // construct the DS check in packet if we need to - if (!_checkInPacket) { - int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0; - - const int IP_ADDRESS_BYTES = 4; - - // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination - int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) + - NUM_BYTES_RFC4122_UUID + sizeof(uint16_t) + IP_ADDRESS_BYTES + numBytesNodesOfInterest + sizeof(unsigned char); - - _checkInPacket = new unsigned char[numPacketBytes]; - unsigned char* packetPosition = _checkInPacket; - - PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES))) - ? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY - : PACKET_TYPE_DOMAIN_LIST_REQUEST; - - int numHeaderBytes = populateTypeAndVersion(packetPosition, nodePacketType); - packetPosition += numHeaderBytes; - - *(packetPosition++) = _ownerType; - - if (assignmentUUID) { - // if we've got an assignment UUID to send add that here - memcpy(packetPosition, assignmentUUID, NUM_BYTES_RFC4122_UUID); - packetPosition += NUM_BYTES_RFC4122_UUID; + if (_publicAddress.isNull() && !_shouldUseDomainServerAsSTUN) { + // we don't know our public socket and we need to send it to the domain server + // send a STUN request to figure it out + sendSTUNRequest(); + } else { + // construct the DS check in packet if we need to + if (!_checkInPacket) { + int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0; + + const int IP_ADDRESS_BYTES = 4; + + // check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination + int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) + + NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) + + numBytesNodesOfInterest + sizeof(unsigned char); + + _checkInPacket = new unsigned char[numPacketBytes]; + unsigned char* packetPosition = _checkInPacket; + + PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES))) + ? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY + : PACKET_TYPE_DOMAIN_LIST_REQUEST; + + packetPosition += populateTypeAndVersion(packetPosition, nodePacketType); + + *(packetPosition++) = _ownerType; + + // send our owner UUID or the null one + QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122(); + memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size()); + packetPosition += rfcOwnerUUID.size(); + + // pack our public address to send to domain-server + packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket), + htonl(_publicAddress.toIPv4Address()), htons(_publicPort)); + + // pack our local address to send to domain-server + packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket), + getLocalAddress(), + htons(_nodeSocket.getListeningPort())); + + // add the number of bytes for node types of interest + *(packetPosition++) = numBytesNodesOfInterest; + + // copy over the bytes for node types of interest, if required + if (numBytesNodesOfInterest > 0) { + memcpy(packetPosition, + _nodeTypesOfInterest, + numBytesNodesOfInterest); + packetPosition += numBytesNodesOfInterest; + } + + _numBytesCheckInPacket = packetPosition - _checkInPacket; } - packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket), - getLocalAddress(), - htons(_nodeSocket.getListeningPort())); + _nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, _checkInPacket, _numBytesCheckInPacket); - // add the number of bytes for node types of interest - *(packetPosition++) = numBytesNodesOfInterest; - - // copy over the bytes for node types of interest, if required - if (numBytesNodesOfInterest > 0) { - memcpy(packetPosition, - _nodeTypesOfInterest, - numBytesNodesOfInterest); - packetPosition += numBytesNodesOfInterest; - } - - _numBytesCheckInPacket = packetPosition - _checkInPacket; + // increment the count of un-replied check-ins + _numNoReplyDomainCheckIns++; } - - _nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, _checkInPacket, _numBytesCheckInPacket); - - // increment the count of un-replied check-ins - _numNoReplyDomainCheckIns++; } int NodeList::processDomainServerList(unsigned char* packetData, size_t dataBytes) { @@ -388,7 +540,6 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte int readNodes = 0; char nodeType; - uint16_t nodeId; // assumes only IPv4 addresses sockaddr_in nodePublicSocket; @@ -401,7 +552,9 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte while((readPtr - startPtr) < dataBytes - sizeof(uint16_t)) { nodeType = *readPtr++; - readPtr += unpackNodeId(readPtr, (uint16_t*) &nodeId); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) readPtr, NUM_BYTES_RFC4122_UUID)); + readPtr += NUM_BYTES_RFC4122_UUID; + readPtr += unpackSocket(readPtr, (sockaddr*) &nodePublicSocket); readPtr += unpackSocket(readPtr, (sockaddr*) &nodeLocalSocket); @@ -411,11 +564,9 @@ int NodeList::processDomainServerList(unsigned char* packetData, size_t dataByte nodePublicSocket.sin_addr.s_addr = htonl(_domainIP.toIPv4Address()); } - addOrUpdateNode((sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket, nodeType, nodeId); + addOrUpdateNode(nodeUUID, nodeType, (sockaddr*) &nodePublicSocket, (sockaddr*) &nodeLocalSocket); } - // read out our ID from the packet - unpackNodeId(readPtr, &_ownerID); return readNodes; } @@ -439,41 +590,41 @@ void NodeList::sendAssignment(Assignment& assignment) { _nodeSocket.send(assignmentServerSocket, assignmentPacket, numHeaderBytes + numAssignmentBytes); } -Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId) { +void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const { + + uint64_t currentTime = 0; + + // setup a ping packet to send to this node + unsigned char pingPacket[numBytesForPacketHeader((uchar*) &PACKET_TYPE_PING) + sizeof(currentTime)]; + int numHeaderBytes = populateTypeAndVersion(pingPacket, PACKET_TYPE_PING); + + currentTime = usecTimestampNow(); + memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime)); + + qDebug() << "Attemping to ping" << *node << "\n"; + // send the ping packet to the local and public sockets for this node + _nodeSocket.send(node->getLocalSocket(), pingPacket, sizeof(pingPacket)); + _nodeSocket.send(node->getPublicSocket(), pingPacket, sizeof(pingPacket)); +} + +Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket) { NodeList::iterator node = end(); - if (publicSocket) { - for (node = begin(); node != end(); node++) { - if (node->matches(publicSocket, localSocket, nodeType)) { - // we already have this node, stop checking - break; - } + for (node = begin(); node != end(); node++) { + if (node->getUUID() == uuid) { + // we already have this node, stop checking + break; } } if (node == end()) { // we didn't have this node, so add them - Node* newNode = new Node(publicSocket, localSocket, nodeType, nodeId); - - if (socketMatch(publicSocket, localSocket)) { - // likely debugging scenario with two nodes on local network - // set the node active right away - newNode->activatePublicSocket(); - } - - if (newNode->getType() == NODE_TYPE_VOXEL_SERVER || - newNode->getType() == NODE_TYPE_AVATAR_MIXER || - newNode->getType() == NODE_TYPE_AUDIO_MIXER) { - // this is currently the cheat we use to talk directly to our test servers on EC2 - // to be removed when we have a proper identification strategy - newNode->activatePublicSocket(); - } + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket); addNodeToList(newNode); return newNode; } else { - if (node->getType() == NODE_TYPE_AUDIO_MIXER || node->getType() == NODE_TYPE_VOXEL_SERVER) { // until the Audio class also uses our nodeList, we need to update @@ -507,25 +658,32 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt unsigned n = 0; for(NodeList::iterator node = begin(); node != end(); node++) { // only send to the NodeTypes we are asked to send to. - if (node->getActiveSocket() != NULL && memchr(nodeTypes, node->getType(), numNodeTypes)) { - // we know which socket is good for this node, send there - _nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes); - ++n; + if (memchr(nodeTypes, node->getType(), numNodeTypes)) { + if (node->getActiveSocket()) { + // we know which socket is good for this node, send there + _nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes); + ++n; + } else { + // we don't have an active link to this node, ping it to set that up + pingPublicAndLocalSocketsForInactiveNode(&(*node)); + } } } return n; } -void NodeList::handlePingReply(sockaddr *nodeAddress) { +void NodeList::activateSocketFromPingReply(sockaddr *nodeAddress) { for(NodeList::iterator node = begin(); node != end(); node++) { - // check both the public and local addresses for each node to see if we find a match - // prioritize the private address so that we prune erroneous local matches - if (socketMatch(node->getPublicSocket(), nodeAddress)) { - node->activatePublicSocket(); - break; - } else if (socketMatch(node->getLocalSocket(), nodeAddress)) { - node->activateLocalSocket(); - break; + if (!node->getActiveSocket()) { + // check both the public and local addresses for each node to see if we find a match + // prioritize the private address so that we prune erroneous local matches + if (socketMatch(node->getPublicSocket(), nodeAddress)) { + node->activatePublicSocket(); + break; + } else if (socketMatch(node->getLocalSocket(), nodeAddress)) { + node->activateLocalSocket(); + break; + } } } } diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 8d2bd16cae..ed7f0e637a 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -42,8 +42,6 @@ extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT; const char LOCAL_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost"; -const int UNKNOWN_NODE_ID = 0; - const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5; class Assignment; @@ -83,12 +81,9 @@ public: unsigned short getDomainPort() const { return _domainPort; } void setDomainPort(unsigned short domainPort) { _domainPort = domainPort; } - - uint16_t getLastNodeID() const { return _lastNodeID; } - void increaseNodeID() { (++_lastNodeID == UNKNOWN_NODE_ID) ? ++_lastNodeID : _lastNodeID; } - uint16_t getOwnerID() const { return _ownerID; } - void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; } + const QUuid& getOwnerUUID() const { return _ownerUUID; } + void setOwnerUUID(const QUuid& ownerUUID) { _ownerUUID = ownerUUID; } UDPSocket* getNodeSocket() { return &_nodeSocket; } @@ -106,16 +101,18 @@ public: void setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest); - void sendDomainServerCheckIn(const char* assignmentUUID = NULL); + void sendDomainServerCheckIn(); int processDomainServerList(unsigned char *packetData, size_t dataBytes); void setAssignmentServerSocket(sockaddr* serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); - Node* nodeWithAddress(sockaddr *senderAddress); - Node* nodeWithID(uint16_t nodeID); + void pingPublicAndLocalSocketsForInactiveNode(Node* node) const; - Node* addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, char nodeType, uint16_t nodeId); + Node* nodeWithAddress(sockaddr *senderAddress); + Node* nodeWithUUID(const QUuid& nodeUUID); + + Node* addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket); void processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes); void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes); @@ -153,6 +150,9 @@ private: void addNodeToList(Node* newNode); + void sendSTUNRequest(); + void processSTUNResponse(unsigned char* packetData, size_t dataBytes); + QString _domainHostname; QHostAddress _domainIP; unsigned short _domainPort; @@ -161,16 +161,18 @@ private: UDPSocket _nodeSocket; char _ownerType; char* _nodeTypesOfInterest; - uint16_t _ownerID; - uint16_t _lastNodeID; + QUuid _ownerUUID; pthread_t removeSilentNodesThread; pthread_t checkInWithDomainServerThread; int _numNoReplyDomainCheckIns; sockaddr* _assignmentServerSocket; uchar* _checkInPacket; int _numBytesCheckInPacket; + QHostAddress _publicAddress; + uint16_t _publicPort; + bool _shouldUseDomainServerAsSTUN; - void handlePingReply(sockaddr *nodeAddress); + void activateSocketFromPingReply(sockaddr *nodeAddress); void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); std::vector _hooks; diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 8146924cf8..80f6ba121f 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -16,7 +16,7 @@ #include "SharedUtil.h" #include "OctalCode.h" -int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { +int numberOfThreeBitSectionsInCode(const unsigned char* octalCode) { assert(octalCode); if (*octalCode == 255) { return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1); @@ -25,18 +25,18 @@ int numberOfThreeBitSectionsInCode(unsigned char * octalCode) { } } -void printOctalCode(unsigned char * octalCode) { +void printOctalCode(const unsigned char* octalCode) { if (!octalCode) { qDebug("NULL\n"); } else { - for (int i = 0; i < bytesRequiredForCodeLength(*octalCode); i++) { + for (int i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) { outputBits(octalCode[i],false); } qDebug("\n"); } } -char sectionValue(unsigned char * startByte, char startIndexInByte) { +char sectionValue(const unsigned char* startByte, char startIndexInByte) { char rightShift = 8 - startIndexInByte - 3; if (rightShift < 0) { @@ -54,14 +54,14 @@ int bytesRequiredForCodeLength(unsigned char threeBitCodes) { } } -int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode) { +int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode) { int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode); int branchStartBit = parentSections * 3; return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8); } -unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber) { +unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber) { // find the length (in number of three bit code sequences) // in the parent @@ -76,7 +76,7 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber int childCodeBytes = bytesRequiredForCodeLength(parentCodeSections + 1); // create a new buffer to hold the new octal code - unsigned char *newCode = new unsigned char[childCodeBytes]; + unsigned char* newCode = new unsigned char[childCodeBytes]; // copy the parent code to the child if (parentOctalCode != NULL) { @@ -115,7 +115,7 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber return newCode; } -void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize) { +void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxelPositionSize) { float output[3]; memset(&output[0], 0, 3 * sizeof(float)); float currentScale = 1.0; @@ -138,7 +138,7 @@ void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPosi voxelPositionSize.s = currentScale; } -void copyFirstVertexForCode(unsigned char * octalCode, float* output) { +void copyFirstVertexForCode(const unsigned char* octalCode, float* output) { memset(output, 0, 3 * sizeof(float)); float currentScale = 0.5; @@ -154,13 +154,13 @@ void copyFirstVertexForCode(unsigned char * octalCode, float* output) { } } -float * firstVertexForCode(unsigned char * octalCode) { +float * firstVertexForCode(const unsigned char* octalCode) { float * firstVertex = new float[3]; copyFirstVertexForCode(octalCode, firstVertex); return firstVertex; } -OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB) { +OctalCodeComparison compareOctalCodes(const unsigned char* codeA, const unsigned char* codeB) { if (!codeA || !codeB) { return ILLEGAL_CODE; } @@ -196,10 +196,10 @@ OctalCodeComparison compareOctalCodes(unsigned char* codeA, unsigned char* codeB } -char getOctalCodeSectionValue(unsigned char* octalCode, int section) { +char getOctalCodeSectionValue(const unsigned char* octalCode, int section) { int startAtByte = 1 + (BITS_IN_OCTAL * section / BITS_IN_BYTE); char startIndexInByte = (BITS_IN_OCTAL * section) % BITS_IN_BYTE; - unsigned char* startByte = octalCode + startAtByte; + const unsigned char* startByte = octalCode + startAtByte; return sectionValue(startByte, startIndexInByte); } @@ -243,7 +243,7 @@ void setOctalCodeSectionValue(unsigned char* octalCode, int section, char sectio } } -unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels) { +unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels) { int codeLength = numberOfThreeBitSectionsInCode(originalOctalCode); unsigned char* newCode = NULL; if (codeLength > chopLevels) { @@ -259,7 +259,9 @@ unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels) { return newCode; } -unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode, bool includeColorSpace) { +unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, + bool includeColorSpace) { + int oldCodeLength = numberOfThreeBitSectionsInCode(originalOctalCode); int newParentCodeLength = numberOfThreeBitSectionsInCode(newParentOctalCode); int newCodeLength = newParentCodeLength + oldCodeLength; @@ -280,7 +282,7 @@ unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* return newCode; } -bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild) { +bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) { if (!possibleAncestor || !possibleDescendent) { return false; } @@ -350,7 +352,7 @@ unsigned char* hexStringToOctalCode(const QString& input) { return bytes; } -QString octalCodeToHexString(unsigned char* octalCode) { +QString octalCodeToHexString(const unsigned char* octalCode) { const int HEX_NUMBER_BASE = 16; const int HEX_BYTE_SIZE = 2; QString output; diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 4a43142bb8..27746474eb 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -20,27 +20,28 @@ const int RED_INDEX = 0; const int GREEN_INDEX = 1; const int BLUE_INDEX = 2; -void printOctalCode(unsigned char * octalCode); +void printOctalCode(const unsigned char* octalCode); int bytesRequiredForCodeLength(unsigned char threeBitCodes); -int branchIndexWithDescendant(unsigned char * ancestorOctalCode, unsigned char * descendantOctalCode); -unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber); -int numberOfThreeBitSectionsInCode(unsigned char * octalCode); -unsigned char* chopOctalCode(unsigned char* originalOctalCode, int chopLevels); -unsigned char* rebaseOctalCode(unsigned char* originalOctalCode, unsigned char* newParentOctalCode, +int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode); +unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber); +int numberOfThreeBitSectionsInCode(const unsigned char* octalCode); +unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels); +unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, bool includeColorSpace = false); const int CHECK_NODE_ONLY = -1; -bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescendent, int descendentsChild = CHECK_NODE_ONLY); +bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, + int descendentsChild = CHECK_NODE_ONLY); // Note: copyFirstVertexForCode() is preferred because it doesn't allocate memory for the return // but other than that these do the same thing. -float * firstVertexForCode(unsigned char * octalCode); -void copyFirstVertexForCode(unsigned char * octalCode, float* output); +float * firstVertexForCode(const unsigned char* octalCode); +void copyFirstVertexForCode(const unsigned char* octalCode, float* output); struct VoxelPositionSize { float x, y, z, s; }; -void voxelDetailsForCode(unsigned char* octalCode, VoxelPositionSize& voxelPositionSize); +void voxelDetailsForCode(const unsigned char* octalCode, VoxelPositionSize& voxelPositionSize); typedef enum { ILLEGAL_CODE = -2, @@ -49,9 +50,9 @@ typedef enum { GREATER_THAN = 1 } OctalCodeComparison; -OctalCodeComparison compareOctalCodes(unsigned char* code1, unsigned char* code2); +OctalCodeComparison compareOctalCodes(const unsigned char* code1, const unsigned char* code2); -QString octalCodeToHexString(unsigned char* octalCode); +QString octalCodeToHexString(const unsigned char* octalCode); unsigned char* hexStringToOctalCode(const QString& input); #endif /* defined(__hifi__OctalCode__) */ diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 19efd7bd5a..134a811c6b 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -17,19 +17,25 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO: case PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO: - return 1; + return 2; case PACKET_TYPE_HEAD_DATA: - return 9; + return 10; case PACKET_TYPE_AVATAR_URLS: - return 1; + return 2; case PACKET_TYPE_AVATAR_FACE_VIDEO: - return 1; + return 2; case PACKET_TYPE_VOXEL_STATS: return 2; + + case PACKET_TYPE_DOMAIN: + case PACKET_TYPE_DOMAIN_LIST_REQUEST: + case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY: + return 1; + default: return 0; } @@ -38,7 +44,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { bool packetVersionMatch(unsigned char* packetHeader) { // currently this just checks if the version in the packet matches our return from versionForPacketType // may need to be expanded in the future for types and versions that take > than 1 byte - if (packetHeader[1] == versionForPacketType(packetHeader[0])) { + if (packetHeader[1] == versionForPacketType(packetHeader[0]) || packetHeader[0] == PACKET_TYPE_STUN_RESPONSE) { return true; } else { qDebug("There is a packet version mismatch for packet with header %c\n", packetHeader[0]); diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index 254f2f74fc..2c4172ee55 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -14,6 +14,7 @@ typedef char PACKET_TYPE; const PACKET_TYPE PACKET_TYPE_UNKNOWN = 0; +const PACKET_TYPE PACKET_TYPE_STUN_RESPONSE = 1; const PACKET_TYPE PACKET_TYPE_DOMAIN = 'D'; const PACKET_TYPE PACKET_TYPE_PING = 'P'; const PACKET_TYPE PACKET_TYPE_PING_REPLY = 'R'; diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index 31f4b1a30b..267108f87e 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -108,7 +108,8 @@ bool PerformanceWarning::_suppressShortTimings = false; // Destructor handles recording all of our stats PerformanceWarning::~PerformanceWarning() { uint64_t end = usecTimestampNow(); - double elapsedmsec = (end - _start) / 1000.0; + uint64_t elapsedusec = (end - _start); + double elapsedmsec = elapsedusec / 1000.0; if ((_alwaysDisplay || _renderWarningsOn) && elapsedmsec > 1) { if (elapsedmsec > 1000) { double elapsedsec = (end - _start) / 1000000.0; @@ -127,6 +128,13 @@ PerformanceWarning::~PerformanceWarning() { } else if (_alwaysDisplay) { qDebug("%s took %lf milliseconds\n", _message, elapsedmsec); } + // if the caller gave us a pointer to store the running total, track it now. + if (_runningTotal) { + *_runningTotal += elapsedusec; + } + if (_totalCalls) { + *_totalCalls += 1; + } }; diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index 2c5924e5ce..e460548f1d 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -89,14 +89,19 @@ private: const char* _message; bool _renderWarningsOn; bool _alwaysDisplay; + uint64_t* _runningTotal; + uint64_t* _totalCalls; static bool _suppressShortTimings; public: - PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false) : + + PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, uint64_t* runningTotal = NULL, uint64_t* totalCalls = NULL) : _start(usecTimestampNow()), _message(message), _renderWarningsOn(renderWarnings), - _alwaysDisplay(alwaysDisplay) { } - + _alwaysDisplay(alwaysDisplay), + _runningTotal(runningTotal), + _totalCalls(totalCalls) { } + ~PerformanceWarning(); static void setSuppressShortTimings(bool suppressShortTimings) { _suppressShortTimings = suppressShortTimings; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 359a154c4f..3d4759c98a 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -105,10 +105,41 @@ void setAtBit(unsigned char& byte, int bitIndex) { byte += (1 << (7 - bitIndex)); } +void clearAtBit(unsigned char& byte, int bitIndex) { + if (oneAtBit(byte, bitIndex)) { + byte -= (1 << (7 - bitIndex)); + } +} + int getSemiNibbleAt(unsigned char& byte, int bitIndex) { return (byte >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 } +int getNthBit(unsigned char byte, int ordinal) { + const int ERROR = -1; + const int MIN_ORDINAL = 1; + const int MAX_ORDINAL = 8; + if (ordinal < MIN_ORDINAL || ordinal > MAX_ORDINAL) { + return ERROR; + } + int bitsSet = 0; + for (int bitIndex = 0; bitIndex < MAX_ORDINAL; bitIndex++) { + if (oneAtBit(byte, bitIndex)) { + bitsSet++; + } + if (bitsSet == ordinal) { + return bitIndex; + } + } + return ERROR; +} + +bool isBetween(int64_t value, int64_t max, int64_t min) { + return ((value <= max) && (value >= min)); +} + + + void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { //assert(value <= 3 && value >= 0); byte += ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 77c9cdd040..ece63d4737 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -55,10 +55,12 @@ void printVoxelCode(unsigned char* voxelCode); int numberOfOnes(unsigned char byte); bool oneAtBit(unsigned char byte, int bitIndex); void setAtBit(unsigned char& byte, int bitIndex); - +void clearAtBit(unsigned char& byte, int bitIndex); int getSemiNibbleAt(unsigned char& byte, int bitIndex); void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value); +int getNthBit(unsigned char byte, int ordinal); /// determines the bit placement 0-7 of the ordinal set bit + bool isInEnvironment(const char* environment); void switchToResourcesParentIfRequired(); @@ -109,4 +111,6 @@ public: static const char* valueOf(bool checkValue) { return checkValue ? "yes" : "no"; } }; +bool isBetween(int64_t value, int64_t max, int64_t min); + #endif /* defined(__hifi__SharedUtil__) */ diff --git a/libraries/shared/src/UDPSocket.cpp b/libraries/shared/src/UDPSocket.cpp index 68eb2ad82e..73849847e4 100644 --- a/libraries/shared/src/UDPSocket.cpp +++ b/libraries/shared/src/UDPSocket.cpp @@ -22,6 +22,8 @@ #endif #include +#include +#include #include "Logging.h" #include "UDPSocket.h" @@ -88,35 +90,35 @@ void copySocketToEmptySocketPointer(sockaddr** destination, const sockaddr* sour } int getLocalAddress() { - // get this node's local address so we can pass that to DS - struct ifaddrs* ifAddrStruct = NULL; - struct ifaddrs* ifa = NULL; - int family; - int localAddress = 0; - -#ifndef _WIN32 - getifaddrs(&ifAddrStruct); - - for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - family = ifa->ifa_addr->sa_family; - if (family == AF_INET) { - localAddress = ((sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; + static int localAddress = 0; + + if (localAddress == 0) { + foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { + if (interface.flags() & QNetworkInterface::IsUp + && interface.flags() & QNetworkInterface::IsRunning + && interface.flags() & ~QNetworkInterface::IsLoopBack) { + // we've decided that this is the active NIC + // enumerate it's addresses to grab the IPv4 address + foreach(const QNetworkAddressEntry &entry, interface.addressEntries()) { + // make sure it's an IPv4 address that isn't the loopback + if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol && !entry.ip().isLoopback()) { + qDebug("Node's local address is %s\n", entry.ip().toString().toLocal8Bit().constData()); + + // set our localAddress and break out + localAddress = htonl(entry.ip().toIPv4Address()); + break; + } + } + } + + if (localAddress != 0) { + break; + } } } - freeifaddrs(ifAddrStruct); -#else - // Get the local hostname - char szHostName[255]; - gethostname(szHostName, 255); - struct hostent *host_entry; - host_entry = gethostbyname(szHostName); - char * szLocalIP; - szLocalIP = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); - localAddress = inet_addr(szLocalIP); -#endif - + // return the looked up local address return localAddress; } diff --git a/libraries/shared/src/UUID.h b/libraries/shared/src/UUID.h index 985f44ae7b..cacadf5a04 100644 --- a/libraries/shared/src/UUID.h +++ b/libraries/shared/src/UUID.h @@ -11,6 +11,8 @@ #include +const int NUM_BYTES_RFC4122_UUID = 16; + QString uuidStringWithoutCurlyBraces(const QUuid& uuid); #endif /* defined(__hifi__UUID__) */ diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 811e3bc63a..1995dcc867 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -32,8 +32,8 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) { // Create voxel sending thread... - uint16_t nodeID = getOwningNode()->getNodeID(); - _voxelSendThread = new VoxelSendThread(nodeID, voxelServer); + QUuid nodeUUID = getOwningNode()->getUUID(); + _voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer); _voxelSendThread->initialize(true); } diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp index 8ad2bfaa4d..75954aacec 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ b/libraries/voxel-server-library/src/VoxelPersistThread.cpp @@ -29,21 +29,38 @@ bool VoxelPersistThread::process() { _initialLoad = true; qDebug("loading voxels from file: %s...\n", _filename); - bool persistantFileRead = _tree->readFromSVOFile(_filename); + bool persistantFileRead; + + { + PerformanceWarning warn(true, "Loading Voxel File", true); + persistantFileRead = _tree->readFromSVOFile(_filename); + } + if (persistantFileRead) { - PerformanceWarning warn(true, "reaverageVoxelColors()", true); + PerformanceWarning warn(true, "Voxels Re-Averaging", true); // after done inserting all these voxels, then reaverage colors + qDebug("BEGIN Voxels Re-Averaging\n"); _tree->reaverageVoxelColors(_tree->rootNode); - qDebug("Voxels reAveraged\n"); + qDebug("DONE WITH Voxels Re-Averaging\n"); } _tree->clearDirtyBit(); // the tree is clean since we just loaded it qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); - unsigned long nodeCount = _tree->rootNode->getSubTreeNodeCount(); - unsigned long internalNodeCount = _tree->rootNode->getSubTreeInternalNodeCount(); - unsigned long leafNodeCount = _tree->rootNode->getSubTreeLeafNodeCount(); + + unsigned long nodeCount = VoxelNode::getNodeCount(); + unsigned long internalNodeCount = VoxelNode::getInternalNodeCount(); + unsigned long leafNodeCount = VoxelNode::getLeafNodeCount(); qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); + + double usecPerGet = (double)VoxelNode::getGetChildAtIndexTime() / (double)VoxelNode::getGetChildAtIndexCalls(); + qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n", + VoxelNode::getGetChildAtIndexTime(), VoxelNode::getGetChildAtIndexCalls(), usecPerGet); + + double usecPerSet = (double)VoxelNode::getSetChildAtIndexTime() / (double)VoxelNode::getSetChildAtIndexCalls(); + qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", + VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet); + } uint64_t MSECS_TO_USECS = 1000; diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index ac2c56d2d3..2cc0277cff 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -18,15 +18,15 @@ extern EnvironmentData environmentData[3]; #include "VoxelServer.h" #include "VoxelServerConsts.h" -VoxelSendThread::VoxelSendThread(uint16_t nodeID, VoxelServer* myServer) : - _nodeID(nodeID), +VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) : + _nodeUUID(nodeUUID), _myServer(myServer) { } bool VoxelSendThread::process() { uint64_t lastSendTime = usecTimestampNow(); - Node* node = NodeList::getInstance()->nodeWithID(_nodeID); + Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); VoxelNodeData* nodeData = NULL; if (node) { @@ -83,7 +83,7 @@ void VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& } else { // just send the voxel packet NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), - nodeData->getPacket(), nodeData->getPacketLength()); + nodeData->getPacket(), nodeData->getPacketLength()); } // remember to track our stats nodeData->stats.packetSent(nodeData->getPacketLength()); diff --git a/libraries/voxel-server-library/src/VoxelSendThread.h b/libraries/voxel-server-library/src/VoxelSendThread.h index 37824abedb..8de3968dcb 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.h +++ b/libraries/voxel-server-library/src/VoxelSendThread.h @@ -21,13 +21,13 @@ /// Threaded processor for sending voxel packets to a single client class VoxelSendThread : public virtual GenericThread { public: - VoxelSendThread(uint16_t nodeID, VoxelServer* myServer); + VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer); protected: /// Implements generic processing behavior for this thread. virtual bool process(); private: - uint16_t _nodeID; + QUuid _nodeUUID; VoxelServer* _myServer; void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent); diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index f02069af2e..a24f13854b 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -11,8 +11,9 @@ #include #include -#include -#include +#include +#include +#include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #ifdef _WIN32 #include "Syssocket.h" @@ -136,18 +138,74 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n"); mg_printf(connection, "%s", "Your Voxel Server is running.\r\n"); mg_printf(connection, "%s", "Current Statistics\r\n"); - mg_printf(connection, "Voxel Node Memory Usage: %f MB\r\n", VoxelNode::getVoxelMemoryUsage() / 1000000.f); - mg_printf(connection, "Octcode Memory Usage: %f MB\r\n", VoxelNode::getOctcodeMemoryUsage() / 1000000.f); + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "Voxel Node Memory Usage: %8.2f MB\r\n", VoxelNode::getVoxelMemoryUsage() / 1000000.f); + mg_printf(connection, "Octcode Memory Usage: %8.2f MB\r\n", VoxelNode::getOctcodeMemoryUsage() / 1000000.f); + mg_printf(connection, "External Children Memory Usage: %8.2f MB\r\n", + VoxelNode::getExternalChildrenMemoryUsage() / 1000000.f); + mg_printf(connection, "%s", " -----------\r\n"); + mg_printf(connection, " Total: %8.2f MB\r\n", VoxelNode::getTotalMemoryUsage() / 1000000.f); - VoxelTree* theTree = VoxelServer::GetInstance()->getTree(); - unsigned long nodeCount = theTree->rootNode->getSubTreeNodeCount(); - unsigned long internalNodeCount = theTree->rootNode->getSubTreeInternalNodeCount(); - unsigned long leafNodeCount = theTree->rootNode->getSubTreeLeafNodeCount(); + unsigned long nodeCount = VoxelNode::getNodeCount(); + unsigned long internalNodeCount = VoxelNode::getInternalNodeCount(); + unsigned long leafNodeCount = VoxelNode::getLeafNodeCount(); + const float AS_PERCENT = 100.0; + + mg_printf(connection, "%s", "\r\n"); mg_printf(connection, "%s", "Current Nodes in scene\r\n"); - mg_printf(connection, " Total Nodes: %lu nodes\r\n", nodeCount); - mg_printf(connection, " Internal Nodes: %lu nodes\r\n", internalNodeCount); - mg_printf(connection, " Leaf Nodes: %lu leaves\r\n", leafNodeCount); + mg_printf(connection, " Total Nodes: %10.lu nodes\r\n", nodeCount); + mg_printf(connection, " Internal Nodes: %10.lu nodes (%5.2f%%)\r\n", + internalNodeCount, ((float)internalNodeCount/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Leaf Nodes: %10.lu nodes (%5.2f%%)\r\n", + leafNodeCount, ((float)leafNodeCount/(float)nodeCount) * AS_PERCENT); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "VoxelNode Children Encoding Statistics...\r\n"); + mg_printf(connection, " Single or No Children: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getSingleChildrenCount(), ((float)VoxelNode::getSingleChildrenCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getTwoChildrenOffsetCount(), + ((float)VoxelNode::getTwoChildrenOffsetCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Two Children as External: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getTwoChildrenExternalCount(), + ((float)VoxelNode::getTwoChildrenExternalCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getThreeChildrenOffsetCount(), + ((float)VoxelNode::getThreeChildrenOffsetCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Three Children as External: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getThreeChildrenExternalCount(), + ((float)VoxelNode::getThreeChildrenExternalCount()/(float)nodeCount) * AS_PERCENT); + mg_printf(connection, " Children as External Array: %10.llu nodes (%5.2f%%)\r\n", + VoxelNode::getExternalChildrenCount(), + ((float)VoxelNode::getExternalChildrenCount()/(float)nodeCount) * AS_PERCENT); + + uint64_t checkSum = VoxelNode::getSingleChildrenCount() + + VoxelNode::getTwoChildrenOffsetCount() + VoxelNode::getTwoChildrenExternalCount() + + VoxelNode::getThreeChildrenOffsetCount() + VoxelNode::getThreeChildrenExternalCount() + + VoxelNode::getExternalChildrenCount(); + + mg_printf(connection, "%s", " ----------------\r\n"); + mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum); + mg_printf(connection, " Expected: %10.lu nodes\r\n", nodeCount); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "VoxelNode Children Population Statistics...\r\n"); + checkSum = 0; + for (int i=0; i <= NUMBER_OF_CHILDREN; i++) { + checkSum += VoxelNode::getChildrenCount(i); + mg_printf(connection, " Nodes with %d children: %10.llu nodes (%5.2f%%)\r\n", i, + VoxelNode::getChildrenCount(i), ((float)VoxelNode::getChildrenCount(i)/(float)nodeCount) * AS_PERCENT); + } + mg_printf(connection, "%s", " ----------------\r\n"); + mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum); + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "In other news....\r\n"); + mg_printf(connection, "could store 4 children internally: %10.llu nodes\r\n", + VoxelNode::getCouldStoreFourChildrenInternally()); + mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n", + VoxelNode::getCouldNotStoreFourChildrenInternally()); return 1; } else { @@ -373,7 +431,7 @@ void VoxelServer::run() { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); - NodeList::getInstance()->sendDomainServerCheckIn(_uuid.toRfc4122().constData()); + NodeList::getInstance()->sendDomainServerCheckIn(); } if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) && @@ -384,12 +442,18 @@ void VoxelServer::run() { if (packetData[0] == PACKET_TYPE_HEAD_DATA) { // If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we // need to make sure we have it in our nodeList. - uint16_t nodeID = 0; - unpackNodeId(packetData + numBytesPacketHeader, &nodeID); - Node* node = NodeList::getInstance()->addOrUpdateNode(&senderAddress, - &senderAddress, - NODE_TYPE_AGENT, - nodeID); + QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader, + NUM_BYTES_RFC4122_UUID)); + + Node* node = NodeList::getInstance()->addOrUpdateNode(nodeUUID, + NODE_TYPE_AGENT, + &senderAddress, + &senderAddress); + + // temp activation of public socket before server ping/reply is setup + if (!node->getActiveSocket()) { + node->activatePublicSocket(); + } NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength); @@ -398,8 +462,10 @@ void VoxelServer::run() { nodeData->initializeVoxelSendThread(this); } - } else if (packetData[0] == PACKET_TYPE_PING) { - // If the packet is a ping, let processNodeData handle it. + } else if (packetData[0] == PACKET_TYPE_PING + || packetData[0] == PACKET_TYPE_DOMAIN + || packetData[0] == PACKET_TYPE_STUN_RESPONSE) { + // let processNodeData handle it. NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); } else if (packetData[0] == PACKET_TYPE_DOMAIN) { NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength); diff --git a/libraries/voxels/src/AABox.cpp b/libraries/voxels/src/AABox.cpp index 6a9dd68082..50b0958ae4 100644 --- a/libraries/voxels/src/AABox.cpp +++ b/libraries/voxels/src/AABox.cpp @@ -13,83 +13,66 @@ #include "AABox.h" #include "GeometryUtil.h" -AABox::AABox(const glm::vec3& corner, float size) : _corner(corner), _size(size, size, size), _topFarLeft(_corner + _size) -{ +AABox::AABox(const glm::vec3& corner, float size) : + _corner(corner), _scale(size) { }; -AABox::AABox(const glm::vec3& corner, float x, float y, float z) : _corner(corner), _size(x, y, z), _topFarLeft(_corner + _size) -{ +AABox::AABox() : _corner(0,0,0), _scale(0) { }; -AABox::AABox(const glm::vec3& corner, const glm::vec3& size) : _corner(corner), _size(size), _topFarLeft(_corner + _size) -{ -}; +glm::vec3 AABox::calcCenter() const { + glm::vec3 center(_corner); + center += (glm::vec3(_scale, _scale, _scale) * 0.5f); + return center; +} -AABox::AABox() : _corner(0,0,0), _size(0,0,0), _topFarLeft(0,0,0) -{ +glm::vec3 AABox::calcTopFarLeft() const { + glm::vec3 topFarLeft(_corner); + topFarLeft += glm::vec3(_scale, _scale, _scale); + return topFarLeft; }; - void AABox::scale(float scale) { _corner = _corner * scale; - _size = _size * scale; - _center = _center * scale; - _topFarLeft = _topFarLeft * scale; + _scale = _scale * scale; } - glm::vec3 AABox::getVertex(BoxVertex vertex) const { switch (vertex) { case BOTTOM_LEFT_NEAR: - return _corner + glm::vec3(_size.x, 0, 0); + return _corner + glm::vec3(_scale, 0, 0); case BOTTOM_RIGHT_NEAR: return _corner; case TOP_RIGHT_NEAR: - return _corner + glm::vec3(0, _size.y, 0); + return _corner + glm::vec3(0, _scale, 0); case TOP_LEFT_NEAR: - return _corner + glm::vec3(_size.x, _size.y, 0); + return _corner + glm::vec3(_scale, _scale, 0); case BOTTOM_LEFT_FAR: - return _corner + glm::vec3(_size.x, 0, _size.z); + return _corner + glm::vec3(_scale, 0, _scale); case BOTTOM_RIGHT_FAR: - return _corner + glm::vec3(0, 0, _size.z); + return _corner + glm::vec3(0, 0, _scale); case TOP_RIGHT_FAR: - return _corner + glm::vec3(0, _size.y, _size.z); + return _corner + glm::vec3(0, _scale, _scale); case TOP_LEFT_FAR: - return _corner + _size; + return _corner + glm::vec3(_scale, _scale, _scale); } } -void AABox::setBox(const glm::vec3& corner, const glm::vec3& size) { +void AABox::setBox(const glm::vec3& corner, float scale) { _corner = corner; - _size = size; - - // In the event that the caller gave us negative sizes, fix things up to be reasonable - if (_size.x < 0.0) { - _size.x = -size.x; - _corner.x -= _size.x; - } - if (_size.y < 0.0) { - _size.y = -size.y; - _corner.y -= _size.y; - } - if (_size.z < 0.0) { - _size.z = -size.z; - _corner.z -= _size.z; - } - _center = _corner + (_size * 0.5f); - _topFarLeft = _corner + _size; + _scale = scale; } glm::vec3 AABox::getVertexP(const glm::vec3& normal) const { glm::vec3 result = _corner; if (normal.x > 0) { - result.x += _size.x; + result.x += _scale; } if (normal.y > 0) { - result.y += _size.y; + result.y += _scale; } if (normal.z > 0) { - result.z += _size.z; + result.z += _scale; } return result; } @@ -98,15 +81,15 @@ glm::vec3 AABox::getVertexN(const glm::vec3& normal) const { glm::vec3 result = _corner; if (normal.x < 0) { - result.x += _size.x; + result.x += _scale; } if (normal.y < 0) { - result.y += _size.y; + result.y += _scale; } if (normal.z < 0) { - result.z += _size.z; + result.z += _scale; } return result; @@ -118,9 +101,9 @@ static bool isWithin(float value, float corner, float size) { } bool AABox::contains(const glm::vec3& point) const { - return isWithin(point.x, _corner.x, _size.x) && - isWithin(point.y, _corner.y, _size.y) && - isWithin(point.z, _corner.z, _size.z); + return isWithin(point.x, _corner.x, _scale) && + isWithin(point.y, _corner.y, _scale) && + isWithin(point.z, _corner.z, _scale); } bool AABox::contains(const AABox& otherBox) const { @@ -140,9 +123,9 @@ static bool isWithinExpanded(float value, float corner, float size, float expans } bool AABox::expandedContains(const glm::vec3& point, float expansion) const { - return isWithinExpanded(point.x, _corner.x, _size.x, expansion) && - isWithinExpanded(point.y, _corner.y, _size.y, expansion) && - isWithinExpanded(point.z, _corner.z, _size.z, expansion); + return isWithinExpanded(point.x, _corner.x, _scale, expansion) && + isWithinExpanded(point.y, _corner.y, _scale, expansion) && + isWithinExpanded(point.z, _corner.z, _scale, expansion); } // finds the intersection between a ray and the facing plane on one axis @@ -164,7 +147,7 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e } // check each axis glm::vec3 expandedCorner = _corner - glm::vec3(expansion, expansion, expansion); - glm::vec3 expandedSize = _size + glm::vec3(expansion, expansion, expansion) * 2.0f; + glm::vec3 expandedSize = glm::vec3(_scale, _scale, _scale) + glm::vec3(expansion, expansion, expansion) * 2.0f; glm::vec3 direction = end - start; float axisDistance; return (findIntersection(start.x, direction.x, expandedCorner.x, expandedSize.x, axisDistance) && @@ -189,23 +172,23 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct } // check each axis float axisDistance; - if ((findIntersection(origin.x, direction.x, _corner.x, _size.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) && - isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) { + if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; return true; } - if ((findIntersection(origin.y, direction.y, _corner.y, _size.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x) && - isWithin(origin.z + axisDistance*direction.z, _corner.z, _size.z))) { + if ((findIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; return true; } - if ((findIntersection(origin.z, direction.z, _corner.z, _size.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance*direction.y, _corner.y, _size.y) && - isWithin(origin.x + axisDistance*direction.x, _corner.x, _size.x))) { + if ((findIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { distance = axisDistance; face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; return true; @@ -268,27 +251,27 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) con switch (face) { case MIN_X_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x, _corner.y + _size.y, _corner.z + _size.z)); + glm::vec3(_corner.x, _corner.y + _scale, _corner.z + _scale)); case MAX_X_FACE: - return glm::clamp(point, glm::vec3(_corner.x + _size.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x + _scale, _corner.y, _corner.z), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); case MIN_Y_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y, _corner.z + _size.z)); + glm::vec3(_corner.x + _scale, _corner.y, _corner.z + _scale)); case MAX_Y_FACE: - return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _size.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x, _corner.y + _scale, _corner.z), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); case MIN_Z_FACE: return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z)); + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z)); case MAX_Z_FACE: - return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _size.z), - glm::vec3(_corner.x + _size.x, _corner.y + _size.y, _corner.z + _size.z)); + return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _scale), + glm::vec3(_corner.x + _scale, _corner.y + _scale, _corner.z + _scale)); } } @@ -338,7 +321,7 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& glm::vec4 thirdAxisMaxPlane = getPlane((BoxFace)(thirdAxis * 2 + 1)); glm::vec4 offset = glm::vec4(0.0f, 0.0f, 0.0f, - glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), _size) * 0.5f); + glm::dot(glm::vec3(secondAxisMaxPlane + thirdAxisMaxPlane), glm::vec3(_scale, _scale, _scale)) * 0.5f); glm::vec4 diagonals[] = { secondAxisMinPlane + thirdAxisMaxPlane + offset, secondAxisMaxPlane + thirdAxisMaxPlane + offset }; @@ -362,11 +345,11 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& glm::vec4 AABox::getPlane(BoxFace face) const { switch (face) { case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x); - case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _size.x); + case MAX_X_FACE: return glm::vec4(1.0f, 0.0f, 0.0f, -_corner.x - _scale); case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y); - case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _size.y); + case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _scale); case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z); - case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _size.z); + case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _scale); } } diff --git a/libraries/voxels/src/AABox.h b/libraries/voxels/src/AABox.h index 7bb203aaf4..aec0fff450 100644 --- a/libraries/voxels/src/AABox.h +++ b/libraries/voxels/src/AABox.h @@ -36,19 +36,14 @@ enum BoxVertex { const int FACE_COUNT = 6; -class AABox -{ +class AABox { public: - AABox(const glm::vec3& corner, float size); - AABox(const glm::vec3& corner, float x, float y, float z); - AABox(const glm::vec3& corner, const glm::vec3& size); AABox(); ~AABox() {}; - void setBox(const glm::vec3& corner, float x, float y, float z) { setBox(corner,glm::vec3(x,y,z)); }; - void setBox(const glm::vec3& corner, const glm::vec3& size); + void setBox(const glm::vec3& corner, float scale); // for use in frustum computations glm::vec3 getVertexP(const glm::vec3& normal) const; @@ -57,9 +52,10 @@ public: void scale(float scale); const glm::vec3& getCorner() const { return _corner; }; - const glm::vec3& getSize() const { return _size; }; - const glm::vec3& getCenter() const { return _center; }; - const glm::vec3& getTopFarLeft() const { return _topFarLeft; }; + float getScale() const { return _scale; } + + glm::vec3 calcCenter() const; + glm::vec3 calcTopFarLeft() const; glm::vec3 getVertex(BoxVertex vertex) const; @@ -72,7 +68,6 @@ public: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const; private: - glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; glm::vec4 getPlane(BoxFace face) const; @@ -80,10 +75,7 @@ private: static BoxFace getOppositeFace(BoxFace face); glm::vec3 _corner; - glm::vec3 _center; - glm::vec3 _size; - glm::vec3 _topFarLeft; + float _scale; }; - #endif diff --git a/libraries/voxels/src/JurisdictionListener.cpp b/libraries/voxels/src/JurisdictionListener.cpp index ea8bea3a2d..0a14d0809e 100644 --- a/libraries/voxels/src/JurisdictionListener.cpp +++ b/libraries/voxels/src/JurisdictionListener.cpp @@ -33,7 +33,7 @@ void JurisdictionListener::nodeAdded(Node* node) { } void JurisdictionListener::nodeKilled(Node* node) { - _jurisdictions.erase(_jurisdictions.find(node->getNodeID())); + _jurisdictions.erase(_jurisdictions.find(node->getUUID())); } bool JurisdictionListener::queueJurisdictionRequest() { @@ -66,10 +66,10 @@ void JurisdictionListener::processPacket(sockaddr& senderAddress, unsigned char* if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); if (node) { - uint16_t nodeID = node->getNodeID(); + QUuid nodeUUID = node->getUUID(); JurisdictionMap map; map.unpackFromMessage(packetData, packetLength); - _jurisdictions[nodeID] = map; + _jurisdictions[nodeUUID] = map; } } } diff --git a/libraries/voxels/src/JurisdictionMap.cpp b/libraries/voxels/src/JurisdictionMap.cpp index 94e589c801..3da1467f46 100644 --- a/libraries/voxels/src/JurisdictionMap.cpp +++ b/libraries/voxels/src/JurisdictionMap.cpp @@ -132,7 +132,7 @@ void JurisdictionMap::init(unsigned char* rootOctalCode, const std::vector #include #include + #include +#include class JurisdictionMap { public: @@ -41,7 +43,7 @@ public: JurisdictionMap(const char* rootHextString, const char* endNodesHextString); ~JurisdictionMap(); - Area isMyJurisdiction(unsigned char* nodeOctalCode, int childIndex) const; + Area isMyJurisdiction(const unsigned char* nodeOctalCode, int childIndex) const; bool writeToFile(const char* filename); bool readFromFile(const char* filename); @@ -71,7 +73,7 @@ private: /// Map between node IDs and their reported JurisdictionMap. Typically used by classes that need to know which nodes are /// managing which jurisdictions. -typedef std::map NodeToJurisdictionMap; +typedef std::map NodeToJurisdictionMap; #endif /* defined(__hifi__JurisdictionMap__) */ diff --git a/libraries/voxels/src/JurisdictionSender.cpp b/libraries/voxels/src/JurisdictionSender.cpp index df48c27c47..150d04440f 100644 --- a/libraries/voxels/src/JurisdictionSender.cpp +++ b/libraries/voxels/src/JurisdictionSender.cpp @@ -27,9 +27,9 @@ void JurisdictionSender::processPacket(sockaddr& senderAddress, unsigned char* if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) { Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); if (node) { - uint16_t nodeID = node->getNodeID(); + QUuid nodeUUID = node->getUUID(); lock(); - _nodesRequestingJurisdictions.insert(nodeID); + _nodesRequestingJurisdictions.insert(nodeUUID); unlock(); } } @@ -52,11 +52,11 @@ bool JurisdictionSender::process() { } int nodeCount = 0; - for (std::set::iterator nodeIterator = _nodesRequestingJurisdictions.begin(); + for (std::set::iterator nodeIterator = _nodesRequestingJurisdictions.begin(); nodeIterator != _nodesRequestingJurisdictions.end(); nodeIterator++) { - uint16_t nodeID = *nodeIterator; - Node* node = NodeList::getInstance()->nodeWithID(nodeID); + QUuid nodeUUID = *nodeIterator; + Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); if (node->getActiveSocket() != NULL) { sockaddr* nodeAddress = node->getActiveSocket(); diff --git a/libraries/voxels/src/JurisdictionSender.h b/libraries/voxels/src/JurisdictionSender.h index 34f5a9f06f..9678eb15e2 100644 --- a/libraries/voxels/src/JurisdictionSender.h +++ b/libraries/voxels/src/JurisdictionSender.h @@ -35,6 +35,6 @@ protected: private: JurisdictionMap* _jurisdictionMap; - std::set _nodesRequestingJurisdictions; + std::set _nodesRequestingJurisdictions; }; #endif // __shared__JurisdictionSender__ diff --git a/libraries/voxels/src/ViewFrustum.cpp b/libraries/voxels/src/ViewFrustum.cpp index e0cfa7cd97..05380c9e4a 100644 --- a/libraries/voxels/src/ViewFrustum.cpp +++ b/libraries/voxels/src/ViewFrustum.cpp @@ -543,7 +543,8 @@ const int hullVertexLookup[MAX_POSSIBLE_COMBINATIONS][MAX_PROJECTED_POLYGON_VERT VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { const glm::vec3& bottomNearRight = box.getCorner(); - const glm::vec3& topFarLeft = box.getTopFarLeft(); + glm::vec3 topFarLeft = box.calcTopFarLeft(); + int lookUp = ((_position.x < bottomNearRight.x) ) // 1 = right | compute 6-bit + ((_position.x > topFarLeft.x ) << 1) // 2 = left | code to + ((_position.y < bottomNearRight.y) << 2) // 4 = bottom | classify camera @@ -596,7 +597,7 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { ***/ } // set the distance from our camera position, to the closest vertex - float distance = glm::distance(getPosition(), box.getCenter()); + float distance = glm::distance(getPosition(), box.calcCenter()); projectedPolygon.setDistance(distance); projectedPolygon.setAnyInView(anyPointsInView); projectedPolygon.setAllInView(allPointsInView); @@ -609,9 +610,9 @@ VoxelProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const { // axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for // squares and square-roots. Just compares. glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const { - const glm::vec3& center = box.getCenter(); const glm::vec3& bottomNearRight = box.getCorner(); - const glm::vec3& topFarLeft = box.getTopFarLeft(); + glm::vec3 center = box.calcCenter(); + glm::vec3 topFarLeft = box.calcTopFarLeft(); glm::vec3 furthestPoint; if (_position.x < center.x) { diff --git a/libraries/voxels/src/ViewFrustum.h b/libraries/voxels/src/ViewFrustum.h index 803e52908e..9a3c69ca7c 100644 --- a/libraries/voxels/src/ViewFrustum.h +++ b/libraries/voxels/src/ViewFrustum.h @@ -97,7 +97,6 @@ public: glm::vec3 getFurthestPointFromCamera(const AABox& box) const; private: - // Used for keyhole calculations ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; ViewFrustum::location sphereInKeyhole(const glm::vec3& center, float radius) const; diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index 3c435ca0af..11e4600f6b 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -17,8 +17,8 @@ #include "VoxelEditPacketSender.h" -EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssize_t length, uint16_t nodeID) { - _nodeID = nodeID; +EditPacketBuffer::EditPacketBuffer(PACKET_TYPE type, unsigned char* buffer, ssize_t length, QUuid nodeUUID) { + _nodeUUID = nodeUUID; _currentType = type; _currentSize = length; memcpy(_currentBuffer, buffer, length); @@ -89,8 +89,13 @@ bool VoxelEditPacketSender::voxelServersExist() const { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER - if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) { - return true; + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + if (node->getActiveSocket()) { + return true; + } else { + // we don't have an active socket for this node, ping it + nodeList->pingPublicAndLocalSocketsForInactiveNode(&(*node)); + } } } return false; @@ -98,14 +103,19 @@ bool VoxelEditPacketSender::voxelServersExist() const { // This method is called when the edit packet layer has determined that it has a fully formed packet destined for // a known nodeID. However, we also want to handle the case where the -void VoxelEditPacketSender::queuePacketToNode(uint16_t nodeID, unsigned char* buffer, ssize_t length) { +void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) { NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER - if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER && - ((node->getNodeID() == nodeID) || (nodeID == (uint16_t)UNKNOWN_NODE_ID)) ) { - sockaddr* nodeAddress = node->getActiveSocket(); - queuePacketForSending(*nodeAddress, buffer, length); + if (node->getType() == NODE_TYPE_VOXEL_SERVER && + ((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) { + if (node->getActiveSocket()) { + sockaddr* nodeAddress = node->getActiveSocket(); + queuePacketForSending(*nodeAddress, buffer, length); + } else { + // we don't have an active socket for this node, ping it + nodeList->pingPublicAndLocalSocketsForInactiveNode(&(*node)); + } } } } @@ -170,14 +180,14 @@ void VoxelEditPacketSender::queuePacketToNodes(unsigned char* buffer, ssize_t le for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) { - uint16_t nodeID = node->getNodeID(); + QUuid nodeUUID = node->getUUID(); bool isMyJurisdiction = true; // we need to get the jurisdiction for this // here we need to get the "pending packet" for this server - const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeID]; + const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeUUID]; isMyJurisdiction = (map.isMyJurisdiction(octCode, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN); if (isMyJurisdiction) { - queuePacketToNode(nodeID, buffer, length); + queuePacketToNode(nodeUUID, buffer, length); } } } @@ -216,18 +226,18 @@ void VoxelEditPacketSender::queueVoxelEditMessage(PACKET_TYPE type, unsigned cha for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) { - uint16_t nodeID = node->getNodeID(); + QUuid nodeUUID = node->getUUID(); bool isMyJurisdiction = true; if (_voxelServerJurisdictions) { // we need to get the jurisdiction for this // here we need to get the "pending packet" for this server - const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeID]; + const JurisdictionMap& map = (*_voxelServerJurisdictions)[nodeUUID]; isMyJurisdiction = (map.isMyJurisdiction(codeColorBuffer, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN); } if (isMyJurisdiction) { - EditPacketBuffer& packetBuffer = _pendingEditPackets[nodeID]; - packetBuffer._nodeID = nodeID; + EditPacketBuffer& packetBuffer = _pendingEditPackets[nodeUUID]; + packetBuffer._nodeUUID = nodeUUID; // If we're switching type, then we send the last one and start over if ((type != packetBuffer._currentType && packetBuffer._currentSize > 0) || @@ -255,14 +265,14 @@ void VoxelEditPacketSender::releaseQueuedMessages() { if (!voxelServersExist()) { _releaseQueuedMessagesPending = true; } else { - for (std::map::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) { + for (std::map::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) { releaseQueuedPacket(i->second); } } } void VoxelEditPacketSender::releaseQueuedPacket(EditPacketBuffer& packetBuffer) { - queuePacketToNode(packetBuffer._nodeID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize); + queuePacketToNode(packetBuffer._nodeUUID, &packetBuffer._currentBuffer[0], packetBuffer._currentSize); packetBuffer._currentSize = 0; packetBuffer._currentType = PACKET_TYPE_UNKNOWN; } diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index 97853af74a..bb54009d1a 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -19,9 +19,9 @@ /// Used for construction of edit voxel packets class EditPacketBuffer { public: - EditPacketBuffer() { _currentSize = 0; _currentType = PACKET_TYPE_UNKNOWN; _nodeID = UNKNOWN_NODE_ID; } - EditPacketBuffer(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length, uint16_t nodeID = UNKNOWN_NODE_ID); - uint16_t _nodeID; + EditPacketBuffer() : _nodeUUID(), _currentType(PACKET_TYPE_UNKNOWN), _currentSize(0) { } + EditPacketBuffer(PACKET_TYPE type, unsigned char* codeColorBuffer, ssize_t length, const QUuid nodeUUID = QUuid()); + QUuid _nodeUUID; PACKET_TYPE _currentType; unsigned char _currentBuffer[MAX_PACKET_SIZE]; ssize_t _currentSize; @@ -83,18 +83,20 @@ public: // the default number of pending messages we will store if no voxel servers are available static const int DEFAULT_MAX_PENDING_MESSAGES; + + bool voxelServersExist() const; private: bool _shouldSend; - void queuePacketToNode(uint16_t nodeID, unsigned char* buffer, ssize_t length); + void queuePacketToNode(const QUuid& nodeID, unsigned char* buffer, ssize_t length); void queuePacketToNodes(unsigned char* buffer, ssize_t length); void initializePacket(EditPacketBuffer& packetBuffer, PACKET_TYPE type); void releaseQueuedPacket(EditPacketBuffer& packetBuffer); // releases specific queued packet - bool voxelServersExist() const; + void processPreServerExistsPackets(); // These are packets which are destined from know servers but haven't been released because they're still too small - std::map _pendingEditPackets; + std::map _pendingEditPackets; // These are packets that are waiting to be processed because we don't yet know if there are voxel servers int _maxPendingMessages; diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index ad83b5a9cf..e850be7ad8 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "AABox.h" #include "OctalCode.h" @@ -23,19 +24,36 @@ uint64_t VoxelNode::_voxelMemoryUsage = 0; uint64_t VoxelNode::_octcodeMemoryUsage = 0; +uint64_t VoxelNode::_externalChildrenMemoryUsage = 0; +uint64_t VoxelNode::_voxelNodeCount = 0; +uint64_t VoxelNode::_voxelNodeLeafCount = 0; VoxelNode::VoxelNode() { unsigned char* rootCode = new unsigned char[1]; *rootCode = 0; init(rootCode); + + _voxelNodeCount++; + _voxelNodeLeafCount++; // all nodes start as leaf nodes } VoxelNode::VoxelNode(unsigned char * octalCode) { init(octalCode); + _voxelNodeCount++; + _voxelNodeLeafCount++; // all nodes start as leaf nodes } void VoxelNode::init(unsigned char * octalCode) { - _octalCode = octalCode; + int octalCodeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); + if (octalCodeLength > sizeof(_octalCode)) { + _octalCode.pointer = octalCode; + _octcodePointer = true; + _octcodeMemoryUsage += octalCodeLength; + } else { + _octcodePointer = false; + memcpy(_octalCode.buffer, octalCode, octalCodeLength); + delete[] octalCode; + } #ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color _falseColored = false; // assume true color @@ -43,41 +61,61 @@ void VoxelNode::init(unsigned char * octalCode) { #endif _trueColor[0] = _trueColor[1] = _trueColor[2] = _trueColor[3] = 0; _density = 0.0f; + + // set up the _children union + _childBitmask = 0; + _childrenExternal = false; + _children.external = NULL; + _singleChildrenCount++; + _childrenCount[0]++; // default pointers to child nodes to NULL +#ifdef HAS_AUDIT_CHILDREN for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - _children[i] = NULL; + _childrenArray[i] = NULL; } - _childCount = 0; - _subtreeNodeCount = 1; // that's me - _subtreeLeafNodeCount = 0; // that's me +#endif // def HAS_AUDIT_CHILDREN - _glBufferIndex = GLBUFFER_INDEX_UNKNOWN; - _voxelSystem = NULL; + + _unknownBufferIndex = true; + setBufferIndex(GLBUFFER_INDEX_UNKNOWN); + setVoxelSystem(NULL); _isDirty = true; _shouldRender = false; - _sourceID = UNKNOWN_NODE_ID; + _sourceUUIDKey = 0; calculateAABox(); markWithChangedTime(); _voxelMemoryUsage += sizeof(VoxelNode); - _octcodeMemoryUsage += bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_octalCode)); } VoxelNode::~VoxelNode() { notifyDeleteHooks(); _voxelMemoryUsage -= sizeof(VoxelNode); - _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_octalCode)); - delete[] _octalCode; + _voxelNodeCount--; + if (isLeaf()) { + _voxelNodeLeafCount--; + } + + if (_octcodePointer) { + _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); + delete[] _octalCode.pointer; + } // delete all of this node's children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { - delete _children[i]; + VoxelNode* childAt = getChildAtIndex(i); + if (childAt) { + delete childAt; + setChildAtIndex(i, NULL); } } + + // at this point, we should have no children, but we need to drop ourselves from population counts + _singleChildrenCount--; + _childrenCount[0]--; } void VoxelNode::markWithChangedTime() { @@ -97,26 +135,87 @@ void VoxelNode::handleSubtreeChanged(VoxelTree* myTree) { setColorFromAverageOfChildren(); } - recalculateSubTreeNodeCount(); markWithChangedTime(); } -void VoxelNode::recalculateSubTreeNodeCount() { - // Assuming the tree below me as changed, I need to recalculate my node count - _subtreeNodeCount = 1; // that's me - if (isLeaf()) { - _subtreeLeafNodeCount = 1; - } else { - _subtreeLeafNodeCount = 0; - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { - _subtreeNodeCount += _children[i]->_subtreeNodeCount; - _subtreeLeafNodeCount += _children[i]->_subtreeLeafNodeCount; - } +const uint8_t INDEX_FOR_NULL = 0; +uint8_t VoxelNode::_nextIndex = INDEX_FOR_NULL + 1; // start at 1, 0 is reserved for NULL +std::map VoxelNode::_mapVoxelSystemPointersToIndex; +std::map VoxelNode::_mapIndexToVoxelSystemPointers; + +VoxelSystem* VoxelNode::getVoxelSystem() const { + if (_voxelSystemIndex > INDEX_FOR_NULL) { + if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) { + return _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; } } + return NULL; } +void VoxelNode::setVoxelSystem(VoxelSystem* voxelSystem) { + if (voxelSystem == NULL) { + _voxelSystemIndex = INDEX_FOR_NULL; + } else { + uint8_t index; + if (_mapVoxelSystemPointersToIndex.end() != _mapVoxelSystemPointersToIndex.find(voxelSystem)) { + index = _mapVoxelSystemPointersToIndex[voxelSystem]; + } else { + index = _nextIndex; + _nextIndex++; + _mapVoxelSystemPointersToIndex[voxelSystem] = index; + _mapIndexToVoxelSystemPointers[index] = voxelSystem; + } + _voxelSystemIndex = index; + } +} + +const uint16_t KEY_FOR_NULL = 0; +uint16_t VoxelNode::_nextUUIDKey = KEY_FOR_NULL + 1; // start at 1, 0 is reserved for NULL +std::map VoxelNode::_mapSourceUUIDsToKeys; +std::map VoxelNode::_mapKeysToSourceUUIDs; + +void VoxelNode::setSourceUUID(const QUuid& sourceUUID) { + uint16_t key; + QString sourceUUIDString = sourceUUID.toString(); + if (_mapSourceUUIDsToKeys.end() != _mapSourceUUIDsToKeys.find(sourceUUIDString)) { + key = _mapSourceUUIDsToKeys[sourceUUIDString]; + } else { + key = _nextUUIDKey; + _nextUUIDKey++; + _mapSourceUUIDsToKeys[sourceUUIDString] = key; + _mapKeysToSourceUUIDs[key] = sourceUUIDString; + } + _sourceUUIDKey = key; +} + +QUuid VoxelNode::getSourceUUID() const { + if (_sourceUUIDKey > KEY_FOR_NULL) { + if (_mapKeysToSourceUUIDs.end() != _mapKeysToSourceUUIDs.find(_sourceUUIDKey)) { + return QUuid(_mapKeysToSourceUUIDs[_sourceUUIDKey]); + } + } + return QUuid(); +} + +bool VoxelNode::matchesSourceUUID(const QUuid& sourceUUID) const { + if (_sourceUUIDKey > KEY_FOR_NULL) { + if (_mapKeysToSourceUUIDs.end() != _mapKeysToSourceUUIDs.find(_sourceUUIDKey)) { + return QUuid(_mapKeysToSourceUUIDs[_sourceUUIDKey]) == sourceUUID; + } + } + return sourceUUID.isNull(); +} + +uint16_t VoxelNode::getSourceNodeUUIDKey(const QUuid& sourceUUID) { + uint16_t key = KEY_FOR_NULL; + QString sourceUUIDString = sourceUUID.toString(); + if (_mapSourceUUIDsToKeys.end() != _mapSourceUUIDsToKeys.find(sourceUUIDString)) { + key = _mapSourceUUIDsToKeys[sourceUUIDString]; + } + return key; +} + + void VoxelNode::setShouldRender(bool shouldRender) { // if shouldRender is changing, then consider ourselves dirty @@ -128,51 +227,749 @@ void VoxelNode::setShouldRender(bool shouldRender) { } void VoxelNode::calculateAABox() { - glm::vec3 corner; - glm::vec3 size; // copy corner into box - copyFirstVertexForCode(_octalCode,(float*)&corner); + copyFirstVertexForCode(getOctalCode(),(float*)&corner); // this tells you the "size" of the voxel - float voxelScale = 1 / powf(2, *_octalCode); - size = glm::vec3(voxelScale,voxelScale,voxelScale); - - _box.setBox(corner,size); + float voxelScale = 1 / powf(2, numberOfThreeBitSectionsInCode(getOctalCode())); + _box.setBox(corner,voxelScale); } void VoxelNode::deleteChildAtIndex(int childIndex) { - if (_children[childIndex]) { - delete _children[childIndex]; - _children[childIndex] = NULL; + VoxelNode* childAt = getChildAtIndex(childIndex); + if (childAt) { + delete childAt; + setChildAtIndex(childIndex, NULL); _isDirty = true; - _childCount--; markWithChangedTime(); + + // after deleting the child, check to see if we're a leaf + if (isLeaf()) { + _voxelNodeLeafCount++; + } } +#ifdef HAS_AUDIT_CHILDREN + auditChildren("deleteChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN } // does not delete the node! VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) { - VoxelNode* returnedChild = _children[childIndex]; - if (_children[childIndex]) { - _children[childIndex] = NULL; + VoxelNode* returnedChild = getChildAtIndex(childIndex); + if (returnedChild) { + setChildAtIndex(childIndex, NULL); _isDirty = true; - _childCount--; markWithChangedTime(); + + // after removing the child, check to see if we're a leaf + if (isLeaf()) { + _voxelNodeLeafCount++; + } } + +#ifdef HAS_AUDIT_CHILDREN + auditChildren("removeChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN return returnedChild; } +#ifdef HAS_AUDIT_CHILDREN +void VoxelNode::auditChildren(const char* label) const { + bool auditFailed = false; + for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { + VoxelNode* testChildNew = getChildAtIndex(childIndex); + VoxelNode* testChildOld = _childrenArray[childIndex]; + + if (testChildNew != testChildOld) { + auditFailed = true; + } + } + + if (auditFailed) { + qDebug("%s... auditChildren() %s <<<< \n", label, (auditFailed ? "FAILED" : "PASSED")); + qDebug(" _childrenExternal=%s\n", debug::valueOf(_childrenExternal)); + qDebug(" childCount=%d\n", getChildCount()); + qDebug(" _childBitmask="); + outputBits(_childBitmask); + + + for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { + VoxelNode* testChildNew = getChildAtIndex(childIndex); + VoxelNode* testChildOld = _childrenArray[childIndex]; + + qDebug("child at index %d... testChildOld=%p testChildNew=%p %s \n", + childIndex, testChildOld, testChildNew , + ((testChildNew != testChildOld) ? " DOES NOT MATCH <<<< BAD <<<<" : " - OK ") + ); + } + qDebug("%s... auditChildren() <<<< DONE <<<< \n", label); + } +} +#endif // def HAS_AUDIT_CHILDREN + + +uint64_t VoxelNode::_getChildAtIndexTime = 0; +uint64_t VoxelNode::_getChildAtIndexCalls = 0; +uint64_t VoxelNode::_setChildAtIndexTime = 0; +uint64_t VoxelNode::_setChildAtIndexCalls = 0; + +uint64_t VoxelNode::_singleChildrenCount = 0; +uint64_t VoxelNode::_twoChildrenOffsetCount = 0; +uint64_t VoxelNode::_twoChildrenExternalCount = 0; +uint64_t VoxelNode::_threeChildrenOffsetCount = 0; +uint64_t VoxelNode::_threeChildrenExternalCount = 0; +uint64_t VoxelNode::_externalChildrenCount = 0; +uint64_t VoxelNode::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +uint64_t VoxelNode::_couldStoreFourChildrenInternally = 0; +uint64_t VoxelNode::_couldNotStoreFourChildrenInternally = 0; + +VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const { + PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls); + VoxelNode* result = NULL; + int childCount = getChildCount(); + + const char* caseStr = NULL; + switch (childCount) { + case 0: + caseStr = "0 child case"; + break; + case 1: { + caseStr = "1 child case"; + int indexOne = getNthBit(_childBitmask, 1); + if (indexOne == childIndex) { + result = _children.single; + } + } break; + case 2: { + caseStr = "2 child case"; + int indexOne = getNthBit(_childBitmask, 1); + int indexTwo = getNthBit(_childBitmask, 2); + + if (_childrenExternal) { + if (indexOne == childIndex) { + result = _children.external[0]; + } else if (indexTwo == childIndex) { + result = _children.external[1]; + } + } else { + if (indexOne == childIndex) { + int32_t offset = _children.offsetsTwoChildren[0]; + result = (VoxelNode*)((uint8_t*)this + offset); + } else if (indexTwo == childIndex) { + int32_t offset = _children.offsetsTwoChildren[1]; + result = (VoxelNode*)((uint8_t*)this + offset); + } + } + } break; + case 3: { + caseStr = "3 child case"; + int indexOne = getNthBit(_childBitmask, 1); + int indexTwo = getNthBit(_childBitmask, 2); + int indexThree = getNthBit(_childBitmask, 3); + + if (_childrenExternal) { + if (indexOne == childIndex) { + result = _children.external[0]; + } else if (indexTwo == childIndex) { + result = _children.external[1]; + } else if (indexThree == childIndex) { + result = _children.external[2]; + } else { + } + } else { + int64_t offsetOne, offsetTwo, offsetThree; + decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + + if (indexOne == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetOne); + } else if (indexTwo == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetTwo); + } else if (indexThree == childIndex) { + result = (VoxelNode*)((uint8_t*)this + offsetThree); + } + } + } break; + default: { + caseStr = "default"; + // if we have 4 or more, we know we're in external mode, so we just need to figure out which + // slot in our external array this child is. + if (oneAtBit(_childBitmask, childIndex)) { + childCount = getChildCount(); + for (int ordinal = 1; ordinal <= childCount; ordinal++) { + int index = getNthBit(_childBitmask, ordinal); + if (index == childIndex) { + result = _children.external[ordinal-1]; + break; + } + } + } + } break; + } +#ifdef HAS_AUDIT_CHILDREN + if (result != _childrenArray[childIndex]) { + qDebug("getChildAtIndex() case:%s result<%p> != _childrenArray[childIndex]<%p> <<<<<<<<<< WARNING!!! \n", + caseStr, result,_childrenArray[childIndex]); + } +#endif // def HAS_AUDIT_CHILDREN + return result; +} + +void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + + const int64_t minOffset = std::numeric_limits::min(); + const int64_t maxOffset = std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { + // if previously external, then clean it up... + if (_childrenExternal) { + const int previousChildCount = 2; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + } + + // encode in union + _children.offsetsTwoChildren[0] = offsetOne; + _children.offsetsTwoChildren[1] = offsetTwo; + + _twoChildrenOffsetCount++; + } else { + // encode in array + + // if not previously external, then allocate appropriately + if (!_childrenExternal) { + _childrenExternal = true; + const int newChildCount = 2; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + _children.external = new VoxelNode*[newChildCount]; + } + _children.external[0] = childOne; + _children.external[1] = childTwo; + _twoChildrenExternalCount++; + } +} + +void VoxelNode::retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo) { + // If we previously had an external array, then get the + if (_childrenExternal) { + childOne = _children.external[0]; + childTwo = _children.external[1]; + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + _twoChildrenExternalCount--; + const int newChildCount = 2; + _externalChildrenMemoryUsage -= newChildCount * sizeof(VoxelNode*); + } else { + int64_t offsetOne = _children.offsetsTwoChildren[0]; + int64_t offsetTwo = _children.offsetsTwoChildren[1]; + childOne = (VoxelNode*)((uint8_t*)this + offsetOne); + childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo); + _twoChildrenOffsetCount--; + } +} + +void VoxelNode::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const { + const uint64_t ENCODE_BITS = 21; + const uint64_t ENCODE_MASK = 0xFFFFF; + const uint64_t ENCODE_MASK_SIGN = 0x100000; + + uint64_t offsetEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK; + uint64_t offsetEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK; + uint64_t offsetEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK); + + uint64_t signEncodedOne = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 2)) & ENCODE_MASK_SIGN; + uint64_t signEncodedTwo = (_children.offsetsThreeChildrenEncoded >> (ENCODE_BITS * 1)) & ENCODE_MASK_SIGN; + uint64_t signEncodedThree = (_children.offsetsThreeChildrenEncoded & ENCODE_MASK_SIGN); + + bool oneNegative = signEncodedOne == ENCODE_MASK_SIGN; + bool twoNegative = signEncodedTwo == ENCODE_MASK_SIGN; + bool threeNegative = signEncodedThree == ENCODE_MASK_SIGN; + + offsetOne = oneNegative ? -offsetEncodedOne : offsetEncodedOne; + offsetTwo = twoNegative ? -offsetEncodedTwo : offsetEncodedTwo; + offsetThree = threeNegative ? -offsetEncodedThree : offsetEncodedThree; +} + +void VoxelNode::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) { + const uint64_t ENCODE_BITS = 21; + const uint64_t ENCODE_MASK = 0xFFFFF; + const uint64_t ENCODE_MASK_SIGN = 0x100000; + + uint64_t offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree; + if (offsetOne < 0) { + offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedOne = offsetOne & ENCODE_MASK; + } + offsetEncodedOne = offsetEncodedOne << (ENCODE_BITS * 2); + + if (offsetTwo < 0) { + offsetEncodedTwo = ((-offsetTwo & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedTwo = offsetTwo & ENCODE_MASK; + } + offsetEncodedTwo = offsetEncodedTwo << ENCODE_BITS; + + if (offsetThree < 0) { + offsetEncodedThree = ((-offsetThree & ENCODE_MASK) | ENCODE_MASK_SIGN); + } else { + offsetEncodedThree = offsetThree & ENCODE_MASK; + } + _children.offsetsThreeChildrenEncoded = offsetEncodedOne | offsetEncodedTwo | offsetEncodedThree; +} + +void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; + + const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); + const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetTwo, maxOffset, minOffset) && + isBetween(offsetThree, maxOffset, minOffset)) { + // if previously external, then clean it up... + if (_childrenExternal) { + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + const int previousChildCount = 3; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + } + // encode in union + encodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + _threeChildrenOffsetCount++; + } else { + // encode in array + + // if not previously external, then allocate appropriately + if (!_childrenExternal) { + _childrenExternal = true; + const int newChildCount = 3; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + _children.external = new VoxelNode*[newChildCount]; + } + _children.external[0] = childOne; + _children.external[1] = childTwo; + _children.external[2] = childThree; + _threeChildrenExternalCount++; + } +} + +void VoxelNode::retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree) { + // If we previously had an external array, then get the + if (_childrenExternal) { + childOne = _children.external[0]; + childTwo = _children.external[1]; + childThree = _children.external[2]; + delete[] _children.external; + _children.external = NULL; // probably not needed! + _childrenExternal = false; + _threeChildrenExternalCount--; + _externalChildrenMemoryUsage -= 3 * sizeof(VoxelNode*); + } else { + int64_t offsetOne, offsetTwo, offsetThree; + decodeThreeOffsets(offsetOne, offsetTwo, offsetThree); + + childOne = (VoxelNode*)((uint8_t*)this + offsetOne); + childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo); + childThree = (VoxelNode*)((uint8_t*)this + offsetThree); + _threeChildrenOffsetCount--; + } +} + +void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour) { + int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; + int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; + int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; + int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this; + + const int64_t minOffset = std::numeric_limits::min(); + const int64_t maxOffset = std::numeric_limits::max(); + + if (isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetTwo, maxOffset, minOffset) && + isBetween(offsetThree, maxOffset, minOffset) && + isBetween(offsetFour, maxOffset, minOffset) + ) { + _couldStoreFourChildrenInternally++; + } else { + _couldNotStoreFourChildrenInternally++; + } +} + +void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { + PerformanceWarning warn(false,"setChildAtIndex",false,&_setChildAtIndexTime,&_setChildAtIndexCalls); + + // Here's how we store things... + // If we have 0 or 1 children, then we just store them in the _children.single; + // If we have 2 children, + // then if we can we store them as 32 bit signed offsets from our own this pointer, + // _children.offsetsTwoChildren[0]-[1] + // these are 32 bit offsets + + unsigned char previousChildMask = _childBitmask; + int previousChildCount = getChildCount(); + if (child) { + setAtBit(_childBitmask, childIndex); + } else { + clearAtBit(_childBitmask, childIndex); + } + int newChildCount = getChildCount(); + + // track our population data + if (previousChildCount != newChildCount) { + _childrenCount[previousChildCount]--; + _childrenCount[newChildCount]++; + } + + // If we had 0 children and we still have 0 children, then there is nothing to do. + if (previousChildCount == 0 && newChildCount == 0) { + // nothing to do... + } else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) { + // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, + // then we can just store it in _children.single + _children.single = child; + } else if (previousChildCount == 1 && newChildCount == 0) { + // If we had 1 child, and we've removed our last child, then we can just store NULL in _children.single + _children.single = NULL; + } else if (previousChildCount == 1 && newChildCount == 2) { + // If we had 1 child, and we're adding a second child, then we need to determine + // if we can use offsets to store them + + VoxelNode* childOne; + VoxelNode* childTwo; + + if (getNthBit(previousChildMask, 1) < childIndex) { + childOne = _children.single; + childTwo = child; + } else { + childOne = child; + childTwo = _children.single; + } + + _singleChildrenCount--; + storeTwoChildren(childOne, childTwo); + } else if (previousChildCount == 2 && newChildCount == 1) { + // If we had 2 children, and we're removing one, then we know we can go down to single mode + assert(child == NULL); // this is the only logical case + + int indexTwo = getNthBit(previousChildMask, 2); + bool keepChildOne = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + + retrieveTwoChildren(childOne, childTwo); + + _singleChildrenCount++; + + if (keepChildOne) { + _children.single = childOne; + } else { + _children.single = childTwo; + } + } else if (previousChildCount == 2 && newChildCount == 2) { + // If we had 2 children, and still have 2, then we know we are resetting one of our existing children + + int indexOne = getNthBit(previousChildMask, 1); + bool replaceChildOne = indexOne == childIndex; + + // If we previously had an external array, then just replace the right one... that's easy. + if (_childrenExternal) { + // technically, we could look to see if these are now in the offsets to handle be encoded, but + // we're going to go ahead and keep this as an array. + if (replaceChildOne) { + _children.external[0] = child; + } else { + _children.external[1] = child; + } + } else { + // If we were previously encoded as offsets, then we need to see if we can still encode as offsets + VoxelNode* childOne; + VoxelNode* childTwo; + + if (replaceChildOne) { + childOne = child; + childTwo = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[1]); + } else { + childOne = (VoxelNode*)((uint8_t*)this + _children.offsetsTwoChildren[0]); + childTwo = child; + } + + _twoChildrenOffsetCount--; // will end up one or the other + storeTwoChildren(childOne, childTwo); + } + } else if (previousChildCount == 2 && newChildCount == 3) { + // If we had 2 children, and now have 3, then we know we are going to an external case... + + // First, decode the children... + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveTwoChildren(childOne, childTwo); + + // determine order of the existing children + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + if (childIndex < indexOne) { + childThree = childTwo; + childTwo = childOne; + childOne = child; + } else if (childIndex < indexTwo) { + childThree = childTwo; + childTwo = child; + } else { + childThree = child; + } + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == 3 && newChildCount == 2) { + // If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + bool removeChildOne = indexOne == childIndex; + bool removeChildTwo = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + if (removeChildOne) { + childOne = childTwo; + childTwo = childThree; + } else if (removeChildTwo) { + childTwo = childThree; + } else { + // removing child three, nothing to do. + } + + storeTwoChildren(childOne, childTwo); + } else if (previousChildCount == 3 && newChildCount == 3) { + // If we had 3 children, and now have 3, then we need to determine which item we're replacing... + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + + bool replaceChildOne = indexOne == childIndex; + bool replaceChildTwo = indexTwo == childIndex; + + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + if (replaceChildOne) { + childOne = child; + } else if (replaceChildTwo) { + childTwo = child; + } else { + childThree = child; + } + + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == 3 && newChildCount == 4) { + // If we had 3 children, and now have 4, then we know we are going to an external case... + + // First, decode the children... + VoxelNode* childOne; + VoxelNode* childTwo; + VoxelNode* childThree; + VoxelNode* childFour; + + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); + + // determine order of the existing children + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + int indexThree = getNthBit(previousChildMask, 3); + + if (childIndex < indexOne) { + childFour = childThree; + childThree = childTwo; + childTwo = childOne; + childOne = child; + } else if (childIndex < indexTwo) { + childFour = childThree; + childThree = childTwo; + childTwo = child; + } else if (childIndex < indexThree) { + childFour = childThree; + childThree = child; + } else { + childFour = child; + } + + // now, allocate the external... + _childrenExternal = true; + const int newChildCount = 4; + _children.external = new VoxelNode*[newChildCount]; + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + + _children.external[0] = childOne; + _children.external[1] = childTwo; + _children.external[2] = childThree; + _children.external[3] = childFour; + _externalChildrenCount++; + } else if (previousChildCount == 4 && newChildCount == 3) { + // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case + assert(_childrenExternal); + + // We need to determine which children we had, and which one we got rid of... + int indexOne = getNthBit(previousChildMask, 1); + int indexTwo = getNthBit(previousChildMask, 2); + int indexThree = getNthBit(previousChildMask, 3); + + bool removeChildOne = indexOne == childIndex; + bool removeChildTwo = indexTwo == childIndex; + bool removeChildThree = indexThree == childIndex; + + VoxelNode* childOne = _children.external[0]; + VoxelNode* childTwo = _children.external[1]; + VoxelNode* childThree = _children.external[2]; + VoxelNode* childFour = _children.external[3]; + + if (removeChildOne) { + childOne = childTwo; + childTwo = childThree; + childThree = childFour; + } else if (removeChildTwo) { + childTwo = childThree; + childThree = childFour; + } else if (removeChildThree) { + childThree = childFour; + } else { + // removing child four, nothing to do. + } + + // clean up the external children... + _childrenExternal = false; + delete[] _children.external; + _children.external = NULL; + _externalChildrenCount--; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + + + storeThreeChildren(childOne, childTwo, childThree); + } else if (previousChildCount == newChildCount) { + // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one + // that needs to be replaced and replace it. + for (int ordinal = 1; ordinal <= 8; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + if (index == childIndex) { + // this is our child to be replaced + int nthChild = ordinal-1; + _children.external[nthChild] = child; + break; + } + } + } else if (previousChildCount < newChildCount) { + // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert + // this child pointer into our external list + VoxelNode** newExternalList = new VoxelNode*[newChildCount]; + + int copiedCount = 0; + for (int ordinal = 1; ordinal <= newChildCount; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + if (index != -1 && index < childIndex) { + newExternalList[ordinal - 1] = _children.external[ordinal - 1]; + copiedCount++; + } else { + + // insert our new child here... + newExternalList[ordinal - 1] = child; + + // if we didn't copy all of our previous children, then we need to + if (copiedCount < previousChildCount) { + // our child needs to be inserted before this index, and everything else pushed out... + for (int oldOrdinal = ordinal; oldOrdinal <= previousChildCount; oldOrdinal++) { + newExternalList[oldOrdinal] = _children.external[oldOrdinal - 1]; + } + } + break; + } + } + delete[] _children.external; + _children.external = newExternalList; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + + } else if (previousChildCount > newChildCount) { + // 4 or more children, one item being removed, we know we're stored externally, we just figure out which + // item to remove from our external list + VoxelNode** newExternalList = new VoxelNode*[newChildCount]; + + for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { + int index = getNthBit(previousChildMask, ordinal); + assert(index != -1); + if (index < childIndex) { + newExternalList[ordinal - 1] = _children.external[ordinal - 1]; + } else { + // our child needs to be removed from here, and everything else pulled in... + for (int moveOrdinal = ordinal; moveOrdinal <= newChildCount; moveOrdinal++) { + newExternalList[moveOrdinal - 1] = _children.external[moveOrdinal]; + } + break; + } + } + delete[] _children.external; + _children.external = newExternalList; + _externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*); + _externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*); + } else { + assert(false); + qDebug("THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d\n",previousChildCount, newChildCount); + } + + // check to see if we could store these 4 children locally + if (getChildCount() == 4 && _childrenExternal && _children.external) { + checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]); + } + + +#ifdef HAS_AUDIT_CHILDREN + _childrenArray[childIndex] = child; + auditChildren("setChildAtIndex()"); +#endif // def HAS_AUDIT_CHILDREN +} + + VoxelNode* VoxelNode::addChildAtIndex(int childIndex) { - if (!_children[childIndex]) { - _children[childIndex] = new VoxelNode(childOctalCode(_octalCode, childIndex)); - _children[childIndex]->setVoxelSystem(_voxelSystem); // our child is always part of our voxel system NULL ok + VoxelNode* childAt = getChildAtIndex(childIndex); + if (!childAt) { + // before adding a child, see if we're currently a leaf + if (isLeaf()) { + _voxelNodeLeafCount--; + } + + childAt = new VoxelNode(childOctalCode(getOctalCode(), childIndex)); + childAt->setVoxelSystem(getVoxelSystem()); // our child is always part of our voxel system NULL ok + + setChildAtIndex(childIndex, childAt); + _isDirty = true; - _childCount++; markWithChangedTime(); } - return _children[childIndex]; + return childAt; } // handles staging or deletion of all deep children @@ -197,14 +994,15 @@ void VoxelNode::setColorFromAverageOfChildren() { int colorArray[4] = {0,0,0,0}; float density = 0.0f; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i] && _children[i]->isColored()) { + VoxelNode* childAt = getChildAtIndex(i); + if (childAt && childAt->isColored()) { for (int j = 0; j < 3; j++) { - colorArray[j] += _children[i]->getTrueColor()[j]; // color averaging should always be based on true colors + colorArray[j] += childAt->getTrueColor()[j]; // color averaging should always be based on true colors } colorArray[3]++; } - if (_children[i]) { - density += _children[i]->getDensity(); + if (childAt) { + density += childAt->getDensity(); } } density /= (float) NUMBER_OF_CHILDREN; @@ -275,6 +1073,9 @@ void VoxelNode::setColor(const nodeColor& color) { } #endif + + + // will detect if children are leaves AND the same color // and in that case will delete the children and make this node // a leaf, returns TRUE if all the leaves are collapsed into a @@ -284,18 +1085,19 @@ bool VoxelNode::collapseIdenticalLeaves() { bool allChildrenMatch = true; // assume the best (ottimista) int red,green,blue; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + VoxelNode* childAt = getChildAtIndex(i); // if no child, child isn't a leaf, or child doesn't have a color - if (!_children[i] || !_children[i]->isLeaf() || !_children[i]->isColored()) { + if (!childAt || !childAt->isLeaf() || !childAt->isColored()) { allChildrenMatch=false; //qDebug("SADNESS child missing or not colored! i=%d\n",i); break; } else { if (i==0) { - red = _children[i]->getColor()[0]; - green = _children[i]->getColor()[1]; - blue = _children[i]->getColor()[2]; - } else if (red != _children[i]->getColor()[0] || - green != _children[i]->getColor()[1] || blue != _children[i]->getColor()[2]) { + red = childAt->getColor()[0]; + green = childAt->getColor()[1]; + blue = childAt->getColor()[2]; + } else if (red != childAt->getColor()[0] || + green != childAt->getColor()[1] || blue != childAt->getColor()[2]) { allChildrenMatch=false; break; } @@ -306,10 +1108,10 @@ bool VoxelNode::collapseIdenticalLeaves() { if (allChildrenMatch) { //qDebug("allChildrenMatch: pruning tree\n"); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - delete _children[i]; // delete all the child nodes - _children[i]=NULL; // set it to NULL + VoxelNode* childAt = getChildAtIndex(i); + delete childAt; // delete all the child nodes + setChildAtIndex(i, NULL); // set it to NULL } - _childCount = 0; nodeColor collapsedColor; collapsedColor[0]=red; collapsedColor[1]=green; @@ -333,19 +1135,20 @@ void VoxelNode::setRandomColor(int minimumBrightness) { void VoxelNode::printDebugDetails(const char* label) const { unsigned char childBits = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - if (_children[i]) { + VoxelNode* childAt = getChildAtIndex(i); + if (childAt) { setAtBit(childBits,i); } } qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isColored=%s (%d,%d,%d,%d) isDirty=%s shouldRender=%s\n children=", label, - _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getSize().x, + _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getScale(), debug::valueOf(isLeaf()), debug::valueOf(isColored()), getColor()[0], getColor()[1], getColor()[2], getColor()[3], debug::valueOf(isDirty()), debug::valueOf(getShouldRender())); outputBits(childBits, false); qDebug("\n octalCode="); - printOctalCode(_octalCode); + printOctalCode(getOctalCode()); } float VoxelNode::getEnclosingRadius() const { @@ -398,20 +1201,20 @@ float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const } float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const { - glm::vec3 center = _box.getCenter() * (float)TREE_SCALE; + glm::vec3 center = _box.calcCenter() * (float)TREE_SCALE; glm::vec3 temp = viewFrustum.getPosition() - center; float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp)); return distanceToVoxelCenter; } float VoxelNode::distanceSquareToPoint(const glm::vec3& point) const { - glm::vec3 temp = point - _box.getCenter(); + glm::vec3 temp = point - _box.calcCenter(); float distanceSquare = glm::dot(temp, temp); return distanceSquare; } float VoxelNode::distanceToPoint(const glm::vec3& point) const { - glm::vec3 temp = point - _box.getCenter(); + glm::vec3 temp = point - _box.calcCenter(); float distance = sqrtf(glm::dot(temp, temp)); return distance; } diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 41196c81b5..4f0fa96ecc 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -41,8 +41,8 @@ public: VoxelNode(unsigned char * octalCode); // regular constructor ~VoxelNode(); - unsigned char* getOctalCode() const { return _octalCode; } - VoxelNode* getChildAtIndex(int childIndex) const { return _children[childIndex]; } + const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; } + VoxelNode* getChildAtIndex(int childIndex) const; void deleteChildAtIndex(int childIndex); VoxelNode* removeChildAtIndex(int childIndex); VoxelNode* addChildAtIndex(int childIndex); @@ -53,10 +53,9 @@ public: bool collapseIdenticalLeaves(); const AABox& getAABox() const { return _box; } - const glm::vec3& getCenter() const { return _box.getCenter(); } const glm::vec3& getCorner() const { return _box.getCorner(); } - float getScale() const { return _box.getSize().x; } // voxelScale = (1 / powf(2, *node->getOctalCode())); } - int getLevel() const { return *_octalCode + 1; } // one based or zero based? this doesn't correctly handle 2 byte case + float getScale() const { return _box.getScale(); } + int getLevel() const { return numberOfThreeBitSectionsInCode(getOctalCode()) + 1; } float getEnclosingRadius() const; @@ -72,8 +71,8 @@ public: float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this. float distanceToPoint(const glm::vec3& point) const; - bool isLeaf() const { return _childCount == 0; } - int getChildCount() const { return _childCount; } + bool isLeaf() const { return getChildCount() == 0; } + int getChildCount() const { return numberOfOnes(_childBitmask); } void printDebugDetails(const char* label) const; bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } @@ -84,37 +83,30 @@ public: void handleSubtreeChanged(VoxelTree* myTree); glBufferIndex getBufferIndex() const { return _glBufferIndex; } - bool isKnownBufferIndex() const { return (_glBufferIndex != GLBUFFER_INDEX_UNKNOWN); } - void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; } - VoxelSystem* getVoxelSystem() const { return _voxelSystem; } - void setVoxelSystem(VoxelSystem* voxelSystem) { _voxelSystem = voxelSystem; } - + bool isKnownBufferIndex() const { return !_unknownBufferIndex; } + void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; _unknownBufferIndex =(index == GLBUFFER_INDEX_UNKNOWN);} + VoxelSystem* getVoxelSystem() const; + void setVoxelSystem(VoxelSystem* voxelSystem); // Used by VoxelSystem for rendering in/out of view and LOD void setShouldRender(bool shouldRender); bool getShouldRender() const { return _shouldRender; } -#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color void setFalseColor(colorPart red, colorPart green, colorPart blue); void setFalseColored(bool isFalseColored); bool getFalseColored() { return _falseColored; } void setColor(const nodeColor& color); const nodeColor& getTrueColor() const { return _trueColor; } const nodeColor& getColor() const { return _currentColor; } -#else - void setFalseColor(colorPart red, colorPart green, colorPart blue) { /* no op */ }; - void setFalseColored(bool isFalseColored) { /* no op */ }; - bool getFalseColored() { return false; }; - void setColor(const nodeColor& color) { memcpy(_trueColor,color,sizeof(nodeColor)); }; - void setDensity(const float density) { _density = density; }; - const nodeColor& getTrueColor() const { return _trueColor; }; - const nodeColor& getColor() const { return _trueColor; }; -#endif void setDensity(float density) { _density = density; } float getDensity() const { return _density; } - void setSourceID(uint16_t sourceID) { _sourceID = sourceID; } - uint16_t getSourceID() const { return _sourceID; } + + void setSourceUUID(const QUuid& sourceID); + QUuid getSourceUUID() const; + uint16_t getSourceUUIDKey() const { return _sourceUUIDKey; } + bool matchesSourceUUID(const QUuid& sourceUUID) const; + static uint16_t getSourceNodeUUIDKey(const QUuid& sourceUUID); static void addDeleteHook(VoxelNodeDeleteHook* hook); static void removeDeleteHook(VoxelNodeDeleteHook* hook); @@ -122,49 +114,131 @@ public: static void addUpdateHook(VoxelNodeUpdateHook* hook); static void removeUpdateHook(VoxelNodeUpdateHook* hook); - void recalculateSubTreeNodeCount(); - unsigned long getSubTreeNodeCount() const { return _subtreeNodeCount; } - unsigned long getSubTreeInternalNodeCount() const { return _subtreeNodeCount - _subtreeLeafNodeCount; } - unsigned long getSubTreeLeafNodeCount() const { return _subtreeLeafNodeCount; } + static unsigned long getNodeCount() { return _voxelNodeCount; } + static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; } + static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; } static uint64_t getVoxelMemoryUsage() { return _voxelMemoryUsage; } static uint64_t getOctcodeMemoryUsage() { return _octcodeMemoryUsage; } + static uint64_t getExternalChildrenMemoryUsage() { return _externalChildrenMemoryUsage; } + static uint64_t getTotalMemoryUsage() { return _voxelMemoryUsage + _octcodeMemoryUsage + _externalChildrenMemoryUsage; } + + static uint64_t getGetChildAtIndexTime() { return _getChildAtIndexTime; } + static uint64_t getGetChildAtIndexCalls() { return _getChildAtIndexCalls; } + static uint64_t getSetChildAtIndexTime() { return _setChildAtIndexTime; } + static uint64_t getSetChildAtIndexCalls() { return _setChildAtIndexCalls; } + + static uint64_t getSingleChildrenCount() { return _singleChildrenCount; } + static uint64_t getTwoChildrenOffsetCount() { return _twoChildrenOffsetCount; } + static uint64_t getTwoChildrenExternalCount() { return _twoChildrenExternalCount; } + static uint64_t getThreeChildrenOffsetCount() { return _threeChildrenOffsetCount; } + static uint64_t getThreeChildrenExternalCount() { return _threeChildrenExternalCount; } + static uint64_t getExternalChildrenCount() { return _externalChildrenCount; } + static uint64_t getChildrenCount(int childCount) { return _childrenCount[childCount]; } + + static uint64_t getCouldStoreFourChildrenInternally() { return _couldStoreFourChildrenInternally; } + static uint64_t getCouldNotStoreFourChildrenInternally() { return _couldNotStoreFourChildrenInternally; } + +#ifdef HAS_AUDIT_CHILDREN + void auditChildren(const char* label) const; +#endif // def HAS_AUDIT_CHILDREN private: + void setChildAtIndex(int childIndex, VoxelNode* child); + void storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo); + void retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo); + void storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree); + void retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree); + void decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const; + void encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree); + void checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour); + void calculateAABox(); void init(unsigned char * octalCode); void notifyDeleteHooks(); void notifyUpdateHooks(); - VoxelNode* _children[8]; /// Client and server, pointers to child nodes, 64 bytes AABox _box; /// Client and server, axis aligned box for bounds of this voxel, 48 bytes - unsigned char* _octalCode; /// Client and server, pointer to octal code for this node, 8 bytes + + /// Client and server, buffer containing the octal code or a pointer to octal code for this node, 8 bytes + union octalCode_t { + unsigned char buffer[8]; + unsigned char* pointer; + } _octalCode; uint64_t _lastChanged; /// Client and server, timestamp this node was last changed, 8 bytes - unsigned long _subtreeNodeCount; /// Client and server, nodes below this node, 8 bytes - unsigned long _subtreeLeafNodeCount; /// Client and server, leaves below this node, 8 bytes - glBufferIndex _glBufferIndex; /// Client only, vbo index for this voxel if being rendered, 8 bytes - VoxelSystem* _voxelSystem; /// Client only, pointer to VoxelSystem rendering this voxel, 8 bytes + /// Client and server, pointers to child nodes, various encodings + union children_t { + VoxelNode* single; + int32_t offsetsTwoChildren[2]; + uint64_t offsetsThreeChildrenEncoded; + VoxelNode** external; + } _children; + +#ifdef HAS_AUDIT_CHILDREN + VoxelNode* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding +#endif // def HAS_AUDIT_CHILDREN + + uint32_t _glBufferIndex : 24, /// Client only, vbo index for this voxel if being rendered, 3 bytes + _voxelSystemIndex : 8; /// Client only, index to the VoxelSystem rendering this voxel, 1 bytes + + // Support for _voxelSystemIndex, we use these static member variables to track the VoxelSystems that are + // in use by various voxel nodes. We map the VoxelSystem pointers into an 1 byte key, this limits us to at + // most 255 voxel systems in use at a time within the client. Which is far more than we need. + static uint8_t _nextIndex; + static std::map _mapVoxelSystemPointersToIndex; + static std::map _mapIndexToVoxelSystemPointers; float _density; /// Client and server, If leaf: density = 1, if internal node: 0-1 density of voxels inside, 4 bytes - int _childCount; /// Client and server, current child nodes set to non-null in _children, 4 bytes nodeColor _trueColor; /// Client and server, true color of this voxel, 4 bytes -#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes - bool _falseColored; /// Client only, is this voxel false colored, 1 bytes -#endif - bool _isDirty; /// Client only, has this voxel changed since being rendered, 1 byte - bool _shouldRender; /// Client only, should this voxel render at this time, 1 byte - uint16_t _sourceID; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes + uint16_t _sourceUUIDKey; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes + + // Support for _sourceUUID, we use these static member variables to track the UUIDs that are + // in use by various voxel server nodes. We map the UUID strings into an 16 bit key, this limits us to at + // most 65k voxel servers in use at a time within the client. Which is far more than we need. + static uint16_t _nextUUIDKey; // start at 1, 0 is reserved for NULL + static std::map _mapSourceUUIDsToKeys; + static std::map _mapKeysToSourceUUIDs; + + unsigned char _childBitmask; // 1 byte + + bool _falseColored : 1, /// Client only, is this voxel false colored, 1 bit + _isDirty : 1, /// Client only, has this voxel changed since being rendered, 1 bit + _shouldRender : 1, /// Client only, should this voxel render at this time, 1 bit + _octcodePointer : 1, /// Client and Server only, is this voxel's octal code a pointer or buffer, 1 bit + _unknownBufferIndex : 1, + _childrenExternal : 1; /// Client only, is this voxel's VBO buffer the unknown buffer index, 1 bit static std::vector _deleteHooks; static std::vector _updateHooks; + + static uint64_t _voxelNodeCount; + static uint64_t _voxelNodeLeafCount; + static uint64_t _voxelMemoryUsage; static uint64_t _octcodeMemoryUsage; + static uint64_t _externalChildrenMemoryUsage; + + static uint64_t _getChildAtIndexTime; + static uint64_t _getChildAtIndexCalls; + static uint64_t _setChildAtIndexTime; + static uint64_t _setChildAtIndexCalls; + + static uint64_t _singleChildrenCount; + static uint64_t _twoChildrenOffsetCount; + static uint64_t _twoChildrenExternalCount; + static uint64_t _threeChildrenOffsetCount; + static uint64_t _threeChildrenExternalCount; + static uint64_t _externalChildrenCount; + static uint64_t _childrenCount[NUMBER_OF_CHILDREN + 1]; + + static uint64_t _couldStoreFourChildrenInternally; + static uint64_t _couldNotStoreFourChildrenInternally; }; #endif /* defined(__hifi__VoxelNode__) */ \ No newline at end of file diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp index 1ebc4015e0..f4570d93a2 100644 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ b/libraries/voxels/src/VoxelSceneStats.cpp @@ -33,9 +33,10 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r reset(); // resets packet and voxel stats _isStarted = true; _start = usecTimestampNow(); - _totalVoxels = root->getSubTreeNodeCount(); - _totalInternal = root->getSubTreeInternalNodeCount(); - _totalLeaves = root->getSubTreeLeafNodeCount(); + + _totalVoxels = VoxelNode::getNodeCount(); + _totalInternal = VoxelNode::getInternalNodeCount(); + _totalLeaves = VoxelNode::getLeafNodeCount(); _isFullScene = isFullScene; _isMoving = isMoving; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 3b79722686..eadba04e65 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -134,7 +134,7 @@ void VoxelTree::recurseNodeWithOperationDistanceSorted(VoxelNode* node, RecurseV VoxelNode* VoxelTree::nodeForOctalCode(VoxelNode* ancestorNode, - unsigned char* needleCode, VoxelNode** parentOfFoundNode) const { + const unsigned char* needleCode, VoxelNode** parentOfFoundNode) const { // find the appropriate branch index based on this ancestorNode if (*needleCode > 0) { int branchForNeedle = branchIndexWithDescendant(ancestorNode->getOctalCode(), needleCode); @@ -221,7 +221,7 @@ int VoxelTree::readNodeData(VoxelNode* destinationNode, unsigned char* nodeData, if (childNodeAt) { nodeWasDirty = childNodeAt->isDirty(); childNodeAt->setColor(newColor); - childNodeAt->setSourceID(args.sourceID); + childNodeAt->setSourceUUID(args.sourceUUID); // if we had a local version of the node already, it's possible that we have it in the VBO but // with the same color data, so this won't count as a change. To address this we check the following @@ -630,9 +630,22 @@ void VoxelTree::printTreeForDebugging(VoxelNode *startNode) { } // Note: this is an expensive call. Don't call it unless you really need to reaverage the entire tree (from startNode) -void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { +void VoxelTree::reaverageVoxelColors(VoxelNode* startNode) { // if our tree is a reaveraging tree, then we do this, otherwise we don't do anything if (_shouldReaverage) { + static int recursionCount; + if (startNode == rootNode) { + recursionCount = 0; + } else { + recursionCount++; + } + const int UNREASONABLY_DEEP_RECURSION = 20; + if (recursionCount > UNREASONABLY_DEEP_RECURSION) { + qDebug("VoxelTree::reaverageVoxelColors()... bailing out of UNREASONABLY_DEEP_RECURSION\n"); + recursionCount--; + return; + } + bool hasChildren = false; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -647,9 +660,7 @@ void VoxelTree::reaverageVoxelColors(VoxelNode *startNode) { if (hasChildren && !startNode->collapseIdenticalLeaves()) { startNode->setColorFromAverageOfChildren(); } - - // this is also a good time to recalculateSubTreeNodeCount() - startNode->recalculateSubTreeNodeCount(); + recursionCount--; } } @@ -713,6 +724,11 @@ VoxelNode* VoxelTree::getVoxelAt(float x, float y, float z, float s) const { node = NULL; } delete[] octalCode; // cleanup memory +#ifdef HAS_AUDIT_CHILDREN + if (node) { + node->auditChildren("VoxelTree::getVoxelAt()"); + } +#endif // def HAS_AUDIT_CHILDREN return node; } @@ -1555,7 +1571,7 @@ bool VoxelTree::readFromSVOFile(const char* fileName) { unsigned char* entireFile = new unsigned char[fileLength]; file.read((char*)entireFile, fileLength); bool wantImportProgress = true; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, UNKNOWN_NODE_ID, wantImportProgress); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, wantImportProgress); readBitstreamToTree(entireFile, fileLength, args); delete[] entireFile; @@ -1815,7 +1831,7 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin // ask destination tree to read the bitstream bool wantImportProgress = true; - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, UNKNOWN_NODE_ID, wantImportProgress); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, destinationNode, 0, wantImportProgress); readBitstreamToTree(&outputBuffer[0], bytesWritten, args); } } @@ -1981,7 +1997,7 @@ bool VoxelTree::nudgeCheck(VoxelNode* node, void* extraData) { NodeChunkArgs* args = (NodeChunkArgs*)extraData; // get octal code of this node - unsigned char* octalCode = node->getOctalCode(); + const unsigned char* octalCode = node->getOctalCode(); // get voxel position/size VoxelPositionSize unNudgedDetails; @@ -2020,7 +2036,7 @@ void VoxelTree::nudgeLeaf(VoxelNode* node, void* extraData) { NodeChunkArgs* args = (NodeChunkArgs*)extraData; // get octal code of this node - unsigned char* octalCode = node->getOctalCode(); + const unsigned char* octalCode = node->getOctalCode(); // get voxel position/size VoxelPositionSize unNudgedDetails; diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 318b8fc5eb..a38c751133 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -101,19 +101,19 @@ public: bool includeColor; bool includeExistsBits; VoxelNode* destinationNode; - uint16_t sourceID; + QUuid sourceUUID; bool wantImportProgress; ReadBitstreamToTreeParams( bool includeColor = WANT_COLOR, bool includeExistsBits = WANT_EXISTS_BITS, VoxelNode* destinationNode = NULL, - uint16_t sourceID = UNKNOWN_NODE_ID, + QUuid sourceUUID = QUuid(), bool wantImportProgress = false) : includeColor(includeColor), includeExistsBits(includeExistsBits), destinationNode(destinationNode), - sourceID(sourceID), + sourceUUID(sourceUUID), wantImportProgress(wantImportProgress) {} }; @@ -214,7 +214,7 @@ private: static bool countVoxelsOperation(VoxelNode* node, void* extraData); - VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; + VoxelNode* nodeForOctalCode(VoxelNode* ancestorNode, const unsigned char* needleCode, VoxelNode** parentOfFoundNode) const; VoxelNode* createMissingNode(VoxelNode* lastParentNode, unsigned char* deepestCodeToCreate); int readNodeData(VoxelNode *destinationNode, unsigned char* nodeData, int bufferSizeBytes, ReadBitstreamToTreeParams& args); @@ -225,7 +225,7 @@ private: /// Octal Codes of any subtrees currently being encoded. While any of these codes is being encoded, ancestors and /// descendants of them can not be deleted. - std::set _codesBeingEncoded; + std::set _codesBeingEncoded; /// mutex lock to protect the encoding set pthread_mutex_t _encodeSetLock; diff --git a/voxel-edit/src/main.cpp b/voxel-edit/src/main.cpp index 11242452dd..244db390df 100644 --- a/voxel-edit/src/main.cpp +++ b/voxel-edit/src/main.cpp @@ -1,4 +1,4 @@ -// + // // main.cpp // Voxel Edit // @@ -233,7 +233,7 @@ void processFillSVOFile(const char* fillSVOFile) { } -int main(int argc, const char * argv[]) +int old_main(int argc, const char * argv[]) { qInstallMessageHandler(sharedMessageHandler); @@ -294,4 +294,76 @@ int main(int argc, const char * argv[]) } return 0; +} + +void unitTest(VoxelTree * tree) { + printf("unit tests...\n"); + + // We want our corner voxels to be about 1/2 meter high, and our TREE_SCALE is in meters, so... + float voxelSize = 0.5f; + + // Here's an example of how to create a voxel. + printf("creating corner points...\n"); + tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); + + // Here's an example of how to test if a voxel exists + VoxelNode* node = tree->getVoxelAt(0, 0, 0, voxelSize); + if (node) { + // and how to access it's color + printf("corner point 0,0,0 exists... color is (%d,%d,%d) \n", + node->getColor()[0], node->getColor()[1], node->getColor()[2]); + } + + // here's an example of how to delete a voxel + printf("attempting to delete corner point 0,0,0\n"); + tree->deleteVoxelAt(0, 0, 0, voxelSize); + + // Test to see that the delete worked... it should be FALSE... + if (tree->getVoxelAt(0, 0, 0, voxelSize)) { + printf("corner point 0,0,0 exists...\n"); + } else { + printf("corner point 0,0,0 does not exists...\n"); + } + + tree->createVoxel(0, 0, 0, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(0, 0, 0, voxelSize)) { + printf("corner point 0,0,0 exists...\n"); + } else { + printf("corner point 0,0,0 does not exists...\n"); + } + + tree->createVoxel(voxelSize, 0, 0, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(voxelSize, 0, 0, voxelSize)) { + printf("corner point voxelSize,0,0 exists...\n"); + } else { + printf("corner point voxelSize,0,0 does not exists...\n"); + } + + tree->createVoxel(0, 0, voxelSize, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(0, 0, voxelSize, voxelSize)) { + printf("corner point 0, 0, voxelSize exists...\n"); + } else { + printf("corner point 0, 0, voxelSize does not exists...\n"); + } + + tree->createVoxel(voxelSize, 0, voxelSize, voxelSize, 255, 255 ,255); + if (tree->getVoxelAt(voxelSize, 0, voxelSize, voxelSize)) { + printf("corner point voxelSize, 0, voxelSize exists...\n"); + } else { + printf("corner point voxelSize, 0, voxelSize does not exists...\n"); + } + + printf("check root voxel exists...\n"); + if (tree->getVoxelAt(0,0,0,1.0)) { + printf("of course it does\n"); + } else { + printf("WTH!?!\n"); + } + +} + + +int main(int argc, const char * argv[]) { + unitTest(&myTree); + return 0; } \ No newline at end of file