mirror of
https://github.com/overte-org/overte.git
synced 2025-07-25 09:36:32 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19433
This commit is contained in:
commit
a746174a3b
77 changed files with 1877 additions and 2433 deletions
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,4 +36,4 @@ private:
|
|||
std::vector<AudioInjector*> _audioInjectors;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Operative__) */
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <StdDev.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "AudioRingBuffer.h"
|
||||
|
||||
|
@ -80,6 +81,9 @@ void AudioMixer::run() {
|
|||
NodeList *nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER);
|
||||
|
||||
const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR };
|
||||
nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST));
|
||||
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||
|
@ -129,7 +133,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
|
||||
|
@ -143,6 +147,9 @@ void AudioMixer::run() {
|
|||
}
|
||||
}
|
||||
|
||||
// get the NodeList to ping any inactive nodes, for hole punching
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) node->getLinkedData();
|
||||
if (positionalRingBuffer && positionalRingBuffer->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES)) {
|
||||
|
@ -156,7 +163,7 @@ void AudioMixer::run() {
|
|||
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
if (node->getType() == NODE_TYPE_AGENT) {
|
||||
if (node->getType() == NODE_TYPE_AGENT && node->getActiveSocket() && node->getLinkedData()) {
|
||||
AvatarAudioRingBuffer* nodeRingBuffer = (AvatarAudioRingBuffer*) node->getLinkedData();
|
||||
|
||||
// zero out the client mix for this node
|
||||
|
@ -164,176 +171,176 @@ void AudioMixer::run() {
|
|||
|
||||
// loop through all other nodes that have sufficient audio to mix
|
||||
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
|
||||
if (((PositionalAudioRingBuffer*) otherNode->getLinkedData())->willBeAddedToMix()
|
||||
if (otherNode->getLinkedData()
|
||||
&& ((PositionalAudioRingBuffer*) otherNode->getLinkedData())->willBeAddedToMix()
|
||||
&& (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];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(clientPacket + numBytesPacketHeader, clientSamples, sizeof(clientSamples));
|
||||
nodeList->getNodeSocket()->send(node->getPublicSocket(), clientPacket, sizeof(clientPacket));
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), clientPacket, sizeof(clientPacket));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,55 +360,26 @@ void AudioMixer::run() {
|
|||
// pull any new audio data from nodes off of the network stack
|
||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
|
||||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
|
||||
unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData);
|
||||
uint16_t sourceID;
|
||||
memcpy(&sourceID, currentBuffer, sizeof(sourceID));
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* avatarNode = nodeList->addOrUpdateNode(nodeAddress,
|
||||
nodeAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
sourceID);
|
||||
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
nodeList->updateNodeWithData(nodeAddress, packetData, receivedBytes);
|
||||
|
||||
if (std::isnan(((PositionalAudioRingBuffer *)avatarNode->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this node - temporary solution to mixer crash on mac sleep
|
||||
avatarNode->setAlive(false);
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
Node* matchingInjector = NULL;
|
||||
|
||||
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 (matchingNode) {
|
||||
nodeList->updateNodeWithData(matchingNode, nodeAddress, packetData, receivedBytes);
|
||||
|
||||
if (packetData[0] != PACKET_TYPE_INJECT_AUDIO
|
||||
&& std::isnan(((PositionalAudioRingBuffer *)matchingNode->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this node - temporary solution to mixer crash on mac sleep
|
||||
matchingNode->setAlive(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchingInjector) {
|
||||
matchingInjector = nodeList->addOrUpdateNode(NULL,
|
||||
NULL,
|
||||
NODE_TYPE_AUDIO_INJECTOR,
|
||||
nodeList->getLastNodeID());
|
||||
nodeList->increaseNodeID();
|
||||
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
#include <Stk.h>
|
||||
#include <TwoPole.h>
|
||||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
typedef std::map<uint16_t, stk::TwoPole*> TwoPoleNodeMap;
|
||||
typedef std::map<QUuid, stk::TwoPole*> TwoPoleNodeMap;
|
||||
|
||||
class AvatarAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||
public:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#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) {
|
||||
|
@ -94,6 +97,8 @@ void AvatarMixer::run() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER);
|
||||
|
||||
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
@ -103,7 +108,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 +122,37 @@ 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();
|
||||
}
|
||||
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
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->nodeWithUUID(nodeUUID);
|
||||
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
||||
if (avatarNode) {
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,88 +30,137 @@ 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);
|
||||
|
||||
const char RESPONSE_200[] = "HTTP/1.0 200 OK\r\n\r\n";
|
||||
const char RESPONSE_400[] = "HTTP/1.0 400 Bad Request\r\n\r\n";
|
||||
|
||||
if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) {
|
||||
// return a 200
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
// upload the file
|
||||
mg_upload(connection, "/tmp");
|
||||
|
||||
return 1;
|
||||
} else if (strcmp(ri->uri, "/assignments.json") == 0) {
|
||||
// user is asking for json list of assignments
|
||||
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// setup the JSON
|
||||
QJsonObject assignmentJSON;
|
||||
|
||||
QJsonObject assignedNodesJSON;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()) {
|
||||
// this is a node with assignment
|
||||
QJsonObject assignedNodeJSON;
|
||||
const char URI_ASSIGNMENT[] = "/assignment";
|
||||
const char URI_NODE[] = "/node";
|
||||
|
||||
if (strcmp(ri->request_method, "GET") == 0) {
|
||||
if (strcmp(ri->uri, "/assignments.json") == 0) {
|
||||
// user is asking for json list of assignments
|
||||
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// setup the JSON
|
||||
QJsonObject assignmentJSON;
|
||||
|
||||
QJsonObject assignedNodesJSON;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()) {
|
||||
// this is a node with assignment
|
||||
QJsonObject assignedNodeJSON;
|
||||
|
||||
// add the assignment UUID
|
||||
QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID());
|
||||
assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID;
|
||||
|
||||
// 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());
|
||||
nodeTypeName = nodeTypeName.toLower();
|
||||
nodeTypeName.replace(' ', '-');
|
||||
|
||||
assignedNodesJSON[nodeTypeName] = assignedNodeJSON;
|
||||
}
|
||||
}
|
||||
|
||||
assignmentJSON["fulfilled"] = assignedNodesJSON;
|
||||
|
||||
QJsonObject queuedAssignmentsJSON;
|
||||
|
||||
// add the queued but unfilled assignments to the json
|
||||
std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
|
||||
|
||||
while (assignment != domainServerInstance->_assignmentQueue.end()) {
|
||||
QJsonObject queuedAssignmentJSON;
|
||||
|
||||
// add the assignment UUID
|
||||
QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID());
|
||||
assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID;
|
||||
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
||||
queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString;
|
||||
|
||||
QJsonObject nodePublicSocketJSON;
|
||||
// add this queued assignment to the JSON
|
||||
queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON;
|
||||
|
||||
// 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);
|
||||
// push forward the iterator to check the next assignment
|
||||
assignment++;
|
||||
}
|
||||
|
||||
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
||||
|
||||
// print out the created JSON
|
||||
QJsonDocument assignmentDocument(assignmentJSON);
|
||||
mg_printf(connection, "%s", assignmentDocument.toJson().constData());
|
||||
|
||||
// we've processed this request
|
||||
return 1;
|
||||
}
|
||||
|
||||
// not processed, pass to document root
|
||||
return 0;
|
||||
} else if (strcmp(ri->request_method, "POST") == 0) {
|
||||
if (strcmp(ri->uri, URI_ASSIGNMENT) == 0) {
|
||||
// return a 200
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
// upload the file
|
||||
mg_upload(connection, "/tmp");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (strcmp(ri->request_method, "DELETE") == 0) {
|
||||
// this is a DELETE request
|
||||
|
||||
// check if it is for an assignment
|
||||
if (memcmp(ri->uri, URI_NODE, strlen(URI_NODE)) == 0) {
|
||||
// pull the UUID from the url
|
||||
QUuid deleteUUID = QUuid(QString(ri->uri + strlen(URI_NODE) + sizeof('/')));
|
||||
|
||||
if (!deleteUUID.isNull()) {
|
||||
Node *nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
|
||||
|
||||
assignedNodeJSON["public"] = nodePublicSocketJSON;
|
||||
|
||||
// re-format the type name so it matches the target name
|
||||
QString nodeTypeName(node->getTypeName());
|
||||
nodeTypeName = nodeTypeName.toLower();
|
||||
nodeTypeName.replace(' ', '-');
|
||||
|
||||
assignedNodesJSON[nodeTypeName] = assignedNodeJSON;
|
||||
if (nodeToKill) {
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// we have a valid UUID and node - kill the node that has this assignment
|
||||
NodeList::getInstance()->killNode(nodeToKill);
|
||||
|
||||
// successfully processed request
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assignmentJSON["fulfilled"] = assignedNodesJSON;
|
||||
// request not processed - bad request
|
||||
mg_printf(connection, "%s", RESPONSE_400);
|
||||
|
||||
QJsonObject queuedAssignmentsJSON;
|
||||
|
||||
// add the queued but unfilled assignments to the json
|
||||
std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
|
||||
|
||||
while (assignment != domainServerInstance->_assignmentQueue.end()) {
|
||||
QJsonObject queuedAssignmentJSON;
|
||||
|
||||
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
||||
queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString;
|
||||
|
||||
// add this queued assignment to the JSON
|
||||
queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON;
|
||||
|
||||
// push forward the iterator to check the next assignment
|
||||
assignment++;
|
||||
}
|
||||
|
||||
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
||||
|
||||
// print out the created JSON
|
||||
QJsonDocument assignmentDocument(assignmentJSON);
|
||||
mg_printf(connection, "%s", assignmentDocument.toJson().constData());
|
||||
|
||||
// we've processed this request
|
||||
// this was processed by civetweb
|
||||
return 1;
|
||||
} else {
|
||||
// have mongoose process this request from the document_root
|
||||
|
@ -153,8 +202,29 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const
|
|||
domainServerInstance->_assignmentQueueMutex.unlock();
|
||||
}
|
||||
|
||||
void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignment) {
|
||||
qDebug() << "Adding assignment" << *releasedAssignment << " back to queue.\n";
|
||||
|
||||
// find this assignment in the static file
|
||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||
if (_staticAssignments[i].getUUID() == releasedAssignment->getUUID()) {
|
||||
// reset the UUID on the static assignment
|
||||
_staticAssignments[i].resetUUID();
|
||||
|
||||
// put this assignment back in the queue so it goes out
|
||||
_assignmentQueueMutex.lock();
|
||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
||||
_assignmentQueueMutex.unlock();
|
||||
|
||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
||||
// we are at the blank part of the static assignments - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::nodeAdded(Node* node) {
|
||||
NodeList::getInstance()->increaseNodeID();
|
||||
|
||||
}
|
||||
|
||||
void DomainServer::nodeKilled(Node* node) {
|
||||
|
@ -162,32 +232,18 @@ void DomainServer::nodeKilled(Node* node) {
|
|||
if (node->getLinkedData()) {
|
||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
||||
|
||||
qDebug() << "Adding assignment" << *nodeAssignment << " back to queue.\n";
|
||||
|
||||
// find this assignment in the static file
|
||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||
if (_staticAssignments[i].getUUID() == nodeAssignment->getUUID()) {
|
||||
// reset the UUID on the static assignment
|
||||
_staticAssignments[i].resetUUID();
|
||||
|
||||
// put this assignment back in the queue so it goes out
|
||||
_assignmentQueueMutex.lock();
|
||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
||||
_assignmentQueueMutex.unlock();
|
||||
|
||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
||||
// we are at the blank part of the static assignments - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
addReleasedAssignmentBackToQueue(nodeAssignment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 +347,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 +448,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 +528,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 +554,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 +562,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 +594,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 +625,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 +647,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 +672,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 +686,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
|
||||
|
|
|
@ -42,11 +42,12 @@ 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 addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||
|
||||
void cleanup();
|
||||
|
||||
|
|
|
@ -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})
|
|
@ -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 <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
#include <AvatarData.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <UDPSocket.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <AudioInjectionManager.h>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,14 +38,11 @@ configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConf
|
|||
|
||||
# grab the implementation and header files from src dirs
|
||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui)
|
||||
file(GLOB SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui starfield)
|
||||
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
|
||||
endforeach(SUBDIR)
|
||||
|
||||
# project subdirectories
|
||||
add_subdirectory(src/starfield)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Gui REQUIRED)
|
||||
find_package(Qt5Multimedia REQUIRED)
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <PairingHandler.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include <UUID.h>
|
||||
#include <VoxelSceneStats.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -72,8 +72,8 @@
|
|||
using namespace std;
|
||||
|
||||
// Starfield information
|
||||
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
||||
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||
static unsigned STARFIELD_SEED = 1;
|
||||
|
||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1560,29 +1562,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);
|
||||
|
@ -1688,7 +1667,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) {
|
||||
|
@ -1697,7 +1676,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++) {
|
||||
|
@ -1710,7 +1689,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;
|
||||
}
|
||||
}
|
||||
|
@ -1759,12 +1738,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();
|
||||
}
|
||||
|
@ -2244,26 +2223,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2509,8 +2489,8 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... stars...");
|
||||
if (!_stars.getFileLoaded()) {
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
if (!_stars.isStarsLoaded()) {
|
||||
_stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED);
|
||||
}
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
|
@ -3573,8 +3553,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);
|
||||
}
|
||||
|
@ -3642,10 +3622,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);
|
||||
|
||||
|
@ -3675,13 +3655,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);
|
||||
|
||||
|
@ -3698,7 +3678,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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -109,70 +109,49 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o
|
|||
Node* audioMixer = nodeList->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
||||
|
||||
if (audioMixer) {
|
||||
audioMixer->lock();
|
||||
sockaddr_in audioSocket = *(sockaddr_in*) audioMixer->getActiveSocket();
|
||||
audioMixer->unlock();
|
||||
|
||||
glm::vec3 headPosition = interfaceAvatar->getHeadJointPosition();
|
||||
glm::quat headOrientation = interfaceAvatar->getHead().getOrientation();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,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);
|
||||
}
|
||||
|
@ -350,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),
|
||||
|
@ -398,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());
|
||||
|
||||
|
|
|
@ -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<int> _listenSources;
|
||||
|
||||
// Audio callback in class context.
|
||||
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
namespace { // .cpp-local
|
||||
|
||||
int const AREA_WIDTH = -400; // Width of the area used. Aligned to the right when negative.
|
||||
int const AREA_WIDTH = -280; // Width of the area used. Aligned to the right when negative.
|
||||
int const AREA_HEIGHT = 40; // Height of the area used. Aligned to the bottom when negative.
|
||||
int const BORDER_DISTANCE_HORIZ = -20; // Distance to edge of screen (use negative value when width is negative).
|
||||
int const BORDER_DISTANCE_VERT = 40; // Distance to edge of screen (use negative value when height is negative).
|
||||
|
@ -33,6 +33,8 @@ namespace { // .cpp-local
|
|||
|
||||
double const UNIT_SCALE = 8000.0 / (1024.0 * 1024.0); // Bytes/ms -> Mbps
|
||||
int const INITIAL_SCALE_MAXIMUM_INDEX = 250; // / 9: exponent, % 9: mantissa - 2, 0 o--o 2 * 10^-10
|
||||
int const MIN_METER_SCALE = 10; // 10Mbps
|
||||
int const NUMBER_OF_MARKERS = 10;
|
||||
}
|
||||
|
||||
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
||||
|
@ -182,7 +184,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
break;
|
||||
}
|
||||
if (totalMax < scaleMax * 0.5) {
|
||||
_scaleMaxIndex = glm::max(0, _scaleMaxIndex-1);
|
||||
_scaleMaxIndex = glm::max(0, _scaleMaxIndex - 1);
|
||||
commit = true;
|
||||
} else if (totalMax > scaleMax) {
|
||||
_scaleMaxIndex += 1;
|
||||
|
@ -190,10 +192,15 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
}
|
||||
} while (commit);
|
||||
|
||||
step = scaleMax / NUMBER_OF_MARKERS;
|
||||
if (scaleMax < MIN_METER_SCALE) {
|
||||
scaleMax = MIN_METER_SCALE;
|
||||
}
|
||||
|
||||
// Render scale indicators
|
||||
setColorRGBA(COLOR_INDICATOR);
|
||||
for (int j = int((scaleMax + step - 0.000001) / step); --j > 0;) {
|
||||
renderVerticalLine(int(barWidth * j * step / scaleMax), 0, h);
|
||||
for (int j = NUMBER_OF_MARKERS; --j > 0;) {
|
||||
renderVerticalLine(int(barWidth * j / NUMBER_OF_MARKERS), 0, h);
|
||||
}
|
||||
|
||||
// Render bars
|
||||
|
@ -220,11 +227,11 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
|||
// Render numbers
|
||||
char fmtBuf[8];
|
||||
setColorRGBA(COLOR_TEXT);
|
||||
sprintf(fmtBuf, "%0.2f", totalIn);
|
||||
sprintf(fmtBuf, "%0.1f", totalIn);
|
||||
_textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
PADDING_HORIZ_VALUE),
|
||||
textYupperLine, fmtBuf);
|
||||
sprintf(fmtBuf, "%0.2f", totalOut);
|
||||
sprintf(fmtBuf, "%0.1f", totalOut);
|
||||
_textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||
PADDING_HORIZ_VALUE),
|
||||
textYlowerLine, fmtBuf);
|
||||
|
|
|
@ -60,11 +60,11 @@ Menu::Menu() :
|
|||
QMenu* fileMenu = addMenu("File");
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AboutApp,
|
||||
0,
|
||||
this,
|
||||
SLOT(aboutApp())))->setMenuRole(QAction::AboutRole);
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AboutApp,
|
||||
0,
|
||||
this,
|
||||
SLOT(aboutApp()));
|
||||
#endif
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
|
@ -72,12 +72,6 @@ Menu::Menu() :
|
|||
0,
|
||||
this,
|
||||
SLOT(login())));
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
this,
|
||||
SLOT(editPreferences())))->setMenuRole(QAction::PreferencesRole);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Voxels");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels()));
|
||||
|
@ -114,13 +108,22 @@ Menu::Menu() :
|
|||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest()));
|
||||
addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true);
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
appInstance,
|
||||
SLOT(quit())))->setMenuRole(QAction::QuitRole);
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
appInstance,
|
||||
SLOT(quit()));
|
||||
|
||||
QMenu* editMenu = addMenu("Edit");
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
this,
|
||||
SLOT(editPreferences()));
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Voxels");
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
|
||||
|
@ -442,21 +445,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");
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
20
interface/src/Stars.cpp
Normal file → Executable file
20
interface/src/Stars.cpp
Normal file → Executable file
|
@ -7,34 +7,28 @@
|
|||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Stars.h"
|
||||
#include "Stars.h"
|
||||
|
||||
#define __interface__Starfield_impl__
|
||||
#include "starfield/Controller.h"
|
||||
#undef __interface__Starfield_impl__
|
||||
|
||||
Stars::Stars() :
|
||||
_controller(0l), _fileLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
_controller(0l), _starsLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
delete _controller;
|
||||
}
|
||||
|
||||
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
|
||||
_fileLoaded = _controller->readInput(url, cacheFile, limit);
|
||||
return _fileLoaded;
|
||||
bool Stars::generate(unsigned numStars, unsigned seed) {
|
||||
_starsLoaded = _controller->computeStars(numStars, seed);
|
||||
return _starsLoaded;
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _controller->setResolution(k);
|
||||
}
|
||||
|
||||
float Stars::changeLOD(float fraction, float overalloc, float realloc) {
|
||||
return float(_controller->changeLOD(fraction, overalloc, realloc));
|
||||
}
|
||||
|
||||
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
||||
|
||||
// determine length of screen diagonal from quadrant height and aspect ratio
|
||||
|
@ -47,7 +41,7 @@ void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
|||
// pull the modelview matrix off the GL stack
|
||||
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
|
||||
|
||||
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
|
||||
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
|
||||
}
|
||||
|
||||
|
||||
|
|
84
interface/src/Stars.h
Normal file → Executable file
84
interface/src/Stars.h
Normal file → Executable file
|
@ -13,69 +13,39 @@
|
|||
|
||||
namespace starfield { class Controller; }
|
||||
|
||||
//
|
||||
// Starfield rendering component.
|
||||
//
|
||||
// Starfield rendering component.
|
||||
class Stars {
|
||||
public:
|
||||
Stars();
|
||||
~Stars();
|
||||
public:
|
||||
Stars();
|
||||
~Stars();
|
||||
|
||||
//
|
||||
// Reads input file from URL. Returns true upon success.
|
||||
//
|
||||
// The limit parameter allows to reduce the number of stars
|
||||
// that are loaded, keeping the brightest ones.
|
||||
//
|
||||
bool readInput(const char* url, const char* cacheFile = 0l, unsigned limit = 200000);
|
||||
// Generate stars from random number
|
||||
// The numStars parameter sets the number of stars to generate.
|
||||
bool generate(unsigned numStars, unsigned seed);
|
||||
|
||||
//
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
// The parameters specifiy the field of view.
|
||||
//
|
||||
void render(float fovY, float aspect, float nearZ, float alpha);
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
// The parameters specifiy the field of view.
|
||||
void render(float fovY, float aspect, float nearZ, float alpha);
|
||||
|
||||
//
|
||||
// Sets the resolution for FOV culling.
|
||||
//
|
||||
// The parameter determines the number of tiles in azimuthal
|
||||
// and altitudinal directions.
|
||||
//
|
||||
// GPU resources are updated upon change in which case 'true'
|
||||
// is returned.
|
||||
//
|
||||
bool setResolution(unsigned k);
|
||||
// Sets the resolution for FOV culling.
|
||||
//
|
||||
// The parameter determines the number of tiles in azimuthal
|
||||
// and altitudinal directions.
|
||||
//
|
||||
// GPU resources are updated upon change in which case 'true'
|
||||
// is returned.
|
||||
bool setResolution(unsigned k);
|
||||
|
||||
//
|
||||
// Allows to alter the number of stars to be rendered given a
|
||||
// factor. The least brightest ones are omitted first.
|
||||
//
|
||||
// The further parameters determine when GPU resources should
|
||||
// be reallocated. Its value is fractional in respect to the
|
||||
// last number of stars 'n' that caused 'n * (1+overalloc)' to
|
||||
// be allocated. When the next call to setLOD causes the total
|
||||
// number of stars that could be rendered to drop below 'n *
|
||||
// (1-realloc)' or rises above 'n * (1+realloc)' GPU resources
|
||||
// are updated. Note that all parameters must be fractions,
|
||||
// that is within the range [0;1] and that 'overalloc' must be
|
||||
// greater than or equal to 'realloc'.
|
||||
//
|
||||
// The current level of detail is returned as a float in [0;1].
|
||||
//
|
||||
float changeLOD(float factor,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
// Returns true when stars have been loaded
|
||||
bool isStarsLoaded() const { return _starsLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
|
||||
bool getFileLoaded() const { return _fileLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
|
||||
// variables
|
||||
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _fileLoaded;
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _starsLoaded;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -283,13 +277,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -565,6 +565,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
jointNeckID = object.properties.at(0).value<qint64>();
|
||||
}
|
||||
glm::vec3 translation;
|
||||
glm::vec3 rotationOffset;
|
||||
glm::vec3 preRotation, rotation, postRotation;
|
||||
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
glm::vec3 scalePivot, rotationPivot;
|
||||
|
@ -578,6 +579,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
property.properties.at(5).value<double>(),
|
||||
property.properties.at(6).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationOffset") {
|
||||
rotationOffset = glm::vec3(property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>(),
|
||||
property.properties.at(6).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationPivot") {
|
||||
rotationPivot = glm::vec3(property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>(),
|
||||
|
@ -613,11 +619,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
}
|
||||
// see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html
|
||||
model.preRotation = glm::translate(translation) * glm::translate(rotationPivot) *
|
||||
glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
||||
model.preRotation = glm::translate(translation) * glm::translate(rotationOffset) *
|
||||
glm::translate(rotationPivot) * glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
||||
model.rotation = glm::quat(glm::radians(rotation));
|
||||
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) *
|
||||
glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot);
|
||||
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) *
|
||||
glm::translate(-rotationPivot) * glm::translate(scalePivot) *
|
||||
glm::scale(scale) * glm::translate(-scalePivot);
|
||||
models.insert(object.properties.at(0).value<qint64>(), model);
|
||||
|
||||
} else if (object.name == "Texture") {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
project(starfield)
|
||||
|
||||
# Only headers (that are facaded by the Stars.cpp file) here -
|
||||
# hence declared as custom target.
|
||||
|
||||
file(GLOB_RECURSE STARFIELD_SRCS *.h)
|
||||
add_custom_target("starfield" SOURCES ${STARFIELD_SRCS})
|
||||
|
35
interface/src/starfield/Config.h
Normal file → Executable file
35
interface/src/starfield/Config.h
Normal file → Executable file
|
@ -9,30 +9,6 @@
|
|||
#ifndef __interface__starfield__Config__
|
||||
#define __interface__starfield__Config__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Compile time configuration:
|
||||
//
|
||||
|
||||
#ifndef STARFIELD_HEMISPHERE_ONLY
|
||||
#define STARFIELD_HEMISPHERE_ONLY 0 // set to 1 for hemisphere only
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_LOW_MEMORY
|
||||
#define STARFIELD_LOW_MEMORY 0 // set to 1 not to use 16-bit types
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_DEBUG_CULLING
|
||||
#define STARFIELD_DEBUG_CULLING 0 // set to 1 to peek behind the scenes
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_MULTITHREADING
|
||||
#define STARFIELD_MULTITHREADING 0
|
||||
#endif
|
||||
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
|
@ -49,11 +25,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
@ -88,14 +59,8 @@ namespace starfield {
|
|||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
typedef uint16_t nuint;
|
||||
typedef uint32_t wuint;
|
||||
#else
|
||||
typedef uint32_t nuint;
|
||||
typedef uint64_t wuint;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
63
interface/src/starfield/Controller.cpp
Executable file
63
interface/src/starfield/Controller.cpp
Executable file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// starfield/Controller.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/16/13
|
||||
// Portions of code based on earlier work by Tobias Schwinger.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Controller.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool Controller::computeStars(unsigned numStars, unsigned seed) {
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
Generator::computeStarPositions(_inputSequence, numStars, seed);
|
||||
|
||||
this->retile(numStars, _tileResolution);
|
||||
|
||||
qDebug("Total time to generate stars: %llu msec\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::setResolution(unsigned tileResolution) {
|
||||
if (tileResolution <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tileResolution != _tileResolution) {
|
||||
|
||||
this->retile(_numStars, tileResolution);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
Renderer* renderer = _renderer;
|
||||
|
||||
if (renderer != 0l) {
|
||||
renderer->render(perspective, angle, orientation, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::retile(unsigned numStars, unsigned tileResolution) {
|
||||
Tiling tiling(tileResolution);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
|
||||
recreateRenderer(numStars, tileResolution);
|
||||
|
||||
_tileResolution = tileResolution;
|
||||
}
|
||||
|
||||
void Controller::recreateRenderer(unsigned numStars, unsigned tileResolution) {
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, numStars, tileResolution);
|
||||
}
|
416
interface/src/starfield/Controller.h
Normal file → Executable file
416
interface/src/starfield/Controller.h
Normal file → Executable file
|
@ -3,425 +3,39 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Controller__
|
||||
#define __interface__starfield__Confroller__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Data pipeline
|
||||
// =============
|
||||
//
|
||||
// ->> readInput -(load)--+---- (get brightness & sort) ---> brightness LUT
|
||||
// | |
|
||||
// ->> setResolution --+ | >extractBrightnessLevels<
|
||||
// V |
|
||||
// (sort by (tile,brightness))
|
||||
// | |
|
||||
// ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterize
|
||||
// V V when enough data on-GPU)
|
||||
// (filter by max-LOD brightness,
|
||||
// build tile info for rendering)
|
||||
// | |
|
||||
// V >recreateRenderer<
|
||||
// (set new renderer)/
|
||||
//
|
||||
//
|
||||
// (process), ->> entry point, ---> data flow, >internal routine<
|
||||
//
|
||||
// (member functions are ordered by data flow)
|
||||
|
||||
//
|
||||
// Still open
|
||||
// ==========
|
||||
//
|
||||
// o atomics/mutexes need to be added as annotated in the source to allow
|
||||
// concurrent threads to pull the strings to e.g. have a low priority
|
||||
// thread run the data pipeline for update -- rendering is wait-free
|
||||
//
|
||||
#include <time.h>
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
#include "starfield/Loader.h"
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
|
||||
Controller() :
|
||||
_tileResolution(20),
|
||||
_lodFraction(1.0),
|
||||
_lodLowWaterMark(0.8),
|
||||
_lodHighWaterMark(1.0),
|
||||
_lodOveralloc(1.2),
|
||||
_lodNalloc(0),
|
||||
_lodNRender(0),
|
||||
_lodBrightness(0),
|
||||
_lodAllocBrightness(0),
|
||||
_renderer(0l) {
|
||||
}
|
||||
|
||||
~Controller() {
|
||||
delete _renderer;
|
||||
}
|
||||
|
||||
#if !STARFIELD_MULTITHREADING
|
||||
#define lock
|
||||
#define _(x)
|
||||
#endif
|
||||
|
||||
bool readInput(const char* url, const char* cacheFile, unsigned limit)
|
||||
{
|
||||
InputVertices vertices;
|
||||
|
||||
if (! Loader().loadVertices(vertices, url, cacheFile, limit))
|
||||
return false;
|
||||
|
||||
BrightnessLevels brightness;
|
||||
extractBrightnessLevels(brightness, vertices);
|
||||
|
||||
// input is read, now run the entire data pipeline on the new input
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
_inputSequence.swap(vertices);
|
||||
#if STARFIELD_MULTITHREADING
|
||||
unsigned k = _tileResolution.load(memory_order_relaxed);
|
||||
#else
|
||||
unsigned k = _tileResolution;
|
||||
#endif
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double rcpChange;
|
||||
|
||||
// we'll have to build a new LOD state for a new total N,
|
||||
// ideally keeping allocation size and number of vertices
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
size_t newLast = _inputSequence.size() - 1;
|
||||
|
||||
// reciprocal change N_old/N_new tells us how to scale
|
||||
// the fractions
|
||||
rcpChange = min(1.0, double(vertices.size()) / _inputSequence.size());
|
||||
|
||||
// initialization? use defaults / previously set values
|
||||
if (rcpChange == 0.0) {
|
||||
|
||||
rcpChange = 1.0;
|
||||
|
||||
nRender = toBufSize(_lodFraction * newLast);
|
||||
n = min(newLast, toBufSize(_lodOveralloc * nRender));
|
||||
|
||||
} else {
|
||||
|
||||
// cannot allocate or render more than we have
|
||||
n = min(newLast, _lodNalloc);
|
||||
nRender = min(newLast, _lodNRender);
|
||||
}
|
||||
|
||||
// determine new minimum brightness levels
|
||||
bMin = brightness[n];
|
||||
b = brightness[nRender];
|
||||
|
||||
// adjust n
|
||||
n = std::upper_bound(
|
||||
brightness.begin() + n - 1,
|
||||
brightness.end(),
|
||||
bMin, GreaterBrightness() ) - brightness.begin();
|
||||
}
|
||||
|
||||
// invoke next stage
|
||||
try {
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
} catch (...) {
|
||||
|
||||
// rollback transaction and rethrow
|
||||
vertices.swap(_inputSequence);
|
||||
throw;
|
||||
}
|
||||
|
||||
// finally publish the new LOD state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodBrightnessSequence.swap(brightness);
|
||||
_lodFraction *= rcpChange;
|
||||
_lodLowWaterMark *= rcpChange;
|
||||
_lodHighWaterMark *= rcpChange;
|
||||
_lodOveralloc *= rcpChange;
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
_lodAllocBrightness = bMin;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setResolution(unsigned k) {
|
||||
|
||||
if (k <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// printLog("Stars.cpp: setResolution(%d)\n", k);
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
if (k != _tileResolution.load(memory_order_relaxed))
|
||||
#else
|
||||
if (k != _tileResolution)
|
||||
#endif
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
unsigned n;
|
||||
BrightnessLevel b, bMin;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
n = _lodNalloc;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
b = _lodBrightness;
|
||||
#endif
|
||||
bMin = _lodAllocBrightness;
|
||||
}
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double changeLOD(double factor, double overalloc, double realloc) {
|
||||
|
||||
assert(overalloc >= realloc && realloc >= 0.0);
|
||||
assert(overalloc <= 1.0 && realloc <= 1.0);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: changeLOD(%lf, %lf, %lf)\n", factor, overalloc, realloc);
|
||||
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double fraction, lwm, hwm;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
Controller() : _tileResolution(20), _renderer(0l) { }
|
||||
|
||||
// acuire a consistent copy of the current LOD state
|
||||
fraction = _lodFraction;
|
||||
lwm = _lodLowWaterMark;
|
||||
hwm = _lodHighWaterMark;
|
||||
size_t last = _lodBrightnessSequence.size() - 1;
|
||||
|
||||
// apply factor
|
||||
fraction = max(0.0, min(1.0, fraction * factor));
|
||||
|
||||
// calculate allocation size and corresponding brightness
|
||||
// threshold
|
||||
double oaFract = std::min(fraction * (1.0 + overalloc), 1.0);
|
||||
n = toBufSize(oaFract * last);
|
||||
bMin = _lodBrightnessSequence[n];
|
||||
n = std::upper_bound(
|
||||
_lodBrightnessSequence.begin() + n - 1,
|
||||
_lodBrightnessSequence.end(),
|
||||
bMin, GreaterBrightness() ) - _lodBrightnessSequence.begin();
|
||||
|
||||
// also determine number of vertices to render and brightness
|
||||
nRender = toBufSize(fraction * last);
|
||||
// Note: nRender does not have to be accurate
|
||||
b = _lodBrightnessSequence[nRender];
|
||||
// this setting controls the renderer, also keep b as the
|
||||
// brightness becomes volatile as soon as the mutex is
|
||||
// released, so keep b
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
|
||||
// printLog("Stars.cpp: "
|
||||
// "fraction = %lf, oaFract = %lf, n = %d, n' = %d, bMin = %d, b = %d\n",
|
||||
// fraction, oaFract, toBufSize(oaFract * last)), n, bMin, b);
|
||||
|
||||
// will not have to reallocate? set new fraction right away
|
||||
// (it is consistent with the rest of the state in this case)
|
||||
if (fraction >= _lodLowWaterMark
|
||||
&& fraction <= _lodHighWaterMark) {
|
||||
|
||||
_lodFraction = fraction;
|
||||
return fraction;
|
||||
}
|
||||
}
|
||||
|
||||
// reallocate
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
recreateRenderer(n, _tileResolution, b, bMin);
|
||||
|
||||
// printLog("Stars.cpp: LOD reallocation\n");
|
||||
|
||||
// publish new lod state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
|
||||
_lodFraction = fraction;
|
||||
_lodLowWaterMark = fraction * (1.0 - realloc);
|
||||
_lodHighWaterMark = fraction * (1.0 + realloc);
|
||||
_lodOveralloc = fraction * (1.0 + overalloc);
|
||||
_lodAllocBrightness = bMin;
|
||||
}
|
||||
}
|
||||
return fraction;
|
||||
}
|
||||
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check out renderer
|
||||
Renderer* renderer = _renderer.exchange(0l);
|
||||
#else
|
||||
Renderer* renderer = _renderer;
|
||||
#endif
|
||||
|
||||
// have it render
|
||||
if (renderer != 0l) {
|
||||
#if STARFIELD_MULTITHREADING
|
||||
BrightnessLevel b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
BrightnessLevel b = _lodBrightness;
|
||||
#endif
|
||||
renderer->render(perspective, angle, orientation, b, alpha);
|
||||
}
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check in - or dispose if there is a new one
|
||||
Renderer* newOne = 0l;
|
||||
if (! _renderer.compare_exchange_strong(newOne, renderer)) {
|
||||
|
||||
assert(!! newOne);
|
||||
delete renderer;
|
||||
}
|
||||
#else
|
||||
# undef lock
|
||||
# undef _
|
||||
#endif
|
||||
}
|
||||
|
||||
~Controller() { delete _renderer; }
|
||||
|
||||
bool computeStars(unsigned numStars, unsigned seed);
|
||||
bool setResolution(unsigned tileResolution);
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha);
|
||||
private:
|
||||
void retile(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
void retile(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
void recreateRenderer(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
Tiling tiling(k);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: recreateRenderer(%d, %d, %d, %d)\n", n, k, b, bMin);
|
||||
|
||||
recreateRenderer(n, k, b, bMin);
|
||||
|
||||
_tileResolution = k;
|
||||
}
|
||||
|
||||
void recreateRenderer(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
delete _renderer.exchange(new Renderer(_inputSequence, n, k, b, bMin) );
|
||||
#else
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, n, k, b, bMin);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline size_t toBufSize(double f) {
|
||||
return size_t(floor(f + 0.5f));
|
||||
}
|
||||
|
||||
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel> {
|
||||
|
||||
typedef Radix2IntegerScanner<BrightnessLevel> base;
|
||||
|
||||
BrightnessSortScanner() : base(BrightnessBits) { }
|
||||
|
||||
bool bit(BrightnessLevel const& k, state_type& s) {
|
||||
|
||||
// bit is inverted to achieve descending order
|
||||
return ! base::bit(k,s);
|
||||
}
|
||||
};
|
||||
|
||||
static void extractBrightnessLevels(BrightnessLevels& dst,
|
||||
InputVertices const& src) {
|
||||
dst.clear();
|
||||
dst.reserve(src.size());
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i)
|
||||
dst.push_back( getBrightness(i->getColor()) );
|
||||
|
||||
radix2InplaceSort(dst.begin(), dst.end(), BrightnessSortScanner());
|
||||
}
|
||||
|
||||
InputVertices _inputSequence;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
mutex _inputMutex;
|
||||
atomic<unsigned> _tileResolution;
|
||||
|
||||
mutex _lodStateMutex;
|
||||
#else
|
||||
unsigned _tileResolution;
|
||||
#endif
|
||||
double _lodFraction;
|
||||
double _lodLowWaterMark;
|
||||
double _lodHighWaterMark;
|
||||
double _lodOveralloc;
|
||||
size_t _lodNalloc;
|
||||
size_t _lodNRender;
|
||||
BrightnessLevels _lodBrightnessSequence;
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
atomic<BrightnessLevel> _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
atomic<Renderer*> _renderer;
|
||||
|
||||
typedef lock_guard<mutex> lock;
|
||||
#else
|
||||
BrightnessLevel _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
Renderer* _renderer;
|
||||
|
||||
#undef lock
|
||||
#undef _
|
||||
#endif
|
||||
InputVertices _inputSequence;
|
||||
unsigned _tileResolution;
|
||||
unsigned _numStars;
|
||||
Renderer* _renderer;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
53
interface/src/starfield/Generator.cpp
Normal file
53
interface/src/starfield/Generator.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// starfield/Generator.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
const float Generator::STAR_COLORIZATION = 0.1;
|
||||
|
||||
void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) {
|
||||
InputVertices* vertices = & destination;
|
||||
//_limit = limit;
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
srand(seed);
|
||||
|
||||
vertices->clear();
|
||||
vertices->reserve(limit);
|
||||
|
||||
const unsigned NUM_DEGREES = 360;
|
||||
|
||||
|
||||
for(int star = 0; star < limit; ++star) {
|
||||
float azimuth, altitude;
|
||||
azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES;
|
||||
altitude = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES / 2) - NUM_DEGREES / 4;
|
||||
|
||||
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
|
||||
}
|
||||
|
||||
qDebug("Took %llu msec to generate stars.\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
}
|
||||
|
||||
// computeStarColor
|
||||
// - Generate a star color.
|
||||
//
|
||||
// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
|
||||
//
|
||||
// 0 = completely black & white
|
||||
// 1 = very colorful
|
||||
unsigned Generator::computeStarColor(float colorization) {
|
||||
unsigned char red = rand() % 256;
|
||||
unsigned char green = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
unsigned char blue = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
return red | green << 8 | blue << 16;
|
||||
}
|
37
interface/src/starfield/Generator.h
Normal file
37
interface/src/starfield/Generator.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// starfield/Generator.h
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Generator__
|
||||
#define __interface__starfield__Generator__
|
||||
|
||||
#include <locale.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
class Generator {
|
||||
|
||||
public:
|
||||
Generator() {}
|
||||
~Generator() {}
|
||||
|
||||
static void computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed);
|
||||
static unsigned computeStarColor(float colorization);
|
||||
|
||||
private:
|
||||
static const float STAR_COLORIZATION;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// starfield/data/BrightnessLevel.h
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__BrightnessLevel__
|
||||
#define __interface__starfield__data__BrightnessLevel__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
typedef nuint BrightnessLevel;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
const unsigned BrightnessBits = 16u;
|
||||
#else
|
||||
const unsigned BrightnessBits = 18u;
|
||||
#endif
|
||||
const BrightnessLevel BrightnessMask = (1u << (BrightnessBits)) - 1u;
|
||||
|
||||
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
||||
|
||||
BrightnessLevel getBrightness(unsigned c) {
|
||||
|
||||
unsigned r = (c >> 16) & 0xff;
|
||||
unsigned g = (c >> 8) & 0xff;
|
||||
unsigned b = c & 0xff;
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
return BrightnessLevel((r*r+g*g+b*b) >> 2);
|
||||
#else
|
||||
return BrightnessLevel(r*r+g*g+b*b);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
struct GreaterBrightness {
|
||||
|
||||
bool operator()(InputVertex const& lhs, InputVertex const& rhs) const {
|
||||
return getBrightness(lhs.getColor())
|
||||
> getBrightness(rhs.getColor());
|
||||
}
|
||||
bool operator()(BrightnessLevel lhs, GpuVertex const& rhs) const {
|
||||
return lhs > getBrightness(rhs.getColor());;
|
||||
}
|
||||
bool operator()(BrightnessLevel lhs, BrightnessLevel rhs) const {
|
||||
return lhs > rhs;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#endif
|
||||
|
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// starfield/data/GpuVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
GpuVertex::GpuVertex(InputVertex const& inputVertex) {
|
||||
_color = inputVertex.getColor();
|
||||
float azimuth = inputVertex.getAzimuth();
|
||||
float altitude = inputVertex.getAltitude();
|
||||
|
||||
// compute altitude/azimuth into X/Y/Z point on a sphere
|
||||
_valX = sin(azimuth) * cos(altitude);
|
||||
_valY = sin(altitude);
|
||||
_valZ = -cos(azimuth) * cos(altitude);
|
||||
}
|
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__GpuVertex__
|
||||
#define __interface__starfield__data__GpuVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -21,33 +18,18 @@ namespace starfield {
|
|||
public:
|
||||
GpuVertex() { }
|
||||
|
||||
GpuVertex(InputVertex const& in) {
|
||||
|
||||
_color = in.getColor();
|
||||
float azi = in.getAzimuth();
|
||||
float alt = in.getAltitude();
|
||||
|
||||
// ground vector in x/z plane...
|
||||
float gx = sin(azi);
|
||||
float gz = -cos(azi);
|
||||
|
||||
// ...elevated in y direction by altitude
|
||||
float exz = cos(alt);
|
||||
_valX = gx * exz;
|
||||
_valY = sin(alt);
|
||||
_valZ = gz * exz;
|
||||
}
|
||||
GpuVertex(InputVertex const& inputVertex);
|
||||
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
24
interface/src/starfield/data/InputVertex.cpp
Executable file
24
interface/src/starfield/data/InputVertex.cpp
Executable file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// starfield/data/InputVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17.13.
|
||||
// Based on code by Tobias Schwinger 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
InputVertex::InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
_color = color | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__InputVertex__
|
||||
#define __interface__starfield__data__InputVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -20,33 +17,21 @@ namespace starfield {
|
|||
class InputVertex {
|
||||
public:
|
||||
|
||||
InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
|
||||
_color = ((color >> 16) & 0xffu) | (color & 0xff00u) |
|
||||
((color << 16) & 0xff0000u) | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
||||
InputVertex(float azimuth, float altitude, unsigned color);
|
||||
|
||||
float getAzimuth() const { return _azimuth; }
|
||||
float getAltitude() const { return _altitude; }
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
};
|
||||
|
||||
typedef std::vector<InputVertex> InputVertices;
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
|
@ -9,30 +9,22 @@
|
|||
#ifndef __interface__starfield__data__Tile__
|
||||
#define __interface__starfield__data__Tile__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
struct Tile {
|
||||
|
||||
nuint offset;
|
||||
nuint count;
|
||||
BrightnessLevel lod;
|
||||
nuint flags;
|
||||
nuint offset;
|
||||
nuint count;
|
||||
nuint flags;
|
||||
|
||||
// flags
|
||||
static uint16_t const checked = 1;
|
||||
static uint16_t const visited = 2;
|
||||
static uint16_t const render = 4;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// starfield/renderer/Renderer.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on earlier work by Tobias Schwinger 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
Renderer::Renderer(InputVertices const& stars, unsigned numStars, unsigned tileResolution) : _dataArray(0l),
|
||||
_tileArray(0l), _tiling(tileResolution) {
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(tileResolution);
|
||||
size_t numTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[numStars];
|
||||
_tileArray = new Tile[numTiles + 1];
|
||||
_batchOffs = new GLint[numTiles * 2];
|
||||
_batchCountArray = new GLsizei[numTiles * 2];
|
||||
|
||||
prepareVertexData(stars, numStars, tiling);
|
||||
|
||||
this->glUpload(numStars);
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void Renderer::render(float perspective, float aspect, mat4 const& orientation, float alpha) {
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude, -Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
}
|
||||
|
||||
// renderer construction
|
||||
|
||||
void Renderer::prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
|
||||
size_t tileIndex = tiling.getTileIndex(i->getAzimuth(), i->getAltitude());
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
Tile* lastTile = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
tile->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offset = vertexIndex; ++tile != lastTile ;) {
|
||||
tile->offset = offset, tile->count = 0u, tile->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
lastTile->offset = vertexIndex;
|
||||
lastTile->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
++count_active;
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
assert(vertexIndex == numStars);
|
||||
|
||||
// flush last tile (see above)
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
tile->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++tile != e;) {
|
||||
tile->offset = vertexIndex, tile->count = 0u, tile->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::visitTile(Tile* tile) {
|
||||
unsigned index = tile - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
return isTileVisible(index);
|
||||
}
|
||||
|
||||
bool Renderer::isTileVisible(unsigned index) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (index % stride) * slice;
|
||||
float altitude = (index / stride) * slice - Radians::halfPi();
|
||||
float groundX = sin(azimuth);
|
||||
float groundZ = -cos(azimuth);
|
||||
float elevation = cos(altitude);
|
||||
vec3 tileCenter = vec3(groundX * elevation, sin(altitude), groundZ * elevation);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
unsigned Renderer::prepareBatch(unsigned const* indices, unsigned const* indicesEnd) {
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs; i != indicesEnd; ++i) {
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
// GL API handling
|
||||
|
||||
void Renderer::glAlloc() {
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
" vec3 c = gl_Color.rgb * 1.22;\n"
|
||||
" float s = min(max(tan((c.r + c.g + c.b) / 3), 1.0), 3.0);\n"
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha * 1.5;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glFree() {
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glUpload(GLsizei numStars) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER, numStars * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS, _batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool Renderer::TileSelection::select(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (tile < _tileArray || tile >= _tilesEnd || !! (tile->flags & Tile::checked)) {
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
tile->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(tile)) {
|
||||
// good one -> remember (for batching) and propagate
|
||||
tile->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::process(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (! (tile->flags & Tile::visited)) {
|
||||
tile->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::right(Renderer::TileSelection::Cursor& cursor) const {
|
||||
cursor.current += 1;
|
||||
if (cursor.current == cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
cursor.current = cursor.firstInRow;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::left(Renderer::TileSelection::Cursor& cursor) const {
|
||||
if (cursor.current == cursor.firstInRow) {
|
||||
cursor.current = cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
cursor.current -= 1;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::up(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current += numTiles;
|
||||
cursor.firstInRow += numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::down(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current -= numTiles;
|
||||
cursor.firstInRow -= numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::defer(Renderer::TileSelection::Cursor const& cursor) {
|
||||
*_stackPos++ = cursor;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::deferred(Renderer::TileSelection::Cursor& cursor) {
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
|
@ -3,24 +3,18 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__renderer__Renderer__
|
||||
#define __interface__starfield__renderer__Renderer__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
#include "starfield/data/Tile.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
#include "Tiling.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
//
|
||||
// FOV culling
|
||||
|
@ -66,174 +60,14 @@ namespace starfield {
|
|||
class Renderer {
|
||||
public:
|
||||
|
||||
Renderer(InputVertices const& src,
|
||||
size_t n,
|
||||
unsigned k,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) :
|
||||
|
||||
_dataArray(0l),
|
||||
_tileArray(0l),
|
||||
_tiling(k) {
|
||||
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(k);
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: could coalesce allocation for faster rebuild
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[n];
|
||||
_tileArray = new Tile[nTiles + 1];
|
||||
_batchOffs = new GLint[nTiles * 2];
|
||||
_batchCountArray = new GLsizei[nTiles * 2];
|
||||
|
||||
prepareVertexData(src, n, tiling, b, bMin);
|
||||
|
||||
this->glUpload(n);
|
||||
}
|
||||
|
||||
~Renderer() {
|
||||
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void render(float perspective,
|
||||
float aspect,
|
||||
mat4 const& orientation,
|
||||
BrightnessLevel minBright,
|
||||
float alpha) {
|
||||
|
||||
// printLog("
|
||||
// Stars.cpp: rendering at minimal brightness %d\n", minBright);
|
||||
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude,
|
||||
-Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
#if STARFIELD_HEMISPHERE_ONLY
|
||||
altitude = std::max(0.0f, altitude);
|
||||
#endif
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
mat4 matrix_debug = glm::translate(vec3(0.0f, 0.0f, -4.0f)) *
|
||||
glm::affineInverse(matrix);
|
||||
#endif
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
this->_minBright = minBright;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(),
|
||||
(TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# define matrix matrix_debug
|
||||
#endif
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch(
|
||||
(unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# undef matrix
|
||||
#endif
|
||||
}
|
||||
|
||||
Renderer(InputVertices const& src, unsigned numStars, unsigned tileResolution);
|
||||
~Renderer();
|
||||
void render(float perspective, float aspect, mat4 const& orientation, float alpha);
|
||||
|
||||
private:
|
||||
// renderer construction
|
||||
|
||||
void prepareVertexData(InputVertices const& src,
|
||||
size_t n, // <-- at bMin and brighter
|
||||
Tiling const& tiling,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].lod = b;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i) {
|
||||
|
||||
BrightnessLevel bv = getBrightness(i->getColor());
|
||||
// filter by alloc brightness
|
||||
if (bv >= bMin) {
|
||||
|
||||
size_t tileIndex = tiling.getTileIndex(
|
||||
i->getAzimuth(), i->getAltitude());
|
||||
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
Tile* tLast = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
t->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offs = vertexIndex; ++t != tLast ;) {
|
||||
t->offset = offs, t->count = 0u,
|
||||
t->lod = b, t->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
tLast->offset = vertexIndex;
|
||||
tLast->lod = b;
|
||||
tLast->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
if (bv >= b)
|
||||
++count_active;
|
||||
|
||||
// printLog("Stars.cpp: Vertex %d on tile #%d\n", vertexIndex, tileIndex);
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
}
|
||||
assert(vertexIndex == n);
|
||||
// flush last tile (see above)
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
t->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++t != e;) {
|
||||
t->offset = vertexIndex, t->count = 0u,
|
||||
t->lod = b, t->flags = 0;
|
||||
}
|
||||
}
|
||||
void prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling);
|
||||
|
||||
// FOV culling / LOD
|
||||
|
||||
|
@ -242,299 +76,65 @@ namespace starfield {
|
|||
|
||||
class TileSelection {
|
||||
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
|
||||
public:
|
||||
|
||||
TileSelection(Renderer& renderer, Tile const* tiles,
|
||||
Tile const* tiles_end, Cursor* stack) :
|
||||
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) {
|
||||
}
|
||||
public:
|
||||
TileSelection(Renderer& renderer, Tile const* tiles, Tile const* tiles_end, Cursor* stack) :
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) { }
|
||||
|
||||
protected:
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool select(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (t < _tileArray || t >= _tilesEnd ||
|
||||
!! (t->flags & Tile::checked)) {
|
||||
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
t->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(t)) {
|
||||
|
||||
// good one -> remember (for batching) and propagate
|
||||
t->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (! (t->flags & Tile::visited)) {
|
||||
|
||||
t->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void right(Cursor& c) const {
|
||||
|
||||
c.current += 1;
|
||||
if (c.current == c.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
c.current = c.firstInRow;
|
||||
}
|
||||
}
|
||||
void left(Cursor& c) const {
|
||||
|
||||
if (c.current == c.firstInRow) {
|
||||
c.current = c.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
c.current -= 1;
|
||||
}
|
||||
void up(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current += d;
|
||||
c.firstInRow += d;
|
||||
}
|
||||
void down(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current -= d;
|
||||
c.firstInRow -= d;
|
||||
}
|
||||
|
||||
void defer(Cursor const& t) {
|
||||
|
||||
*_stackPos++ = t;
|
||||
}
|
||||
|
||||
bool deferred(Cursor& cursor) {
|
||||
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected:
|
||||
bool select(Cursor const& cursor);
|
||||
bool process(Cursor const& cursor);
|
||||
void right(Cursor& cursor) const;
|
||||
void left(Cursor& cursor) const;
|
||||
void up(Cursor& cursor) const;
|
||||
void down(Cursor& cursor) const;
|
||||
void defer(Cursor const& cursor);
|
||||
bool deferred(Cursor& cursor);
|
||||
};
|
||||
|
||||
bool visitTile(Tile* t) {
|
||||
|
||||
unsigned index = t - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
if (! tileVisible(t, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t->lod != _minBright) {
|
||||
updateVertexCount(t, _minBright);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tileVisible(Tile* t, unsigned i) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (i % stride) * slice;
|
||||
float altitude = (i / stride) * slice - Radians::halfPi();
|
||||
float gx = sin(azimuth);
|
||||
float gz = -cos(azimuth);
|
||||
float exz = cos(altitude);
|
||||
vec3 tileCenter = vec3(gx * exz, sin(altitude), gz * exz);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
// printLog("Stars.cpp: checking tile #%d, w = %f, near = %f\n", i, w, nearClip);
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
void updateVertexCount(Tile* t, BrightnessLevel minBright) {
|
||||
|
||||
// a growing number of stars needs to be rendereed when the
|
||||
// minimum brightness decreases
|
||||
// perform a binary search in the so found partition for the
|
||||
// new vertex count of this tile
|
||||
|
||||
GpuVertex const* start = _dataArray + t[0].offset;
|
||||
GpuVertex const* end = _dataArray + t[1].offset;
|
||||
|
||||
assert(end >= start);
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
if (t->lod < minBright)
|
||||
end = start + t->count;
|
||||
else
|
||||
start += (t->count > 0 ? t->count - 1 : 0);
|
||||
|
||||
end = std::upper_bound(
|
||||
start, end, minBright, GreaterBrightness());
|
||||
|
||||
assert(end >= _dataArray + t[0].offset);
|
||||
|
||||
t->count = end - _dataArray - t[0].offset;
|
||||
t->lod = minBright;
|
||||
}
|
||||
|
||||
unsigned prepareBatch(unsigned const* indices,
|
||||
unsigned const* indicesEnd) {
|
||||
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs;
|
||||
i != indicesEnd; ++i) {
|
||||
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
bool visitTile(Tile* tile);
|
||||
bool isTileVisible(unsigned index);
|
||||
unsigned prepareBatch(unsigned const* indices, unsigned const* indicesEnd);
|
||||
|
||||
// GL API handling
|
||||
|
||||
void glAlloc() {
|
||||
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
|
||||
" vec3 c = gl_Color.rgb * 1.0125;\n"
|
||||
" float s = max(1.0, dot(c, c) * 0.7);\n"
|
||||
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glFree() {
|
||||
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glUpload(GLsizei n) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
n * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
//glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
|
||||
// printLog("Stars.cpp: rendering %d-multibatch\n", n_ranges);
|
||||
|
||||
// for (int i = 0; i < n_ranges; ++i)
|
||||
// printLog("Stars.cpp: Batch #%d - %d stars @ %d\n", i,
|
||||
// _batchOffs[i], _batchCountArray[i]);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS,
|
||||
_batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void glAlloc();
|
||||
void glFree();
|
||||
void glUpload(GLsizei numStars);
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha);
|
||||
|
||||
// variables
|
||||
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
|
||||
Tiling _tiling;
|
||||
Tiling _tiling;
|
||||
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
BrightnessLevel _minBright;
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
64
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
64
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
|
@ -9,64 +9,38 @@
|
|||
#ifndef __interface__starfield__renderer__Tiling__
|
||||
#define __interface__starfield__renderer__Tiling__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
const float LOG2 = 1.4426950408889634;
|
||||
|
||||
class Tiling {
|
||||
public:
|
||||
|
||||
Tiling(unsigned k) :
|
||||
_valK(k),
|
||||
_rcpSlice(k / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * 1.4426950408889634); // log2
|
||||
}
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _valK; }
|
||||
unsigned getAltitudinalTiles() const { return _valK / 2 + 1; }
|
||||
Tiling(unsigned tileResolution) : _tileResolution(tileResolution), _rcpSlice(tileResolution / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * LOG2); }
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _tileResolution; }
|
||||
unsigned getAltitudinalTiles() const { return _tileResolution / 2 + 1; }
|
||||
unsigned getTileIndexBits() const { return _nBits; }
|
||||
|
||||
unsigned getTileCount() const {
|
||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
||||
}
|
||||
|
||||
unsigned getTileIndex(float azimuth, float altitude) const {
|
||||
return discreteAzimuth(azimuth) +
|
||||
_valK * discreteAltitude(altitude);
|
||||
}
|
||||
|
||||
float getSliceAngle() const {
|
||||
return 1.0f / _rcpSlice;
|
||||
}
|
||||
unsigned getTileCount() const { return getAzimuthalTiles() * getAltitudinalTiles(); }
|
||||
unsigned getTileIndex(float azimuth, float altitude) const { return discreteAzimuth(azimuth) +
|
||||
_tileResolution * discreteAltitude(altitude); }
|
||||
float getSliceAngle() const { return 1.0f / _rcpSlice; }
|
||||
|
||||
private:
|
||||
|
||||
unsigned discreteAngle(float unsigned_angle) const {
|
||||
|
||||
return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f));
|
||||
}
|
||||
|
||||
unsigned discreteAzimuth(float a) const {
|
||||
return discreteAngle(a) % _valK;
|
||||
}
|
||||
|
||||
unsigned discreteAltitude(float a) const {
|
||||
return min(getAltitudinalTiles() - 1,
|
||||
discreteAngle(a + Radians::halfPi()) );
|
||||
}
|
||||
unsigned discreteAngle(float unsigned_angle) const { return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f)); }
|
||||
unsigned discreteAzimuth(float angle) const { return discreteAngle(angle) % _tileResolution; }
|
||||
unsigned discreteAltitude(float angle) const { return min( getAltitudinalTiles() - 1,
|
||||
discreteAngle(angle + Radians::halfPi()) ); }
|
||||
|
||||
// variables
|
||||
|
||||
unsigned _valK;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
unsigned _tileResolution;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// starfield/renderer/VertexOrder.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/22/13.
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool VertexOrder::bit(InputVertex const& vertex, state_type const& state) const {
|
||||
unsigned key = _tiling.getTileIndex(vertex.getAzimuth(), vertex.getAltitude());
|
||||
return base::bit(key, state);
|
||||
}
|
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
|
@ -9,37 +9,23 @@
|
|||
#ifndef __interface__starfield__renderer__VertexOrder__
|
||||
#define __interface__starfield__renderer__VertexOrder__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
/**
|
||||
* Defines the vertex order for the renderer as a bit extractor for
|
||||
* binary in-place Radix Sort.
|
||||
*/
|
||||
// Defines the vertex order for the renderer as a bit extractor for
|
||||
//binary in-place Radix Sort.
|
||||
|
||||
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
||||
{
|
||||
public:
|
||||
explicit VertexOrder(Tiling const& tiling) :
|
||||
|
||||
base(tiling.getTileIndexBits() + BrightnessBits),
|
||||
_tiling(tiling) {
|
||||
}
|
||||
base(tiling.getTileIndexBits()), _tiling(tiling) { }
|
||||
|
||||
bool bit(InputVertex const& v, state_type const& s) const {
|
||||
|
||||
// inspect (tile_index, brightness) tuples
|
||||
unsigned key = getBrightness(v.getColor()) ^ BrightnessMask;
|
||||
key |= _tiling.getTileIndex(
|
||||
v.getAzimuth(), v.getAltitude()) << BrightnessBits;
|
||||
return base::bit(key, s);
|
||||
}
|
||||
bool bit(InputVertex const& vertex, state_type const& state) const;
|
||||
|
||||
private:
|
||||
Tiling _tiling;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
#include <cstring>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#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));
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <Node.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
|
|
@ -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<int> _listenSources;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__PositionalAudioRingBuffer__) */
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include <VoxelConstants.h>
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,19 +23,9 @@
|
|||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
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),
|
||||
|
@ -43,17 +33,8 @@ Node::Node(sockaddr* publicSocket, sockaddr* localSocket, char type, uint16_t no
|
|||
_linkedData(NULL),
|
||||
_isAlive(true)
|
||||
{
|
||||
if (publicSocket) {
|
||||
_publicSocket = new sockaddr(*publicSocket);
|
||||
} else {
|
||||
_publicSocket = NULL;
|
||||
}
|
||||
|
||||
if (localSocket) {
|
||||
_localSocket = new sockaddr(*localSocket);
|
||||
} else {
|
||||
_localSocket = NULL;
|
||||
}
|
||||
setPublicSocket(publicSocket);
|
||||
setLocalSocket(localSocket);
|
||||
|
||||
pthread_mutex_init(&_mutex, 0);
|
||||
}
|
||||
|
@ -105,11 +86,39 @@ const char* Node::getTypeName() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::setPublicSocket(sockaddr* publicSocket) {
|
||||
if (_activeSocket == _publicSocket) {
|
||||
// if the active socket was the public socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (publicSocket) {
|
||||
_publicSocket = new sockaddr(*publicSocket);
|
||||
} else {
|
||||
_publicSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Node::setLocalSocket(sockaddr* localSocket) {
|
||||
if (_activeSocket == _localSocket) {
|
||||
// if the active socket was the local socket then reset it to NULL
|
||||
_activeSocket = NULL;
|
||||
}
|
||||
|
||||
if (localSocket) {
|
||||
_localSocket = new sockaddr(*localSocket);
|
||||
} else {
|
||||
_localSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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 +161,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();
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
#endif
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#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; }
|
||||
|
@ -46,9 +47,9 @@ public:
|
|||
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||
|
||||
sockaddr* getPublicSocket() const { return _publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
|
||||
void setPublicSocket(sockaddr* publicSocket);
|
||||
sockaddr* getLocalSocket() const { return _localSocket; }
|
||||
void setLocalSocket(sockaddr* localSocket) { _localSocket = localSocket; }
|
||||
void setLocalSocket(sockaddr* localSocket);
|
||||
|
||||
sockaddr* getActiveSocket() const { return _activeSocket; }
|
||||
|
||||
|
@ -78,7 +79,7 @@ private:
|
|||
Node& operator=(Node otherNode);
|
||||
|
||||
char _type;
|
||||
uint16_t _nodeID;
|
||||
QUuid _uuid;
|
||||
uint64_t _wakeMicrostamp;
|
||||
uint64_t _lastHeardMicrostamp;
|
||||
sockaddr* _publicSocket;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "NodeTypes.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UUID.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
|
@ -67,12 +68,13 @@ 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)
|
||||
_publicAddress(),
|
||||
_publicPort(0),
|
||||
_hasCompletedInitialSTUNFailure(false),
|
||||
_stunRequestsSinceSuccess(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -115,8 +117,6 @@ void NodeList::setDomainHostname(const QString& domainHostname) {
|
|||
_domainIP.clear();
|
||||
notifyDomainChanged();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void NodeList::timePingReply(sockaddr *nodeAddress, unsigned char *packetData) {
|
||||
|
@ -143,16 +143,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
|
||||
activateSocketFromNodeCommunication(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,22 +183,22 @@ 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,
|
||||
NULL,
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
|
@ -197,35 +206,32 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
|
||||
// find the node by the sockaddr
|
||||
Node* matchingNode = nodeWithAddress(senderAddress);
|
||||
|
||||
if (matchingNode) {
|
||||
return updateNodeWithData(matchingNode, packetData, dataBytes);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes) {
|
||||
int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes) {
|
||||
node->lock();
|
||||
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
if (senderAddress) {
|
||||
activateSocketFromNodeCommunication(senderAddress);
|
||||
}
|
||||
|
||||
if (node->getActiveSocket() || !senderAddress) {
|
||||
node->recordBytesReceived(dataBytes);
|
||||
|
||||
if (!node->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(node);
|
||||
}
|
||||
|
||||
int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
||||
node->unlock();
|
||||
|
||||
return numParsedBytes;
|
||||
} else {
|
||||
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
|
||||
node->unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!node->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(node);
|
||||
}
|
||||
|
||||
int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
||||
node->unlock();
|
||||
|
||||
return numParsedBytes;
|
||||
}
|
||||
|
||||
Node* NodeList::nodeWithAddress(sockaddr *senderAddress) {
|
||||
|
@ -238,9 +244,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);
|
||||
}
|
||||
}
|
||||
|
@ -281,13 +287,11 @@ void NodeList::reset() {
|
|||
clear();
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
delete[] _checkInPacket;
|
||||
_checkInPacket = NULL;
|
||||
|
||||
_numBytesCheckInPacket = 0;
|
||||
|
||||
delete _nodeTypesOfInterest;
|
||||
_nodeTypesOfInterest = NULL;
|
||||
|
||||
// refresh the owner UUID
|
||||
_ownerUUID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
void NodeList::setNodeTypesOfInterest(const char* nodeTypesOfInterest, int numNodeTypesOfInterest) {
|
||||
|
@ -298,7 +302,154 @@ 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;
|
||||
|
||||
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();
|
||||
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
qDebug("Sending intial stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
||||
STUN_SERVER_PORT,
|
||||
stunRequestPacket,
|
||||
sizeof(stunRequestPacket));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_stunRequestsSinceSuccess++;
|
||||
|
||||
if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||
if (!_hasCompletedInitialSTUNFailure) {
|
||||
// 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);
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
}
|
||||
|
||||
// reset the public address and port
|
||||
_publicAddress = QHostAddress::Null;
|
||||
_publicPort = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// reset the number of failed STUN requests since last success
|
||||
_stunRequestsSinceSuccess = 0;
|
||||
|
||||
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));
|
||||
|
||||
uint16_t newPublicPort = 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);
|
||||
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
if (newPublicAddress != _publicAddress || newPublicPort != _publicPort) {
|
||||
_publicAddress = newPublicAddress;
|
||||
_publicPort = newPublicPort;
|
||||
|
||||
qDebug("New public socket received from STUN server is %s:%hu\n",
|
||||
_publicAddress.toString().toLocal8Bit().constData(),
|
||||
_publicPort);
|
||||
|
||||
}
|
||||
|
||||
_hasCompletedInitialSTUNFailure = true;
|
||||
|
||||
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,41 +480,49 @@ void NodeList::sendDomainServerCheckIn(const char* assignmentUUID) {
|
|||
printedDomainServerIP = true;
|
||||
}
|
||||
|
||||
// construct the DS check in packet if we need to
|
||||
if (!_checkInPacket) {
|
||||
if (_publicAddress.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
// 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
|
||||
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);
|
||||
NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
|
||||
numBytesNodesOfInterest + sizeof(unsigned char);
|
||||
|
||||
_checkInPacket = new unsigned char[numPacketBytes];
|
||||
unsigned char* packetPosition = _checkInPacket;
|
||||
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 += populateTypeAndVersion(packetPosition, nodePacketType);
|
||||
|
||||
*(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;
|
||||
}
|
||||
// send our owner UUID or the null one
|
||||
QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
|
||||
memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
|
||||
packetPosition += rfcOwnerUUID.size();
|
||||
|
||||
packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket),
|
||||
// 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,
|
||||
|
@ -372,13 +531,19 @@ void NodeList::sendDomainServerCheckIn(const char* assignmentUUID) {
|
|||
packetPosition += numBytesNodesOfInterest;
|
||||
}
|
||||
|
||||
_numBytesCheckInPacket = packetPosition - _checkInPacket;
|
||||
_nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, checkInPacket, packetPosition - checkInPacket);
|
||||
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||
sendSTUNRequest();
|
||||
}
|
||||
|
||||
// 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 +553,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 +565,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 +577,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,40 +603,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));
|
||||
|
||||
// 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 {
|
||||
node->lock();
|
||||
|
||||
if (node->getType() == NODE_TYPE_AUDIO_MIXER ||
|
||||
node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||
|
@ -481,6 +646,19 @@ Node* NodeList::addOrUpdateNode(sockaddr* publicSocket, sockaddr* localSocket, c
|
|||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (!socketMatch(publicSocket, node->getPublicSocket())) {
|
||||
node->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *node << "\n";
|
||||
}
|
||||
|
||||
if (!socketMatch(localSocket, node->getLocalSocket())) {
|
||||
node->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *node << "\n";
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
|
||||
// we had this node already, do nothing for now
|
||||
return &*node;
|
||||
}
|
||||
|
@ -507,25 +685,50 @@ 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) {
|
||||
const uint64_t PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
||||
|
||||
void NodeList::possiblyPingInactiveNodes() {
|
||||
static timeval lastPing = {};
|
||||
|
||||
// make sure PING_INACTIVE_NODE_INTERVAL_USECS has elapsed since last ping
|
||||
if (usecTimestampNow() - usecTimestamp(&lastPing) >= PING_INACTIVE_NODE_INTERVAL_USECS) {
|
||||
gettimeofday(&lastPing, NULL);
|
||||
|
||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// we don't have an active link to this node, ping it to set that up
|
||||
pingPublicAndLocalSocketsForInactiveNode(&(*node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::activateSocketFromNodeCommunication(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -542,6 +745,22 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void NodeList::killNode(Node* node, bool mustLockNode) {
|
||||
if (mustLockNode) {
|
||||
node->lock();
|
||||
}
|
||||
|
||||
qDebug() << "Killed " << *node << "\n";
|
||||
|
||||
notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node->setAlive(false);
|
||||
|
||||
if (mustLockNode) {
|
||||
node->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void* removeSilentNodes(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
uint64_t checkTimeUsecs = 0;
|
||||
|
@ -555,12 +774,8 @@ void* removeSilentNodes(void *args) {
|
|||
node->lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||
|
||||
qDebug() << "Killed " << *node << "\n";
|
||||
|
||||
nodeList->notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node->setAlive(false);
|
||||
// kill this node, don't lock - we already did it
|
||||
nodeList->killNode(&(*node), false);
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
|
|
|
@ -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,22 +101,24 @@ 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 killNode(Node* node, bool mustLockNode = true);
|
||||
|
||||
void processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||
void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes);
|
||||
|
||||
int updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||
int updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes);
|
||||
int updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes);
|
||||
|
||||
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
|
@ -143,6 +140,7 @@ public:
|
|||
void addDomainListener(DomainChangeListener* listener);
|
||||
void removeDomainListener(DomainChangeListener* listener);
|
||||
|
||||
void possiblyPingInactiveNodes();
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
|
@ -153,6 +151,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 +162,17 @@ 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 _hasCompletedInitialSTUNFailure;
|
||||
unsigned int _stunRequestsSinceSuccess;
|
||||
|
||||
void handlePingReply(sockaddr *nodeAddress);
|
||||
void activateSocketFromNodeCommunication(sockaddr *nodeAddress);
|
||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||
|
||||
std::vector<NodeListHook*> _hooks;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#endif
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -263,16 +265,22 @@ bool UDPSocket::receive(sockaddr* recvAddress, void* receivedData, ssize_t* rece
|
|||
}
|
||||
|
||||
int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength) const {
|
||||
// send data via UDP
|
||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (sent_bytes != byteLength) {
|
||||
qDebug("Failed to send packet: %s\n", strerror(errno));
|
||||
return false;
|
||||
if (destAddress) {
|
||||
// send data via UDP
|
||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (sent_bytes != byteLength) {
|
||||
qDebug("Failed to send packet: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return sent_bytes;
|
||||
} else {
|
||||
qDebug("UDPSocket send called with NULL destination address - Likely a node with no active socket.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent_bytes;
|
||||
}
|
||||
|
||||
int UDPSocket::send(const char* destAddress, int destPort, const void* data, size_t byteLength) const {
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
const int NUM_BYTES_RFC4122_UUID = 16;
|
||||
|
||||
QString uuidStringWithoutCurlyBraces(const QUuid& uuid);
|
||||
|
||||
#endif /* defined(__hifi__UUID__) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <OctalCode.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <SceneUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <JurisdictionSender.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
|
@ -325,6 +327,9 @@ void VoxelServer::run() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER);
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// tell our NodeList about our desire to get notifications
|
||||
|
@ -429,9 +434,12 @@ 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();
|
||||
}
|
||||
|
||||
// ping our inactive nodes to punch holes with them
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
|
@ -440,22 +448,23 @@ 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);
|
||||
|
||||
NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength);
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
||||
nodeData->initializeVoxelSendThread(this);
|
||||
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node) {
|
||||
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
|
||||
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
class JurisdictionMap {
|
||||
public:
|
||||
|
@ -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<uint16_t, JurisdictionMap> NodeToJurisdictionMap;
|
||||
typedef std::map<QUuid, JurisdictionMap> NodeToJurisdictionMap;
|
||||
|
||||
|
||||
#endif /* defined(__hifi__JurisdictionMap__) */
|
||||
|
|
|
@ -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<uint16_t>::iterator nodeIterator = _nodesRequestingJurisdictions.begin();
|
||||
for (std::set<QUuid>::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();
|
||||
|
|
|
@ -35,6 +35,6 @@ protected:
|
|||
|
||||
private:
|
||||
JurisdictionMap* _jurisdictionMap;
|
||||
std::set<uint16_t> _nodesRequestingJurisdictions;
|
||||
std::set<QUuid> _nodesRequestingJurisdictions;
|
||||
};
|
||||
#endif // __shared__JurisdictionSender__
|
||||
|
|
|
@ -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<uint16_t,EditPacketBuffer>::iterator i = _pendingEditPackets.begin(); i != _pendingEditPackets.end(); i++) {
|
||||
for (std::map<QUuid, EditPacketBuffer>::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;
|
||||
}
|
||||
|
|
|
@ -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<uint16_t,EditPacketBuffer> _pendingEditPackets;
|
||||
std::map<QUuid, EditPacketBuffer> _pendingEditPackets;
|
||||
|
||||
// These are packets that are waiting to be processed because we don't yet know if there are voxel servers
|
||||
int _maxPendingMessages;
|
||||
|
|
|
@ -82,7 +82,7 @@ void VoxelNode::init(unsigned char * octalCode) {
|
|||
setVoxelSystem(NULL);
|
||||
_isDirty = true;
|
||||
_shouldRender = false;
|
||||
_sourceID = UNKNOWN_NODE_ID;
|
||||
_sourceUUIDKey = 0;
|
||||
calculateAABox();
|
||||
markWithChangedTime();
|
||||
|
||||
|
@ -169,6 +169,53 @@ void VoxelNode::setVoxelSystem(VoxelSystem* voxelSystem) {
|
|||
}
|
||||
}
|
||||
|
||||
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<QString, uint16_t> VoxelNode::_mapSourceUUIDsToKeys;
|
||||
std::map<uint16_t, QString> 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
|
||||
|
@ -1026,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
|
||||
|
|
|
@ -101,8 +101,12 @@ public:
|
|||
|
||||
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);
|
||||
|
@ -191,7 +195,15 @@ private:
|
|||
nodeColor _trueColor; /// Client and server, true color of this voxel, 4 bytes
|
||||
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
|
||||
|
||||
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<QString, uint16_t> _mapSourceUUIDsToKeys;
|
||||
static std::map<uint16_t, QString> _mapKeysToSourceUUIDs;
|
||||
|
||||
unsigned char _childBitmask; // 1 byte
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -1571,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;
|
||||
|
||||
|
@ -1831,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue