mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 02:03:36 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into keep_local_voxels
This commit is contained in:
commit
1b302f60f8
38 changed files with 1170 additions and 1505 deletions
|
@ -36,4 +36,4 @@ private:
|
||||||
std::vector<AudioInjector*> _audioInjectors;
|
std::vector<AudioInjector*> _audioInjectors;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Operative__) */
|
#endif /* defined(__hifi__Agent__) */
|
||||||
|
|
|
@ -81,6 +81,9 @@ void AudioMixer::run() {
|
||||||
NodeList *nodeList = NodeList::getInstance();
|
NodeList *nodeList = NodeList::getInstance();
|
||||||
nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER);
|
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;
|
ssize_t receivedBytes = 0;
|
||||||
|
|
||||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||||
|
@ -144,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++) {
|
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||||
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) node->getLinkedData();
|
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) node->getLinkedData();
|
||||||
if (positionalRingBuffer && positionalRingBuffer->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES)) {
|
if (positionalRingBuffer && positionalRingBuffer->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES)) {
|
||||||
|
@ -157,7 +163,7 @@ void AudioMixer::run() {
|
||||||
|
|
||||||
const int PHASE_DELAY_AT_90 = 20;
|
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();
|
AvatarAudioRingBuffer* nodeRingBuffer = (AvatarAudioRingBuffer*) node->getLinkedData();
|
||||||
|
|
||||||
// zero out the client mix for this node
|
// zero out the client mix for this node
|
||||||
|
@ -165,7 +171,8 @@ void AudioMixer::run() {
|
||||||
|
|
||||||
// loop through all other nodes that have sufficient audio to mix
|
// loop through all other nodes that have sufficient audio to mix
|
||||||
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
|
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()))) {
|
&& (otherNode != node || (otherNode == node && nodeRingBuffer->shouldLoopbackForNode()))) {
|
||||||
PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) otherNode->getLinkedData();
|
PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) otherNode->getLinkedData();
|
||||||
// based on our listen mode we will do this mixing...
|
// based on our listen mode we will do this mixing...
|
||||||
|
@ -333,7 +340,7 @@ void AudioMixer::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(clientPacket + numBytesPacketHeader, clientSamples, sizeof(clientSamples));
|
memcpy(clientPacket + numBytesPacketHeader, clientSamples, sizeof(clientSamples));
|
||||||
nodeList->getNodeSocket()->send(node->getPublicSocket(), clientPacket, sizeof(clientPacket));
|
nodeList->getNodeSocket()->send(node->getActiveSocket(), clientPacket, sizeof(clientPacket));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,39 +360,24 @@ void AudioMixer::run() {
|
||||||
// pull any new audio data from nodes off of the network stack
|
// pull any new audio data from nodes off of the network stack
|
||||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||||
packetVersionMatch(packetData)) {
|
packetVersionMatch(packetData)) {
|
||||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
|
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|
||||||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
|
|| packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||||
|
|| packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||||
|
|
||||||
unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData);
|
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) currentBuffer, NUM_BYTES_RFC4122_UUID));
|
|
||||||
|
|
||||||
Node* avatarNode = nodeList->addOrUpdateNode(nodeUUID,
|
|
||||||
NODE_TYPE_AGENT,
|
|
||||||
nodeAddress,
|
|
||||||
nodeAddress);
|
|
||||||
|
|
||||||
// temp activation of public socket before server ping/reply is setup
|
|
||||||
if (!avatarNode->getActiveSocket()) {
|
|
||||||
avatarNode->activatePublicSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
|
||||||
NUM_BYTES_RFC4122_UUID));
|
NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
Node* matchingInjector = nodeList->addOrUpdateNode(nodeUUID,
|
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||||
NODE_TYPE_AUDIO_INJECTOR,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
// give the new audio data to the matching injector node
|
if (matchingNode) {
|
||||||
nodeList->updateNodeWithData(matchingInjector, packetData, receivedBytes);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// let processNodeData handle it.
|
// let processNodeData handle it.
|
||||||
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
||||||
|
|
|
@ -97,6 +97,8 @@ void AvatarMixer::run() {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER);
|
nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER);
|
||||||
|
|
||||||
|
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
||||||
|
|
||||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||||
|
|
||||||
nodeList->startSilentNodeRemovalThread();
|
nodeList->startSilentNodeRemovalThread();
|
||||||
|
@ -123,6 +125,8 @@ void AvatarMixer::run() {
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodeList->possiblyPingInactiveNodes();
|
||||||
|
|
||||||
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
|
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
|
||||||
packetVersionMatch(packetData)) {
|
packetVersionMatch(packetData)) {
|
||||||
switch (packetData[0]) {
|
switch (packetData[0]) {
|
||||||
|
@ -131,10 +135,14 @@ void AvatarMixer::run() {
|
||||||
NUM_BYTES_RFC4122_UUID));
|
NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
// add or update the node in our list
|
// add or update the node in our list
|
||||||
avatarNode = nodeList->addOrUpdateNode(nodeUUID, NODE_TYPE_AGENT, &nodeAddress, &nodeAddress);
|
avatarNode = nodeList->nodeWithUUID(nodeUUID);
|
||||||
|
|
||||||
// parse positional data from an node
|
if (avatarNode) {
|
||||||
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
// parse positional data from an node
|
||||||
|
nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PACKET_TYPE_INJECT_AUDIO:
|
case PACKET_TYPE_INJECT_AUDIO:
|
||||||
broadcastAvatarData(nodeList, nodeUUID, &nodeAddress);
|
broadcastAvatarData(nodeList, nodeUUID, &nodeAddress);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -46,79 +46,121 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) {
|
||||||
const struct mg_request_info* ri = mg_get_request_info(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_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) {
|
const char URI_ASSIGNMENT[] = "/assignment";
|
||||||
// return a 200
|
const char URI_NODE[] = "/node";
|
||||||
mg_printf(connection, "%s", RESPONSE_200);
|
|
||||||
// upload the file
|
if (strcmp(ri->request_method, "GET") == 0) {
|
||||||
mg_upload(connection, "/tmp");
|
if (strcmp(ri->uri, "/assignments.json") == 0) {
|
||||||
|
// user is asking for json list of assignments
|
||||||
return 1;
|
|
||||||
} else if (strcmp(ri->uri, "/assignments.json") == 0) {
|
// start with a 200 response
|
||||||
// user is asking for json list of assignments
|
mg_printf(connection, "%s", RESPONSE_200);
|
||||||
|
|
||||||
// start with a 200 response
|
// setup the JSON
|
||||||
mg_printf(connection, "%s", RESPONSE_200);
|
QJsonObject assignmentJSON;
|
||||||
|
|
||||||
// setup the JSON
|
QJsonObject assignedNodesJSON;
|
||||||
QJsonObject assignmentJSON;
|
|
||||||
|
// enumerate the NodeList to find the assigned nodes
|
||||||
QJsonObject assignedNodesJSON;
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
|
|
||||||
// enumerate the NodeList to find the assigned nodes
|
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
|
||||||
|
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||||
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
if (node->getLinkedData()) {
|
||||||
|
// this is a node with assignment
|
||||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
QJsonObject assignedNodeJSON;
|
||||||
if (node->getLinkedData()) {
|
|
||||||
// this is a node with assignment
|
// add the assignment UUID
|
||||||
QJsonObject assignedNodeJSON;
|
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 uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
||||||
QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID());
|
queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString;
|
||||||
assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID;
|
|
||||||
|
|
||||||
// add the node socket information
|
// add this queued assignment to the JSON
|
||||||
assignedNodeJSON["public"] = jsonForSocket(node->getPublicSocket());
|
queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON;
|
||||||
assignedNodeJSON["local"] = jsonForSocket(node->getLocalSocket());
|
|
||||||
|
|
||||||
// re-format the type name so it matches the target name
|
// push forward the iterator to check the next assignment
|
||||||
QString nodeTypeName(node->getTypeName());
|
assignment++;
|
||||||
nodeTypeName = nodeTypeName.toLower();
|
}
|
||||||
nodeTypeName.replace(' ', '-');
|
|
||||||
|
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);
|
||||||
|
|
||||||
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;
|
// this was processed by civetweb
|
||||||
|
|
||||||
// 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
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
// have mongoose process this request from the document_root
|
// have mongoose process this request from the document_root
|
||||||
|
@ -160,6 +202,27 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const
|
||||||
domainServerInstance->_assignmentQueueMutex.unlock();
|
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) {
|
void DomainServer::nodeAdded(Node* node) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -169,26 +232,8 @@ void DomainServer::nodeKilled(Node* node) {
|
||||||
if (node->getLinkedData()) {
|
if (node->getLinkedData()) {
|
||||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
||||||
|
|
||||||
qDebug() << "Adding assignment" << *nodeAssignment << " back to queue.\n";
|
addReleasedAssignmentBackToQueue(nodeAssignment);
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd) {
|
unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd) {
|
||||||
|
|
|
@ -47,6 +47,7 @@ private:
|
||||||
void removeAssignmentFromQueue(Assignment* removableAssignment);
|
void removeAssignmentFromQueue(Assignment* removableAssignment);
|
||||||
bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI);
|
bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI);
|
||||||
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
|
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
|
||||||
|
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
|
|
|
@ -38,14 +38,11 @@ configure_file(InterfaceConfig.h.in ${PROJECT_BINARY_DIR}/includes/InterfaceConf
|
||||||
|
|
||||||
# grab the implementation and header files from src dirs
|
# grab the implementation and header files from src dirs
|
||||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||||
foreach(SUBDIR avatar devices renderer ui)
|
foreach(SUBDIR avatar devices renderer ui starfield)
|
||||||
file(GLOB SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
|
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
|
||||||
endforeach(SUBDIR)
|
endforeach(SUBDIR)
|
||||||
|
|
||||||
# project subdirectories
|
|
||||||
add_subdirectory(src/starfield)
|
|
||||||
|
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core REQUIRED)
|
||||||
find_package(Qt5Gui REQUIRED)
|
find_package(Qt5Gui REQUIRED)
|
||||||
find_package(Qt5Multimedia REQUIRED)
|
find_package(Qt5Multimedia REQUIRED)
|
||||||
|
|
|
@ -72,8 +72,8 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// Starfield information
|
// Starfield information
|
||||||
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
static unsigned STARFIELD_SEED = 1;
|
||||||
|
|
||||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||||
|
|
||||||
|
@ -2510,8 +2510,8 @@ void Application::displaySide(Camera& whichCamera) {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
"Application::displaySide() ... stars...");
|
"Application::displaySide() ... stars...");
|
||||||
if (!_stars.getFileLoaded()) {
|
if (!_stars.isStarsLoaded()) {
|
||||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
_stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED);
|
||||||
}
|
}
|
||||||
// should be the first rendering pass - w/o depth buffer / lighting
|
// should be the first rendering pass - w/o depth buffer / lighting
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
namespace { // .cpp-local
|
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 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_HORIZ = -20; // Distance to edge of screen (use negative value when width is negative).
|
||||||
int const BORDER_DISTANCE_VERT = 40; // Distance to edge of screen (use negative value when height is negative).
|
int 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
|
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 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[] = {
|
BandwidthMeter::ChannelInfo BandwidthMeter::_CHANNELS[] = {
|
||||||
|
@ -182,7 +184,7 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (totalMax < scaleMax * 0.5) {
|
if (totalMax < scaleMax * 0.5) {
|
||||||
_scaleMaxIndex = glm::max(0, _scaleMaxIndex-1);
|
_scaleMaxIndex = glm::max(0, _scaleMaxIndex - 1);
|
||||||
commit = true;
|
commit = true;
|
||||||
} else if (totalMax > scaleMax) {
|
} else if (totalMax > scaleMax) {
|
||||||
_scaleMaxIndex += 1;
|
_scaleMaxIndex += 1;
|
||||||
|
@ -190,10 +192,15 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
||||||
}
|
}
|
||||||
} while (commit);
|
} while (commit);
|
||||||
|
|
||||||
|
step = scaleMax / NUMBER_OF_MARKERS;
|
||||||
|
if (scaleMax < MIN_METER_SCALE) {
|
||||||
|
scaleMax = MIN_METER_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
// Render scale indicators
|
// Render scale indicators
|
||||||
setColorRGBA(COLOR_INDICATOR);
|
setColorRGBA(COLOR_INDICATOR);
|
||||||
for (int j = int((scaleMax + step - 0.000001) / step); --j > 0;) {
|
for (int j = NUMBER_OF_MARKERS; --j > 0;) {
|
||||||
renderVerticalLine(int(barWidth * j * step / scaleMax), 0, h);
|
renderVerticalLine(int(barWidth * j / NUMBER_OF_MARKERS), 0, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render bars
|
// Render bars
|
||||||
|
@ -220,11 +227,11 @@ void BandwidthMeter::render(int screenWidth, int screenHeight) {
|
||||||
// Render numbers
|
// Render numbers
|
||||||
char fmtBuf[8];
|
char fmtBuf[8];
|
||||||
setColorRGBA(COLOR_TEXT);
|
setColorRGBA(COLOR_TEXT);
|
||||||
sprintf(fmtBuf, "%0.2f", totalIn);
|
sprintf(fmtBuf, "%0.1f", totalIn);
|
||||||
_textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
_textRenderer.draw(glm::max(xIn - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||||
PADDING_HORIZ_VALUE),
|
PADDING_HORIZ_VALUE),
|
||||||
textYupperLine, fmtBuf);
|
textYupperLine, fmtBuf);
|
||||||
sprintf(fmtBuf, "%0.2f", totalOut);
|
sprintf(fmtBuf, "%0.1f", totalOut);
|
||||||
_textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
_textRenderer.draw(glm::max(xOut - fontMetrics.width(fmtBuf) - PADDING_HORIZ_VALUE,
|
||||||
PADDING_HORIZ_VALUE),
|
PADDING_HORIZ_VALUE),
|
||||||
textYlowerLine, fmtBuf);
|
textYlowerLine, fmtBuf);
|
||||||
|
|
|
@ -60,11 +60,11 @@ Menu::Menu() :
|
||||||
QMenu* fileMenu = addMenu("File");
|
QMenu* fileMenu = addMenu("File");
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
(addActionToQMenuAndActionHash(fileMenu,
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
MenuOption::AboutApp,
|
MenuOption::AboutApp,
|
||||||
0,
|
0,
|
||||||
this,
|
this,
|
||||||
SLOT(aboutApp())))->setMenuRole(QAction::AboutRole);
|
SLOT(aboutApp()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(addActionToQMenuAndActionHash(fileMenu,
|
(addActionToQMenuAndActionHash(fileMenu,
|
||||||
|
@ -72,12 +72,6 @@ Menu::Menu() :
|
||||||
0,
|
0,
|
||||||
this,
|
this,
|
||||||
SLOT(login())));
|
SLOT(login())));
|
||||||
|
|
||||||
(addActionToQMenuAndActionHash(fileMenu,
|
|
||||||
MenuOption::Preferences,
|
|
||||||
Qt::CTRL | Qt::Key_Comma,
|
|
||||||
this,
|
|
||||||
SLOT(editPreferences())))->setMenuRole(QAction::PreferencesRole);
|
|
||||||
|
|
||||||
addDisabledActionAndSeparator(fileMenu, "Voxels");
|
addDisabledActionAndSeparator(fileMenu, "Voxels");
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels()));
|
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()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest()));
|
||||||
addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true);
|
addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true);
|
||||||
|
|
||||||
(addActionToQMenuAndActionHash(fileMenu,
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
MenuOption::Quit,
|
MenuOption::Quit,
|
||||||
Qt::CTRL | Qt::Key_Q,
|
Qt::CTRL | Qt::Key_Q,
|
||||||
appInstance,
|
appInstance,
|
||||||
SLOT(quit())))->setMenuRole(QAction::QuitRole);
|
SLOT(quit()));
|
||||||
|
|
||||||
QMenu* editMenu = addMenu("Edit");
|
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::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels()));
|
||||||
addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
|
addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
|
||||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
|
addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
|
||||||
|
|
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 "InterfaceConfig.h"
|
||||||
#include "Stars.h"
|
#include "Stars.h"
|
||||||
|
|
||||||
#define __interface__Starfield_impl__
|
|
||||||
#include "starfield/Controller.h"
|
#include "starfield/Controller.h"
|
||||||
#undef __interface__Starfield_impl__
|
|
||||||
|
|
||||||
Stars::Stars() :
|
Stars::Stars() :
|
||||||
_controller(0l), _fileLoaded(false) {
|
_controller(0l), _starsLoaded(false) {
|
||||||
_controller = new starfield::Controller;
|
_controller = new starfield::Controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stars::~Stars() {
|
Stars::~Stars() {
|
||||||
delete _controller;
|
delete _controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
|
bool Stars::generate(unsigned numStars, unsigned seed) {
|
||||||
_fileLoaded = _controller->readInput(url, cacheFile, limit);
|
_starsLoaded = _controller->computeStars(numStars, seed);
|
||||||
return _fileLoaded;
|
return _starsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stars::setResolution(unsigned k) {
|
bool Stars::setResolution(unsigned k) {
|
||||||
return _controller->setResolution(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) {
|
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
||||||
|
|
||||||
// determine length of screen diagonal from quadrant height and aspect ratio
|
// 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
|
// pull the modelview matrix off the GL stack
|
||||||
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
|
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; }
|
namespace starfield { class Controller; }
|
||||||
|
|
||||||
//
|
// Starfield rendering component.
|
||||||
// Starfield rendering component.
|
|
||||||
//
|
|
||||||
class Stars {
|
class Stars {
|
||||||
public:
|
public:
|
||||||
Stars();
|
Stars();
|
||||||
~Stars();
|
~Stars();
|
||||||
|
|
||||||
//
|
// Generate stars from random number
|
||||||
// Reads input file from URL. Returns true upon success.
|
// The numStars parameter sets the number of stars to generate.
|
||||||
//
|
bool generate(unsigned numStars, unsigned seed);
|
||||||
// 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);
|
|
||||||
|
|
||||||
//
|
// Renders the starfield from a local viewer's perspective.
|
||||||
// Renders the starfield from a local viewer's perspective.
|
// The parameters specifiy the field of view.
|
||||||
// The parameters specifiy the field of view.
|
void render(float fovY, float aspect, float nearZ, float alpha);
|
||||||
//
|
|
||||||
void render(float fovY, float aspect, float nearZ, float alpha);
|
|
||||||
|
|
||||||
//
|
// Sets the resolution for FOV culling.
|
||||||
// Sets the resolution for FOV culling.
|
//
|
||||||
//
|
// The parameter determines the number of tiles in azimuthal
|
||||||
// The parameter determines the number of tiles in azimuthal
|
// and altitudinal directions.
|
||||||
// and altitudinal directions.
|
//
|
||||||
//
|
// GPU resources are updated upon change in which case 'true'
|
||||||
// GPU resources are updated upon change in which case 'true'
|
// is returned.
|
||||||
// is returned.
|
bool setResolution(unsigned k);
|
||||||
//
|
|
||||||
bool setResolution(unsigned k);
|
|
||||||
|
|
||||||
//
|
// Returns true when stars have been loaded
|
||||||
// Allows to alter the number of stars to be rendered given a
|
bool isStarsLoaded() const { return _starsLoaded; };
|
||||||
// factor. The least brightest ones are omitted first.
|
private:
|
||||||
//
|
// don't copy/assign
|
||||||
// The further parameters determine when GPU resources should
|
Stars(Stars const&); // = delete;
|
||||||
// be reallocated. Its value is fractional in respect to the
|
Stars& operator=(Stars const&); // delete;
|
||||||
// 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);
|
|
||||||
|
|
||||||
bool getFileLoaded() const { return _fileLoaded; };
|
starfield::Controller* _controller;
|
||||||
private:
|
|
||||||
// don't copy/assign
|
bool _starsLoaded;
|
||||||
Stars(Stars const&); // = delete;
|
|
||||||
Stars& operator=(Stars const&); // delete;
|
|
||||||
|
|
||||||
// variables
|
|
||||||
|
|
||||||
starfield::Controller* _controller;
|
|
||||||
|
|
||||||
bool _fileLoaded;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ const quint16 FACESHIFT_PORT = 33433;
|
||||||
|
|
||||||
Faceshift::Faceshift() :
|
Faceshift::Faceshift() :
|
||||||
_tcpEnabled(false),
|
_tcpEnabled(false),
|
||||||
|
_tcpRetryCount(0),
|
||||||
_lastTrackingStateReceived(0),
|
_lastTrackingStateReceived(0),
|
||||||
_eyeGazeLeftPitch(0.0f),
|
_eyeGazeLeftPitch(0.0f),
|
||||||
_eyeGazeLeftYaw(0.0f),
|
_eyeGazeLeftYaw(0.0f),
|
||||||
|
@ -88,7 +89,9 @@ void Faceshift::setTCPEnabled(bool enabled) {
|
||||||
|
|
||||||
void Faceshift::connectSocket() {
|
void Faceshift::connectSocket() {
|
||||||
if (_tcpEnabled) {
|
if (_tcpEnabled) {
|
||||||
qDebug("Faceshift: Connecting...\n");
|
if (!_tcpRetryCount) {
|
||||||
|
qDebug("Faceshift: Connecting...\n");
|
||||||
|
}
|
||||||
|
|
||||||
_tcpSocket.connectToHost("localhost", FACESHIFT_PORT);
|
_tcpSocket.connectToHost("localhost", FACESHIFT_PORT);
|
||||||
_tracking = false;
|
_tracking = false;
|
||||||
|
@ -105,11 +108,14 @@ void Faceshift::noteConnected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
||||||
qDebug() << "Faceshift: " << _tcpSocket.errorString() << "\n";
|
if (!_tcpRetryCount) {
|
||||||
|
// Only spam log with fail to connect the first time, so that we can keep waiting for server
|
||||||
// reconnect after a delay
|
qDebug() << "Faceshift: " << _tcpSocket.errorString() << "\n";
|
||||||
|
}
|
||||||
|
// retry connection after a 2 second delay
|
||||||
if (_tcpEnabled) {
|
if (_tcpEnabled) {
|
||||||
QTimer::singleShot(1000, this, SLOT(connectSocket()));
|
_tcpRetryCount++;
|
||||||
|
QTimer::singleShot(2000, this, SLOT(connectSocket()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ private:
|
||||||
QUdpSocket _udpSocket;
|
QUdpSocket _udpSocket;
|
||||||
fs::fsBinaryStream _stream;
|
fs::fsBinaryStream _stream;
|
||||||
bool _tcpEnabled;
|
bool _tcpEnabled;
|
||||||
|
int _tcpRetryCount;
|
||||||
bool _tracking;
|
bool _tracking;
|
||||||
uint64_t _lastTrackingStateReceived;
|
uint64_t _lastTrackingStateReceived;
|
||||||
|
|
||||||
|
|
|
@ -565,6 +565,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
jointNeckID = object.properties.at(0).value<qint64>();
|
jointNeckID = object.properties.at(0).value<qint64>();
|
||||||
}
|
}
|
||||||
glm::vec3 translation;
|
glm::vec3 translation;
|
||||||
|
glm::vec3 rotationOffset;
|
||||||
glm::vec3 preRotation, rotation, postRotation;
|
glm::vec3 preRotation, rotation, postRotation;
|
||||||
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||||
glm::vec3 scalePivot, rotationPivot;
|
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(5).value<double>(),
|
||||||
property.properties.at(6).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") {
|
} else if (property.properties.at(0) == "RotationPivot") {
|
||||||
rotationPivot = glm::vec3(property.properties.at(4).value<double>(),
|
rotationPivot = glm::vec3(property.properties.at(4).value<double>(),
|
||||||
property.properties.at(5).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
|
// see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html
|
||||||
model.preRotation = glm::translate(translation) * glm::translate(rotationPivot) *
|
model.preRotation = glm::translate(translation) * glm::translate(rotationOffset) *
|
||||||
glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
glm::translate(rotationPivot) * glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
||||||
model.rotation = glm::quat(glm::radians(rotation));
|
model.rotation = glm::quat(glm::radians(rotation));
|
||||||
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) *
|
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) *
|
||||||
glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot);
|
glm::translate(-rotationPivot) * glm::translate(scalePivot) *
|
||||||
|
glm::scale(scale) * glm::translate(-scalePivot);
|
||||||
models.insert(object.properties.at(0).value<qint64>(), model);
|
models.insert(object.properties.at(0).value<qint64>(), model);
|
||||||
|
|
||||||
} else if (object.name == "Texture") {
|
} 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__
|
#ifndef __interface__starfield__Config__
|
||||||
#define __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:
|
// Dependencies:
|
||||||
//
|
//
|
||||||
|
@ -49,11 +25,6 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#if STARFIELD_MULTITHREADING
|
|
||||||
#include <mutex>
|
|
||||||
#include <atomic>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -88,14 +59,8 @@ namespace starfield {
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
#if STARFIELD_SAVE_MEMORY
|
|
||||||
typedef uint16_t nuint;
|
|
||||||
typedef uint32_t wuint;
|
|
||||||
#else
|
|
||||||
typedef uint32_t nuint;
|
typedef uint32_t nuint;
|
||||||
typedef uint64_t wuint;
|
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
|
// interface
|
||||||
//
|
//
|
||||||
// Created by Tobias Schwinger on 3/29/13.
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// Modified by Chris Barnard 10/16/13.
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef __interface__starfield__Controller__
|
#ifndef __interface__starfield__Controller__
|
||||||
#define __interface__starfield__Confroller__
|
#define __interface__starfield__Confroller__
|
||||||
|
|
||||||
#ifndef __interface__Starfield_impl__
|
#include <time.h>
|
||||||
#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 "starfield/Generator.h"
|
||||||
#include "starfield/data/InputVertex.h"
|
#include "starfield/data/InputVertex.h"
|
||||||
#include "starfield/data/BrightnessLevel.h"
|
|
||||||
#include "starfield/Loader.h"
|
|
||||||
|
|
||||||
#include "starfield/renderer/Renderer.h"
|
#include "starfield/renderer/Renderer.h"
|
||||||
#include "starfield/renderer/VertexOrder.h"
|
#include "starfield/renderer/VertexOrder.h"
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
public:
|
public:
|
||||||
|
Controller() : _tileResolution(20), _renderer(0l) { }
|
||||||
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);
|
|
||||||
|
|
||||||
// acuire a consistent copy of the current LOD state
|
~Controller() { delete _renderer; }
|
||||||
fraction = _lodFraction;
|
|
||||||
lwm = _lodLowWaterMark;
|
bool computeStars(unsigned numStars, unsigned seed);
|
||||||
hwm = _lodHighWaterMark;
|
bool setResolution(unsigned tileResolution);
|
||||||
size_t last = _lodBrightnessSequence.size() - 1;
|
void render(float perspective, float angle, mat4 const& orientation, float alpha);
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void retile(unsigned numStars, unsigned tileResolution);
|
||||||
|
|
||||||
void retile(size_t n, unsigned k,
|
void recreateRenderer(unsigned numStars, unsigned tileResolution);
|
||||||
BrightnessLevel b, BrightnessLevel bMin) {
|
|
||||||
|
|
||||||
Tiling tiling(k);
|
InputVertices _inputSequence;
|
||||||
VertexOrder scanner(tiling);
|
unsigned _tileResolution;
|
||||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
unsigned _numStars;
|
||||||
|
Renderer* _renderer;
|
||||||
// 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
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#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
|
// interface
|
||||||
//
|
//
|
||||||
// Created by Tobias Schwinger on 3/29/13.
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// Modified 10/17/13 Chris Barnard.
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef __interface__starfield__data__GpuVertex__
|
#ifndef __interface__starfield__data__GpuVertex__
|
||||||
#define __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"
|
#include "starfield/data/InputVertex.h"
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
@ -21,33 +18,18 @@ namespace starfield {
|
||||||
public:
|
public:
|
||||||
GpuVertex() { }
|
GpuVertex() { }
|
||||||
|
|
||||||
GpuVertex(InputVertex const& in) {
|
GpuVertex(InputVertex const& inputVertex);
|
||||||
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getColor() const { return _color; }
|
unsigned getColor() const { return _color; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned _color;
|
unsigned _color;
|
||||||
float _valX;
|
float _valX;
|
||||||
float _valY;
|
float _valY;
|
||||||
float _valZ;
|
float _valZ;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // anonymous namespace
|
}
|
||||||
|
|
||||||
#endif
|
#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
|
// interface
|
||||||
//
|
//
|
||||||
// Created by Tobias Schwinger on 3/29/13.
|
// Created by Tobias Schwinger on 3/29/13.
|
||||||
|
// Modified by Chris Barnard 10/17/13.
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef __interface__starfield__data__InputVertex__
|
#ifndef __interface__starfield__data__InputVertex__
|
||||||
#define __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"
|
#include "starfield/Config.h"
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
@ -20,33 +17,21 @@ namespace starfield {
|
||||||
class InputVertex {
|
class InputVertex {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
InputVertex(float azimuth, float altitude, unsigned color) {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getAzimuth() const { return _azimuth; }
|
float getAzimuth() const { return _azimuth; }
|
||||||
float getAltitude() const { return _altitude; }
|
float getAltitude() const { return _altitude; }
|
||||||
unsigned getColor() const { return _color; }
|
unsigned getColor() const { return _color; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned _color;
|
unsigned _color;
|
||||||
float _azimuth;
|
float _azimuth;
|
||||||
float _altitude;
|
float _altitude;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<InputVertex> InputVertices;
|
typedef std::vector<InputVertex> InputVertices;
|
||||||
|
|
||||||
} // anonymous namespace
|
}
|
||||||
|
|
||||||
#endif
|
#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__
|
#ifndef __interface__starfield__data__Tile__
|
||||||
#define __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/Config.h"
|
||||||
#include "starfield/data/BrightnessLevel.h"
|
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
|
||||||
struct Tile {
|
struct Tile {
|
||||||
|
nuint offset;
|
||||||
nuint offset;
|
nuint count;
|
||||||
nuint count;
|
nuint flags;
|
||||||
BrightnessLevel lod;
|
|
||||||
nuint flags;
|
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
static uint16_t const checked = 1;
|
static uint16_t const checked = 1;
|
||||||
static uint16_t const visited = 2;
|
static uint16_t const visited = 2;
|
||||||
static uint16_t const render = 4;
|
static uint16_t const render = 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
#endif
|
#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
|
// interface
|
||||||
//
|
//
|
||||||
// Created by Tobias Schwinger on 3/22/13.
|
// Created by Tobias Schwinger on 3/22/13.
|
||||||
|
// Modified 10/17/13 Chris Barnard.
|
||||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef __interface__starfield__renderer__Renderer__
|
#ifndef __interface__starfield__renderer__Renderer__
|
||||||
#define __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/Config.h"
|
||||||
#include "starfield/data/InputVertex.h"
|
#include "starfield/data/InputVertex.h"
|
||||||
#include "starfield/data/BrightnessLevel.h"
|
|
||||||
|
|
||||||
#include "starfield/data/Tile.h"
|
#include "starfield/data/Tile.h"
|
||||||
#include "starfield/data/GpuVertex.h"
|
#include "starfield/data/GpuVertex.h"
|
||||||
|
#include "starfield/renderer/Tiling.h"
|
||||||
#include "Tiling.h"
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// FOV culling
|
// FOV culling
|
||||||
|
@ -66,174 +60,14 @@ namespace starfield {
|
||||||
class Renderer {
|
class Renderer {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Renderer(InputVertices const& src,
|
Renderer(InputVertices const& src, unsigned numStars, unsigned tileResolution);
|
||||||
size_t n,
|
~Renderer();
|
||||||
unsigned k,
|
void render(float perspective, float aspect, mat4 const& orientation, float alpha);
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// renderer construction
|
// renderer construction
|
||||||
|
|
||||||
void prepareVertexData(InputVertices const& src,
|
void prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FOV culling / LOD
|
// FOV culling / LOD
|
||||||
|
|
||||||
|
@ -242,299 +76,65 @@ namespace starfield {
|
||||||
|
|
||||||
class TileSelection {
|
class TileSelection {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Cursor { Tile* current, * firstInRow; };
|
struct Cursor { Tile* current, * firstInRow; };
|
||||||
private:
|
|
||||||
Renderer& _rendererRef;
|
private:
|
||||||
Cursor* const _stackArray;
|
Renderer& _rendererRef;
|
||||||
Cursor* _stackPos;
|
Cursor* const _stackArray;
|
||||||
Tile const* const _tileArray;
|
Cursor* _stackPos;
|
||||||
Tile const* const _tilesEnd;
|
Tile const* const _tileArray;
|
||||||
|
Tile const* const _tilesEnd;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
TileSelection(Renderer& renderer, Tile const* tiles, Tile const* tiles_end, Cursor* stack) :
|
||||||
TileSelection(Renderer& renderer, Tile const* tiles,
|
_rendererRef(renderer),
|
||||||
Tile const* tiles_end, Cursor* stack) :
|
_stackArray(stack),
|
||||||
|
_stackPos(stack),
|
||||||
_rendererRef(renderer),
|
_tileArray(tiles),
|
||||||
_stackArray(stack),
|
_tilesEnd(tiles_end) { }
|
||||||
_stackPos(stack),
|
|
||||||
_tileArray(tiles),
|
|
||||||
_tilesEnd(tiles_end) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool select(Cursor const& cursor);
|
||||||
// flood fill strategy
|
bool process(Cursor const& cursor);
|
||||||
|
void right(Cursor& cursor) const;
|
||||||
bool select(Cursor const& c) {
|
void left(Cursor& cursor) const;
|
||||||
Tile* t = c.current;
|
void up(Cursor& cursor) const;
|
||||||
|
void down(Cursor& cursor) const;
|
||||||
if (t < _tileArray || t >= _tilesEnd ||
|
void defer(Cursor const& cursor);
|
||||||
!! (t->flags & Tile::checked)) {
|
bool deferred(Cursor& cursor);
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool visitTile(Tile* t) {
|
bool visitTile(Tile* tile);
|
||||||
|
bool isTileVisible(unsigned index);
|
||||||
unsigned index = t - _tileArray;
|
unsigned prepareBatch(unsigned const* indices, unsigned const* indicesEnd);
|
||||||
*_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GL API handling
|
// GL API handling
|
||||||
|
|
||||||
void glAlloc() {
|
void glAlloc();
|
||||||
|
void glFree();
|
||||||
GLchar const* const VERTEX_SHADER =
|
void glUpload(GLsizei numStars);
|
||||||
"#version 120\n"
|
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha);
|
||||||
"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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// variables
|
// variables
|
||||||
|
|
||||||
GpuVertex* _dataArray;
|
GpuVertex* _dataArray;
|
||||||
Tile* _tileArray;
|
Tile* _tileArray;
|
||||||
GLint* _batchOffs;
|
GLint* _batchOffs;
|
||||||
GLsizei* _batchCountArray;
|
GLsizei* _batchCountArray;
|
||||||
GLuint _vertexArrayHandle;
|
GLuint _vertexArrayHandle;
|
||||||
ProgramObject _program;
|
ProgramObject _program;
|
||||||
int _alphaLocationHandle;
|
int _alphaLocationHandle;
|
||||||
|
|
||||||
Tiling _tiling;
|
Tiling _tiling;
|
||||||
|
|
||||||
unsigned* _outIndexPos;
|
unsigned* _outIndexPos;
|
||||||
vec3 _wRowVec;
|
vec3 _wRowVec;
|
||||||
float _halfPerspectiveAngle;
|
float _halfPerspectiveAngle;
|
||||||
BrightnessLevel _minBright;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // anonymous namespace
|
}
|
||||||
|
|
||||||
#endif
|
#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__
|
#ifndef __interface__starfield__renderer__Tiling__
|
||||||
#define __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"
|
#include "starfield/Config.h"
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
const float LOG2 = 1.4426950408889634;
|
||||||
|
|
||||||
class Tiling {
|
class Tiling {
|
||||||
public:
|
public:
|
||||||
|
Tiling(unsigned tileResolution) : _tileResolution(tileResolution), _rcpSlice(tileResolution / Radians::twicePi()) {
|
||||||
Tiling(unsigned k) :
|
_nBits = ceil(log(getTileCount()) * LOG2); }
|
||||||
_valK(k),
|
|
||||||
_rcpSlice(k / Radians::twicePi()) {
|
unsigned getAzimuthalTiles() const { return _tileResolution; }
|
||||||
_nBits = ceil(log(getTileCount()) * 1.4426950408889634); // log2
|
unsigned getAltitudinalTiles() const { return _tileResolution / 2 + 1; }
|
||||||
}
|
|
||||||
|
|
||||||
unsigned getAzimuthalTiles() const { return _valK; }
|
|
||||||
unsigned getAltitudinalTiles() const { return _valK / 2 + 1; }
|
|
||||||
unsigned getTileIndexBits() const { return _nBits; }
|
unsigned getTileIndexBits() const { return _nBits; }
|
||||||
|
unsigned getTileCount() const { return getAzimuthalTiles() * getAltitudinalTiles(); }
|
||||||
unsigned getTileCount() const {
|
unsigned getTileIndex(float azimuth, float altitude) const { return discreteAzimuth(azimuth) +
|
||||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
_tileResolution * discreteAltitude(altitude); }
|
||||||
}
|
float getSliceAngle() const { return 1.0f / _rcpSlice; }
|
||||||
|
|
||||||
unsigned getTileIndex(float azimuth, float altitude) const {
|
|
||||||
return discreteAzimuth(azimuth) +
|
|
||||||
_valK * discreteAltitude(altitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
float getSliceAngle() const {
|
|
||||||
return 1.0f / _rcpSlice;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned discreteAngle(float unsigned_angle) const {
|
unsigned discreteAngle(float unsigned_angle) const { return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f)); }
|
||||||
|
unsigned discreteAzimuth(float angle) const { return discreteAngle(angle) % _tileResolution; }
|
||||||
return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f));
|
unsigned discreteAltitude(float angle) const { return min( getAltitudinalTiles() - 1,
|
||||||
}
|
discreteAngle(angle + Radians::halfPi()) ); }
|
||||||
|
|
||||||
unsigned discreteAzimuth(float a) const {
|
|
||||||
return discreteAngle(a) % _valK;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned discreteAltitude(float a) const {
|
|
||||||
return min(getAltitudinalTiles() - 1,
|
|
||||||
discreteAngle(a + Radians::halfPi()) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// variables
|
// variables
|
||||||
|
|
||||||
unsigned _valK;
|
unsigned _tileResolution;
|
||||||
float _rcpSlice;
|
float _rcpSlice;
|
||||||
unsigned _nBits;
|
unsigned _nBits;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
#endif
|
#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__
|
#ifndef __interface__starfield__renderer__VertexOrder__
|
||||||
#define __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/Config.h"
|
||||||
#include "starfield/data/InputVertex.h"
|
#include "starfield/data/InputVertex.h"
|
||||||
#include "starfield/renderer/Tiling.h"
|
#include "starfield/renderer/Tiling.h"
|
||||||
|
|
||||||
namespace starfield {
|
namespace starfield {
|
||||||
|
|
||||||
/**
|
// Defines the vertex order for the renderer as a bit extractor for
|
||||||
* Defines the vertex order for the renderer as a bit extractor for
|
//binary in-place Radix Sort.
|
||||||
* binary in-place Radix Sort.
|
|
||||||
*/
|
|
||||||
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VertexOrder(Tiling const& tiling) :
|
explicit VertexOrder(Tiling const& tiling) :
|
||||||
|
|
||||||
base(tiling.getTileIndexBits() + BrightnessBits),
|
base(tiling.getTileIndexBits()), _tiling(tiling) { }
|
||||||
_tiling(tiling) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bit(InputVertex const& v, state_type const& s) const {
|
bool bit(InputVertex const& vertex, state_type const& state) 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Tiling _tiling;
|
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 we don't have an explicit destination socket then pull active socket for current audio mixer from node list
|
||||||
if (!_isDestinationSocketExplicit) {
|
if (!_isDestinationSocketExplicit) {
|
||||||
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
|
||||||
if (audioMixer) {
|
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||||
_destinationSocket = *audioMixer->getActiveSocket();
|
_destinationSocket = *audioMixer->getActiveSocket();
|
||||||
} else {
|
} else {
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
|
|
|
@ -33,17 +33,8 @@ Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* local
|
||||||
_linkedData(NULL),
|
_linkedData(NULL),
|
||||||
_isAlive(true)
|
_isAlive(true)
|
||||||
{
|
{
|
||||||
if (publicSocket) {
|
setPublicSocket(publicSocket);
|
||||||
_publicSocket = new sockaddr(*publicSocket);
|
setLocalSocket(localSocket);
|
||||||
} else {
|
|
||||||
_publicSocket = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localSocket) {
|
|
||||||
_localSocket = new sockaddr(*localSocket);
|
|
||||||
} else {
|
|
||||||
_localSocket = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_init(&_mutex, 0);
|
pthread_mutex_init(&_mutex, 0);
|
||||||
}
|
}
|
||||||
|
@ -95,6 +86,32 @@ 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() {
|
void Node::activateLocalSocket() {
|
||||||
qDebug() << "Activating local socket for node" << *this << "\n";
|
qDebug() << "Activating local socket for node" << *this << "\n";
|
||||||
_activeSocket = _localSocket;
|
_activeSocket = _localSocket;
|
||||||
|
|
|
@ -47,9 +47,9 @@ public:
|
||||||
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
void setLastHeardMicrostamp(uint64_t lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; }
|
||||||
|
|
||||||
sockaddr* getPublicSocket() const { return _publicSocket; }
|
sockaddr* getPublicSocket() const { return _publicSocket; }
|
||||||
void setPublicSocket(sockaddr* publicSocket) { _publicSocket = publicSocket; }
|
void setPublicSocket(sockaddr* publicSocket);
|
||||||
sockaddr* getLocalSocket() const { return _localSocket; }
|
sockaddr* getLocalSocket() const { return _localSocket; }
|
||||||
void setLocalSocket(sockaddr* localSocket) { _localSocket = localSocket; }
|
void setLocalSocket(sockaddr* localSocket);
|
||||||
|
|
||||||
sockaddr* getActiveSocket() const { return _activeSocket; }
|
sockaddr* getActiveSocket() const { return _activeSocket; }
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,10 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||||
_ownerUUID(QUuid::createUuid()),
|
_ownerUUID(QUuid::createUuid()),
|
||||||
_numNoReplyDomainCheckIns(0),
|
_numNoReplyDomainCheckIns(0),
|
||||||
_assignmentServerSocket(NULL),
|
_assignmentServerSocket(NULL),
|
||||||
_checkInPacket(NULL),
|
|
||||||
_numBytesCheckInPacket(0),
|
|
||||||
_publicAddress(),
|
_publicAddress(),
|
||||||
_publicPort(0),
|
_publicPort(0),
|
||||||
_shouldUseDomainServerAsSTUN(0)
|
_hasCompletedInitialSTUNFailure(false),
|
||||||
|
_stunRequestsSinceSuccess(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -151,7 +150,7 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat
|
||||||
}
|
}
|
||||||
case PACKET_TYPE_PING_REPLY: {
|
case PACKET_TYPE_PING_REPLY: {
|
||||||
// activate the appropriate socket for this node, if not yet updated
|
// activate the appropriate socket for this node, if not yet updated
|
||||||
activateSocketFromPingReply(senderAddress);
|
activateSocketFromNodeCommunication(senderAddress);
|
||||||
|
|
||||||
// set the ping time for this node for stat collection
|
// set the ping time for this node for stat collection
|
||||||
timePingReply(senderAddress, packetData);
|
timePingReply(senderAddress, packetData);
|
||||||
|
@ -199,6 +198,7 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPosition += updateNodeWithData(matchingNode,
|
currentPosition += updateNodeWithData(matchingNode,
|
||||||
|
NULL,
|
||||||
packetHolder,
|
packetHolder,
|
||||||
numTotalBytes - (currentPosition - startPosition));
|
numTotalBytes - (currentPosition - startPosition));
|
||||||
|
|
||||||
|
@ -206,35 +206,32 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int NodeList::updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
|
int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int 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) {
|
|
||||||
node->lock();
|
node->lock();
|
||||||
|
|
||||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
|
|
||||||
if (node->getActiveSocket()) {
|
if (senderAddress) {
|
||||||
|
activateSocketFromNodeCommunication(senderAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->getActiveSocket() || !senderAddress) {
|
||||||
node->recordBytesReceived(dataBytes);
|
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) {
|
Node* NodeList::nodeWithAddress(sockaddr *senderAddress) {
|
||||||
|
@ -290,11 +287,6 @@ void NodeList::reset() {
|
||||||
clear();
|
clear();
|
||||||
_numNoReplyDomainCheckIns = 0;
|
_numNoReplyDomainCheckIns = 0;
|
||||||
|
|
||||||
delete[] _checkInPacket;
|
|
||||||
_checkInPacket = NULL;
|
|
||||||
|
|
||||||
_numBytesCheckInPacket = 0;
|
|
||||||
|
|
||||||
delete _nodeTypesOfInterest;
|
delete _nodeTypesOfInterest;
|
||||||
_nodeTypesOfInterest = NULL;
|
_nodeTypesOfInterest = NULL;
|
||||||
|
|
||||||
|
@ -318,62 +310,67 @@ void NodeList::sendSTUNRequest() {
|
||||||
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
|
const char STUN_SERVER_HOSTNAME[] = "stun.highfidelity.io";
|
||||||
const unsigned short STUN_SERVER_PORT = 3478;
|
const unsigned short STUN_SERVER_PORT = 3478;
|
||||||
|
|
||||||
static int failedStunRequests = 0;
|
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||||
|
|
||||||
if (failedStunRequests < NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
int packetIndex = 0;
|
||||||
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
|
||||||
|
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||||
int packetIndex = 0;
|
|
||||||
|
// leading zeros + message type
|
||||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
|
||||||
|
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
|
||||||
// leading zeros + message type
|
packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
|
||||||
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
|
|
||||||
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
|
// message length (no additional attributes are included)
|
||||||
packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
|
uint16_t messageLength = 0;
|
||||||
|
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
|
||||||
// message length (no additional attributes are included)
|
packetIndex += sizeof(messageLength);
|
||||||
uint16_t messageLength = 0;
|
|
||||||
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
|
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
|
||||||
packetIndex += sizeof(messageLength);
|
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||||
|
|
||||||
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
|
// transaction ID (random 12-byte unsigned integer)
|
||||||
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
const uint NUM_TRANSACTION_ID_BYTES = 12;
|
||||||
|
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
|
||||||
// transaction ID (random 12-byte unsigned integer)
|
loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES);
|
||||||
const uint NUM_TRANSACTION_ID_BYTES = 12;
|
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
|
||||||
unsigned char transactionID[NUM_TRANSACTION_ID_BYTES];
|
|
||||||
loadRandomIdentifier(transactionID, NUM_TRANSACTION_ID_BYTES);
|
// lookup the IP for the STUN server
|
||||||
memcpy(stunRequestPacket + packetIndex, &transactionID, sizeof(transactionID));
|
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
|
||||||
|
|
||||||
// lookup the IP for the STUN server
|
for (int i = 0; i < stunInfo.addresses().size(); i++) {
|
||||||
static QHostInfo stunInfo = QHostInfo::fromName(STUN_SERVER_HOSTNAME);
|
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
|
QString stunIPAddress = stunInfo.addresses()[i].toString();
|
||||||
for (int i = 0; i < stunInfo.addresses().size(); i++) {
|
|
||||||
if (stunInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
if (!_hasCompletedInitialSTUNFailure) {
|
||||||
QString stunIPAddress = stunInfo.addresses()[i].toString();
|
qDebug("Sending intial stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
||||||
|
|
||||||
qDebug("Sending a stun request to %s\n", stunIPAddress.toLocal8Bit().constData());
|
|
||||||
|
|
||||||
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
|
||||||
STUN_SERVER_PORT,
|
|
||||||
stunRequestPacket,
|
|
||||||
sizeof(stunRequestPacket));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_nodeSocket.send(stunIPAddress.toLocal8Bit().constData(),
|
||||||
|
STUN_SERVER_PORT,
|
||||||
|
stunRequestPacket,
|
||||||
|
sizeof(stunRequestPacket));
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
failedStunRequests++;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're here this was the last failed STUN request
|
_stunRequestsSinceSuccess++;
|
||||||
// use our DS as our stun server
|
|
||||||
qDebug("Failed to lookup public address via STUN server at %s:%hu. Using DS for STUN.\n",
|
if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) {
|
||||||
STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
if (!_hasCompletedInitialSTUNFailure) {
|
||||||
_shouldUseDomainServerAsSTUN = true;
|
// 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) {
|
void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes) {
|
||||||
|
@ -397,6 +394,9 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
||||||
const int NUM_BYTES_FAMILY_ALIGN = 1;
|
const int NUM_BYTES_FAMILY_ALIGN = 1;
|
||||||
const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
|
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;
|
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||||
|
|
||||||
uint8_t addressFamily = 0;
|
uint8_t addressFamily = 0;
|
||||||
|
@ -409,7 +409,7 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
||||||
uint16_t xorMappedPort = 0;
|
uint16_t xorMappedPort = 0;
|
||||||
memcpy(&xorMappedPort, packetData + byteIndex, sizeof(xorMappedPort));
|
memcpy(&xorMappedPort, packetData + byteIndex, sizeof(xorMappedPort));
|
||||||
|
|
||||||
_publicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
|
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
|
||||||
|
|
||||||
byteIndex += sizeof(xorMappedPort);
|
byteIndex += sizeof(xorMappedPort);
|
||||||
|
|
||||||
|
@ -418,12 +418,21 @@ void NodeList::processSTUNResponse(unsigned char* packetData, size_t dataBytes)
|
||||||
memcpy(&xorMappedAddress, packetData + byteIndex, sizeof(xorMappedAddress));
|
memcpy(&xorMappedAddress, packetData + byteIndex, sizeof(xorMappedAddress));
|
||||||
|
|
||||||
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||||
_publicAddress = QHostAddress(stunAddress);
|
|
||||||
|
|
||||||
qDebug("Public socket received from STUN server is %s:%hu\n",
|
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||||
_publicAddress.toString().toLocal8Bit().constData(),
|
|
||||||
_publicPort);
|
|
||||||
|
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -471,62 +480,66 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
printedDomainServerIP = true;
|
printedDomainServerIP = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_publicAddress.isNull() && !_shouldUseDomainServerAsSTUN) {
|
if (_publicAddress.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||||
// we don't know our public socket and we need to send it to the domain server
|
// 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
|
// send a STUN request to figure it out
|
||||||
sendSTUNRequest();
|
sendSTUNRequest();
|
||||||
} else {
|
} else {
|
||||||
// construct the DS check in packet if we need to
|
// construct the DS check in packet if we need to
|
||||||
if (!_checkInPacket) {
|
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
|
||||||
int numBytesNodesOfInterest = _nodeTypesOfInterest ? strlen((char*) _nodeTypesOfInterest) : 0;
|
|
||||||
|
const int IP_ADDRESS_BYTES = 4;
|
||||||
const int IP_ADDRESS_BYTES = 4;
|
|
||||||
|
// check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
|
||||||
// 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) +
|
||||||
int numPacketBytes = sizeof(PACKET_TYPE) + sizeof(PACKET_VERSION) + sizeof(NODE_TYPE) +
|
NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
|
||||||
NUM_BYTES_RFC4122_UUID + (2 * (sizeof(uint16_t) + IP_ADDRESS_BYTES)) +
|
numBytesNodesOfInterest + sizeof(unsigned char);
|
||||||
numBytesNodesOfInterest + sizeof(unsigned char);
|
|
||||||
|
unsigned char* checkInPacket = new unsigned char[numPacketBytes];
|
||||||
_checkInPacket = new unsigned char[numPacketBytes];
|
unsigned char* packetPosition = checkInPacket;
|
||||||
unsigned char* packetPosition = _checkInPacket;
|
|
||||||
|
PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
||||||
PACKET_TYPE nodePacketType = (memchr(SOLO_NODE_TYPES, _ownerType, sizeof(SOLO_NODE_TYPES)))
|
? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
||||||
? PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY
|
: PACKET_TYPE_DOMAIN_LIST_REQUEST;
|
||||||
: PACKET_TYPE_DOMAIN_LIST_REQUEST;
|
|
||||||
|
packetPosition += populateTypeAndVersion(packetPosition, nodePacketType);
|
||||||
packetPosition += populateTypeAndVersion(packetPosition, nodePacketType);
|
|
||||||
|
*(packetPosition++) = _ownerType;
|
||||||
*(packetPosition++) = _ownerType;
|
|
||||||
|
// send our owner UUID or the null one
|
||||||
// send our owner UUID or the null one
|
QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
|
||||||
QByteArray rfcOwnerUUID = _ownerUUID.toRfc4122();
|
memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
|
||||||
memcpy(packetPosition, rfcOwnerUUID.constData(), rfcOwnerUUID.size());
|
packetPosition += rfcOwnerUUID.size();
|
||||||
packetPosition += rfcOwnerUUID.size();
|
|
||||||
|
// pack our public address to send to domain-server
|
||||||
// pack our public address to send to domain-server
|
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||||
packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket),
|
htonl(_publicAddress.toIPv4Address()), htons(_publicPort));
|
||||||
htonl(_publicAddress.toIPv4Address()), htons(_publicPort));
|
|
||||||
|
// pack our local address to send to domain-server
|
||||||
// pack our local address to send to domain-server
|
packetPosition += packSocket(checkInPacket + (packetPosition - checkInPacket),
|
||||||
packetPosition += packSocket(_checkInPacket + (packetPosition - _checkInPacket),
|
getLocalAddress(),
|
||||||
getLocalAddress(),
|
htons(_nodeSocket.getListeningPort()));
|
||||||
htons(_nodeSocket.getListeningPort()));
|
|
||||||
|
// add the number of bytes for node types of interest
|
||||||
// add the number of bytes for node types of interest
|
*(packetPosition++) = numBytesNodesOfInterest;
|
||||||
*(packetPosition++) = numBytesNodesOfInterest;
|
|
||||||
|
// copy over the bytes for node types of interest, if required
|
||||||
// copy over the bytes for node types of interest, if required
|
if (numBytesNodesOfInterest > 0) {
|
||||||
if (numBytesNodesOfInterest > 0) {
|
memcpy(packetPosition,
|
||||||
memcpy(packetPosition,
|
_nodeTypesOfInterest,
|
||||||
_nodeTypesOfInterest,
|
numBytesNodesOfInterest);
|
||||||
numBytesNodesOfInterest);
|
packetPosition += numBytesNodesOfInterest;
|
||||||
packetPosition += numBytesNodesOfInterest;
|
|
||||||
}
|
|
||||||
|
|
||||||
_numBytesCheckInPacket = packetPosition - _checkInPacket;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_nodeSocket.send(_domainIP.toString().toLocal8Bit().constData(), _domainPort, _checkInPacket, _numBytesCheckInPacket);
|
_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
|
// increment the count of un-replied check-ins
|
||||||
_numNoReplyDomainCheckIns++;
|
_numNoReplyDomainCheckIns++;
|
||||||
|
@ -624,6 +637,8 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
} else {
|
} else {
|
||||||
|
node->lock();
|
||||||
|
|
||||||
if (node->getType() == NODE_TYPE_AUDIO_MIXER ||
|
if (node->getType() == NODE_TYPE_AUDIO_MIXER ||
|
||||||
node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
node->getType() == NODE_TYPE_VOXEL_SERVER) {
|
||||||
// until the Audio class also uses our nodeList, we need to update
|
// until the Audio class also uses our nodeList, we need to update
|
||||||
|
@ -631,6 +646,19 @@ Node* NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publ
|
||||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
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
|
// we had this node already, do nothing for now
|
||||||
return &*node;
|
return &*node;
|
||||||
}
|
}
|
||||||
|
@ -657,21 +685,39 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
|
||||||
unsigned n = 0;
|
unsigned n = 0;
|
||||||
for(NodeList::iterator node = begin(); node != end(); node++) {
|
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||||
// only send to the NodeTypes we are asked to send to.
|
// only send to the NodeTypes we are asked to send to.
|
||||||
if (node->getActiveSocket() != NULL) {
|
if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
|
||||||
if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
|
if (node->getActiveSocket()) {
|
||||||
// we know which socket is good for this node, send there
|
// we know which socket is good for this node, send there
|
||||||
_nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes);
|
_nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes);
|
||||||
++n;
|
++n;
|
||||||
|
} else {
|
||||||
|
// we don't have an active link to this node, ping it to set that up
|
||||||
|
pingPublicAndLocalSocketsForInactiveNode(&(*node));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// we don't have an active link to this node, ping it to set that up
|
|
||||||
pingPublicAndLocalSocketsForInactiveNode(&(*node));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::activateSocketFromPingReply(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++) {
|
for(NodeList::iterator node = begin(); node != end(); node++) {
|
||||||
if (!node->getActiveSocket()) {
|
if (!node->getActiveSocket()) {
|
||||||
// check both the public and local addresses for each node to see if we find a match
|
// check both the public and local addresses for each node to see if we find a match
|
||||||
|
@ -699,6 +745,22 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
||||||
return NULL;
|
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) {
|
void* removeSilentNodes(void *args) {
|
||||||
NodeList* nodeList = (NodeList*) args;
|
NodeList* nodeList = (NodeList*) args;
|
||||||
uint64_t checkTimeUsecs = 0;
|
uint64_t checkTimeUsecs = 0;
|
||||||
|
@ -712,12 +774,8 @@ void* removeSilentNodes(void *args) {
|
||||||
node->lock();
|
node->lock();
|
||||||
|
|
||||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||||
|
// kill this node, don't lock - we already did it
|
||||||
qDebug() << "Killed " << *node << "\n";
|
nodeList->killNode(&(*node), false);
|
||||||
|
|
||||||
nodeList->notifyHooksOfKilledNode(&*node);
|
|
||||||
|
|
||||||
node->setAlive(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node->unlock();
|
node->unlock();
|
||||||
|
|
|
@ -113,12 +113,12 @@ public:
|
||||||
Node* nodeWithUUID(const QUuid& nodeUUID);
|
Node* nodeWithUUID(const QUuid& nodeUUID);
|
||||||
|
|
||||||
Node* addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket);
|
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 processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||||
void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes);
|
void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes);
|
||||||
|
|
||||||
int updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
int updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes);
|
||||||
int updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes);
|
|
||||||
|
|
||||||
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
|
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
|
||||||
|
|
||||||
|
@ -140,6 +140,7 @@ public:
|
||||||
void addDomainListener(DomainChangeListener* listener);
|
void addDomainListener(DomainChangeListener* listener);
|
||||||
void removeDomainListener(DomainChangeListener* listener);
|
void removeDomainListener(DomainChangeListener* listener);
|
||||||
|
|
||||||
|
void possiblyPingInactiveNodes();
|
||||||
private:
|
private:
|
||||||
static NodeList* _sharedInstance;
|
static NodeList* _sharedInstance;
|
||||||
|
|
||||||
|
@ -166,13 +167,12 @@ private:
|
||||||
pthread_t checkInWithDomainServerThread;
|
pthread_t checkInWithDomainServerThread;
|
||||||
int _numNoReplyDomainCheckIns;
|
int _numNoReplyDomainCheckIns;
|
||||||
sockaddr* _assignmentServerSocket;
|
sockaddr* _assignmentServerSocket;
|
||||||
uchar* _checkInPacket;
|
|
||||||
int _numBytesCheckInPacket;
|
|
||||||
QHostAddress _publicAddress;
|
QHostAddress _publicAddress;
|
||||||
uint16_t _publicPort;
|
uint16_t _publicPort;
|
||||||
bool _shouldUseDomainServerAsSTUN;
|
bool _hasCompletedInitialSTUNFailure;
|
||||||
|
unsigned int _stunRequestsSinceSuccess;
|
||||||
|
|
||||||
void activateSocketFromPingReply(sockaddr *nodeAddress);
|
void activateSocketFromNodeCommunication(sockaddr *nodeAddress);
|
||||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||||
|
|
||||||
std::vector<NodeListHook*> _hooks;
|
std::vector<NodeListHook*> _hooks;
|
||||||
|
|
|
@ -265,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 {
|
int UDPSocket::send(sockaddr* destAddress, const void* data, size_t byteLength) const {
|
||||||
// send data via UDP
|
if (destAddress) {
|
||||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
// send data via UDP
|
||||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
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));
|
if (sent_bytes != byteLength) {
|
||||||
return false;
|
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 {
|
int UDPSocket::send(const char* destAddress, int destPort, const void* data, size_t byteLength) const {
|
||||||
|
|
|
@ -327,6 +327,9 @@ void VoxelServer::run() {
|
||||||
NodeList* nodeList = NodeList::getInstance();
|
NodeList* nodeList = NodeList::getInstance();
|
||||||
nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER);
|
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);
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
// tell our NodeList about our desire to get notifications
|
// tell our NodeList about our desire to get notifications
|
||||||
|
@ -434,6 +437,9 @@ void VoxelServer::run() {
|
||||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ping our inactive nodes to punch holes with them
|
||||||
|
nodeList->possiblyPingInactiveNodes();
|
||||||
|
|
||||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||||
packetVersionMatch(packetData)) {
|
packetVersionMatch(packetData)) {
|
||||||
|
|
||||||
|
@ -445,23 +451,16 @@ void VoxelServer::run() {
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
||||||
NUM_BYTES_RFC4122_UUID));
|
NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
Node* node = NodeList::getInstance()->addOrUpdateNode(nodeUUID,
|
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||||
NODE_TYPE_AGENT,
|
|
||||||
&senderAddress,
|
|
||||||
&senderAddress);
|
|
||||||
|
|
||||||
// temp activation of public socket before server ping/reply is setup
|
if (node) {
|
||||||
if (!node->getActiveSocket()) {
|
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
|
||||||
node->activatePublicSocket();
|
|
||||||
|
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||||
|
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
||||||
|
nodeData->initializeVoxelSendThread(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength);
|
|
||||||
|
|
||||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
|
||||||
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
|
|
||||||
nodeData->initializeVoxelSendThread(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (packetData[0] == PACKET_TYPE_PING
|
} else if (packetData[0] == PACKET_TYPE_PING
|
||||||
|| packetData[0] == PACKET_TYPE_DOMAIN
|
|| packetData[0] == PACKET_TYPE_DOMAIN
|
||||||
|| packetData[0] == PACKET_TYPE_STUN_RESPONSE) {
|
|| packetData[0] == PACKET_TYPE_STUN_RESPONSE) {
|
||||||
|
|
Loading…
Reference in a new issue