mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into ossome
Conflicts: interface/src/renderer/FBXReader.cpp
This commit is contained in:
commit
591cf98821
31 changed files with 952 additions and 1346 deletions
|
@ -36,4 +36,4 @@ private:
|
|||
std::vector<AudioInjector*> _audioInjectors;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Operative__) */
|
||||
#endif /* defined(__hifi__Agent__) */
|
||||
|
|
|
@ -81,6 +81,9 @@ void AudioMixer::run() {
|
|||
NodeList *nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AUDIO_MIXER);
|
||||
|
||||
const char AUDIO_MIXER_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_AGENT, NODE_TYPE_AUDIO_INJECTOR };
|
||||
nodeList->setNodeTypesOfInterest(AUDIO_MIXER_NODE_TYPES_OF_INTEREST, sizeof(AUDIO_MIXER_NODE_TYPES_OF_INTEREST));
|
||||
|
||||
ssize_t receivedBytes = 0;
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||
|
@ -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++) {
|
||||
PositionalAudioRingBuffer* positionalRingBuffer = (PositionalAudioRingBuffer*) node->getLinkedData();
|
||||
if (positionalRingBuffer && positionalRingBuffer->shouldBeAddedToMix(JITTER_BUFFER_SAMPLES)) {
|
||||
|
@ -157,7 +163,7 @@ void AudioMixer::run() {
|
|||
|
||||
const int PHASE_DELAY_AT_90 = 20;
|
||||
|
||||
if (node->getType() == NODE_TYPE_AGENT) {
|
||||
if (node->getType() == NODE_TYPE_AGENT && node->getActiveSocket() && node->getLinkedData()) {
|
||||
AvatarAudioRingBuffer* nodeRingBuffer = (AvatarAudioRingBuffer*) node->getLinkedData();
|
||||
|
||||
// zero out the client mix for this node
|
||||
|
@ -165,7 +171,8 @@ void AudioMixer::run() {
|
|||
|
||||
// loop through all other nodes that have sufficient audio to mix
|
||||
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
|
||||
if (((PositionalAudioRingBuffer*) otherNode->getLinkedData())->willBeAddedToMix()
|
||||
if (otherNode->getLinkedData()
|
||||
&& ((PositionalAudioRingBuffer*) otherNode->getLinkedData())->willBeAddedToMix()
|
||||
&& (otherNode != node || (otherNode == node && nodeRingBuffer->shouldLoopbackForNode()))) {
|
||||
PositionalAudioRingBuffer* otherNodeBuffer = (PositionalAudioRingBuffer*) otherNode->getLinkedData();
|
||||
// based on our listen mode we will do this mixing...
|
||||
|
@ -333,7 +340,7 @@ void AudioMixer::run() {
|
|||
}
|
||||
|
||||
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
|
||||
while (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO ||
|
||||
packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO) {
|
||||
if (packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|
||||
|| packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
|
||||
|
||||
unsigned char* currentBuffer = packetData + numBytesForPacketHeader(packetData);
|
||||
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),
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* matchingInjector = nodeList->addOrUpdateNode(nodeUUID,
|
||||
NODE_TYPE_AUDIO_INJECTOR,
|
||||
NULL,
|
||||
NULL);
|
||||
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
// give the new audio data to the matching injector node
|
||||
nodeList->updateNodeWithData(matchingInjector, packetData, receivedBytes);
|
||||
if (matchingNode) {
|
||||
nodeList->updateNodeWithData(matchingNode, nodeAddress, packetData, receivedBytes);
|
||||
|
||||
if (packetData[0] != PACKET_TYPE_INJECT_AUDIO
|
||||
&& std::isnan(((PositionalAudioRingBuffer *)matchingNode->getLinkedData())->getOrientation().x)) {
|
||||
// kill off this node - temporary solution to mixer crash on mac sleep
|
||||
matchingNode->setAlive(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// let processNodeData handle it.
|
||||
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
|
||||
|
|
|
@ -97,6 +97,8 @@ void AvatarMixer::run() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER);
|
||||
|
||||
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
||||
|
||||
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
|
||||
|
||||
nodeList->startSilentNodeRemovalThread();
|
||||
|
@ -123,6 +125,8 @@ void AvatarMixer::run() {
|
|||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
switch (packetData[0]) {
|
||||
|
@ -131,10 +135,14 @@ void AvatarMixer::run() {
|
|||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// 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
|
||||
nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes);
|
||||
if (avatarNode) {
|
||||
// parse positional data from an node
|
||||
nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
case PACKET_TYPE_INJECT_AUDIO:
|
||||
broadcastAvatarData(nodeList, nodeUUID, &nodeAddress);
|
||||
break;
|
||||
|
|
|
@ -46,79 +46,121 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) {
|
|||
const struct mg_request_info* ri = mg_get_request_info(connection);
|
||||
|
||||
const char RESPONSE_200[] = "HTTP/1.0 200 OK\r\n\r\n";
|
||||
const char RESPONSE_400[] = "HTTP/1.0 400 Bad Request\r\n\r\n";
|
||||
|
||||
if (strcmp(ri->uri, "/assignment") == 0 && strcmp(ri->request_method, "POST") == 0) {
|
||||
// return a 200
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
// upload the file
|
||||
mg_upload(connection, "/tmp");
|
||||
|
||||
return 1;
|
||||
} else if (strcmp(ri->uri, "/assignments.json") == 0) {
|
||||
// user is asking for json list of assignments
|
||||
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// setup the JSON
|
||||
QJsonObject assignmentJSON;
|
||||
|
||||
QJsonObject assignedNodesJSON;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()) {
|
||||
// this is a node with assignment
|
||||
QJsonObject assignedNodeJSON;
|
||||
const char URI_ASSIGNMENT[] = "/assignment";
|
||||
const char URI_NODE[] = "/node";
|
||||
|
||||
if (strcmp(ri->request_method, "GET") == 0) {
|
||||
if (strcmp(ri->uri, "/assignments.json") == 0) {
|
||||
// user is asking for json list of assignments
|
||||
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// setup the JSON
|
||||
QJsonObject assignmentJSON;
|
||||
|
||||
QJsonObject assignedNodesJSON;
|
||||
|
||||
// enumerate the NodeList to find the assigned nodes
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
const char ASSIGNMENT_JSON_UUID_KEY[] = "UUID";
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
if (node->getLinkedData()) {
|
||||
// this is a node with assignment
|
||||
QJsonObject assignedNodeJSON;
|
||||
|
||||
// add the assignment UUID
|
||||
QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID());
|
||||
assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID;
|
||||
|
||||
// add the node socket information
|
||||
assignedNodeJSON["public"] = jsonForSocket(node->getPublicSocket());
|
||||
assignedNodeJSON["local"] = jsonForSocket(node->getLocalSocket());
|
||||
|
||||
// re-format the type name so it matches the target name
|
||||
QString nodeTypeName(node->getTypeName());
|
||||
nodeTypeName = nodeTypeName.toLower();
|
||||
nodeTypeName.replace(' ', '-');
|
||||
|
||||
assignedNodesJSON[nodeTypeName] = assignedNodeJSON;
|
||||
}
|
||||
}
|
||||
|
||||
assignmentJSON["fulfilled"] = assignedNodesJSON;
|
||||
|
||||
QJsonObject queuedAssignmentsJSON;
|
||||
|
||||
// add the queued but unfilled assignments to the json
|
||||
std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
|
||||
|
||||
while (assignment != domainServerInstance->_assignmentQueue.end()) {
|
||||
QJsonObject queuedAssignmentJSON;
|
||||
|
||||
// add the assignment UUID
|
||||
QString assignmentUUID = uuidStringWithoutCurlyBraces(((Assignment*) node->getLinkedData())->getUUID());
|
||||
assignedNodeJSON[ASSIGNMENT_JSON_UUID_KEY] = assignmentUUID;
|
||||
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
||||
queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString;
|
||||
|
||||
// add the node socket information
|
||||
assignedNodeJSON["public"] = jsonForSocket(node->getPublicSocket());
|
||||
assignedNodeJSON["local"] = jsonForSocket(node->getLocalSocket());
|
||||
// add this queued assignment to the JSON
|
||||
queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON;
|
||||
|
||||
// re-format the type name so it matches the target name
|
||||
QString nodeTypeName(node->getTypeName());
|
||||
nodeTypeName = nodeTypeName.toLower();
|
||||
nodeTypeName.replace(' ', '-');
|
||||
// push forward the iterator to check the next assignment
|
||||
assignment++;
|
||||
}
|
||||
|
||||
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
||||
|
||||
// print out the created JSON
|
||||
QJsonDocument assignmentDocument(assignmentJSON);
|
||||
mg_printf(connection, "%s", assignmentDocument.toJson().constData());
|
||||
|
||||
// we've processed this request
|
||||
return 1;
|
||||
}
|
||||
|
||||
// not processed, pass to document root
|
||||
return 0;
|
||||
} else if (strcmp(ri->request_method, "POST") == 0) {
|
||||
if (strcmp(ri->uri, URI_ASSIGNMENT) == 0) {
|
||||
// return a 200
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
// upload the file
|
||||
mg_upload(connection, "/tmp");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (strcmp(ri->request_method, "DELETE") == 0) {
|
||||
// this is a DELETE request
|
||||
|
||||
// check if it is for an assignment
|
||||
if (memcmp(ri->uri, URI_NODE, strlen(URI_NODE)) == 0) {
|
||||
// pull the UUID from the url
|
||||
QUuid deleteUUID = QUuid(QString(ri->uri + strlen(URI_NODE) + sizeof('/')));
|
||||
|
||||
if (!deleteUUID.isNull()) {
|
||||
Node *nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
|
||||
|
||||
assignedNodesJSON[nodeTypeName] = assignedNodeJSON;
|
||||
if (nodeToKill) {
|
||||
// start with a 200 response
|
||||
mg_printf(connection, "%s", RESPONSE_200);
|
||||
|
||||
// we have a valid UUID and node - kill the node that has this assignment
|
||||
NodeList::getInstance()->killNode(nodeToKill);
|
||||
|
||||
// successfully processed request
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assignmentJSON["fulfilled"] = assignedNodesJSON;
|
||||
// request not processed - bad request
|
||||
mg_printf(connection, "%s", RESPONSE_400);
|
||||
|
||||
QJsonObject queuedAssignmentsJSON;
|
||||
|
||||
// add the queued but unfilled assignments to the json
|
||||
std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
|
||||
|
||||
while (assignment != domainServerInstance->_assignmentQueue.end()) {
|
||||
QJsonObject queuedAssignmentJSON;
|
||||
|
||||
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
|
||||
queuedAssignmentJSON[ASSIGNMENT_JSON_UUID_KEY] = uuidString;
|
||||
|
||||
// add this queued assignment to the JSON
|
||||
queuedAssignmentsJSON[(*assignment)->getTypeName()] = queuedAssignmentJSON;
|
||||
|
||||
// push forward the iterator to check the next assignment
|
||||
assignment++;
|
||||
}
|
||||
|
||||
assignmentJSON["queued"] = queuedAssignmentsJSON;
|
||||
|
||||
// print out the created JSON
|
||||
QJsonDocument assignmentDocument(assignmentJSON);
|
||||
mg_printf(connection, "%s", assignmentDocument.toJson().constData());
|
||||
|
||||
// we've processed this request
|
||||
// this was processed by civetweb
|
||||
return 1;
|
||||
} else {
|
||||
// have mongoose process this request from the document_root
|
||||
|
@ -160,6 +202,27 @@ void DomainServer::civetwebUploadHandler(struct mg_connection *connection, const
|
|||
domainServerInstance->_assignmentQueueMutex.unlock();
|
||||
}
|
||||
|
||||
void DomainServer::addReleasedAssignmentBackToQueue(Assignment* releasedAssignment) {
|
||||
qDebug() << "Adding assignment" << *releasedAssignment << " back to queue.\n";
|
||||
|
||||
// find this assignment in the static file
|
||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||
if (_staticAssignments[i].getUUID() == releasedAssignment->getUUID()) {
|
||||
// reset the UUID on the static assignment
|
||||
_staticAssignments[i].resetUUID();
|
||||
|
||||
// put this assignment back in the queue so it goes out
|
||||
_assignmentQueueMutex.lock();
|
||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
||||
_assignmentQueueMutex.unlock();
|
||||
|
||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
||||
// we are at the blank part of the static assignments - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::nodeAdded(Node* node) {
|
||||
|
||||
}
|
||||
|
@ -169,26 +232,8 @@ void DomainServer::nodeKilled(Node* node) {
|
|||
if (node->getLinkedData()) {
|
||||
Assignment* nodeAssignment = (Assignment*) node->getLinkedData();
|
||||
|
||||
qDebug() << "Adding assignment" << *nodeAssignment << " back to queue.\n";
|
||||
|
||||
// find this assignment in the static file
|
||||
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
|
||||
if (_staticAssignments[i].getUUID() == nodeAssignment->getUUID()) {
|
||||
// reset the UUID on the static assignment
|
||||
_staticAssignments[i].resetUUID();
|
||||
|
||||
// put this assignment back in the queue so it goes out
|
||||
_assignmentQueueMutex.lock();
|
||||
_assignmentQueue.push_back(&_staticAssignments[i]);
|
||||
_assignmentQueueMutex.unlock();
|
||||
|
||||
} else if (_staticAssignments[i].getUUID().isNull()) {
|
||||
// we are at the blank part of the static assignments - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
addReleasedAssignmentBackToQueue(nodeAssignment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosition, Node* nodeToAdd) {
|
||||
|
|
|
@ -47,6 +47,7 @@ private:
|
|||
void removeAssignmentFromQueue(Assignment* removableAssignment);
|
||||
bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI);
|
||||
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
|
||||
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
|
||||
|
||||
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
|
||||
file(GLOB INTERFACE_SRCS src/*.cpp src/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui)
|
||||
file(GLOB SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||
foreach(SUBDIR avatar devices renderer ui starfield)
|
||||
file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h)
|
||||
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${SUBDIR_SRCS})
|
||||
endforeach(SUBDIR)
|
||||
|
||||
# project subdirectories
|
||||
add_subdirectory(src/starfield)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Gui REQUIRED)
|
||||
find_package(Qt5Multimedia REQUIRED)
|
||||
|
|
|
@ -72,8 +72,8 @@
|
|||
using namespace std;
|
||||
|
||||
// Starfield information
|
||||
static char STAR_FILE[] = "http://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
|
||||
static char STAR_CACHE_FILE[] = "cachedStars.txt";
|
||||
static unsigned STARFIELD_NUM_STARS = 50000;
|
||||
static unsigned STARFIELD_SEED = 1;
|
||||
|
||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||
|
||||
|
@ -2491,8 +2491,8 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... stars...");
|
||||
if (!_stars.getFileLoaded()) {
|
||||
_stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0);
|
||||
if (!_stars.isStarsLoaded()) {
|
||||
_stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED);
|
||||
}
|
||||
// should be the first rendering pass - w/o depth buffer / lighting
|
||||
|
||||
|
|
20
interface/src/Stars.cpp
Normal file → Executable file
20
interface/src/Stars.cpp
Normal file → Executable file
|
@ -7,34 +7,28 @@
|
|||
//
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Stars.h"
|
||||
#include "Stars.h"
|
||||
|
||||
#define __interface__Starfield_impl__
|
||||
#include "starfield/Controller.h"
|
||||
#undef __interface__Starfield_impl__
|
||||
|
||||
Stars::Stars() :
|
||||
_controller(0l), _fileLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
_controller(0l), _starsLoaded(false) {
|
||||
_controller = new starfield::Controller;
|
||||
}
|
||||
|
||||
Stars::~Stars() {
|
||||
delete _controller;
|
||||
}
|
||||
|
||||
bool Stars::readInput(const char* url, const char* cacheFile, unsigned limit) {
|
||||
_fileLoaded = _controller->readInput(url, cacheFile, limit);
|
||||
return _fileLoaded;
|
||||
bool Stars::generate(unsigned numStars, unsigned seed) {
|
||||
_starsLoaded = _controller->computeStars(numStars, seed);
|
||||
return _starsLoaded;
|
||||
}
|
||||
|
||||
bool Stars::setResolution(unsigned k) {
|
||||
return _controller->setResolution(k);
|
||||
}
|
||||
|
||||
float Stars::changeLOD(float fraction, float overalloc, float realloc) {
|
||||
return float(_controller->changeLOD(fraction, overalloc, realloc));
|
||||
}
|
||||
|
||||
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
||||
|
||||
// determine length of screen diagonal from quadrant height and aspect ratio
|
||||
|
@ -47,7 +41,7 @@ void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
|
|||
// pull the modelview matrix off the GL stack
|
||||
glm::mat4 view; glGetFloatv(GL_MODELVIEW_MATRIX, glm::value_ptr(view));
|
||||
|
||||
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
|
||||
_controller->render(fovDiagonal, aspect, glm::affineInverse(view), alpha);
|
||||
}
|
||||
|
||||
|
||||
|
|
84
interface/src/Stars.h
Normal file → Executable file
84
interface/src/Stars.h
Normal file → Executable file
|
@ -13,69 +13,39 @@
|
|||
|
||||
namespace starfield { class Controller; }
|
||||
|
||||
//
|
||||
// Starfield rendering component.
|
||||
//
|
||||
// Starfield rendering component.
|
||||
class Stars {
|
||||
public:
|
||||
Stars();
|
||||
~Stars();
|
||||
public:
|
||||
Stars();
|
||||
~Stars();
|
||||
|
||||
//
|
||||
// Reads input file from URL. Returns true upon success.
|
||||
//
|
||||
// The limit parameter allows to reduce the number of stars
|
||||
// that are loaded, keeping the brightest ones.
|
||||
//
|
||||
bool readInput(const char* url, const char* cacheFile = 0l, unsigned limit = 200000);
|
||||
// Generate stars from random number
|
||||
// The numStars parameter sets the number of stars to generate.
|
||||
bool generate(unsigned numStars, unsigned seed);
|
||||
|
||||
//
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
// The parameters specifiy the field of view.
|
||||
//
|
||||
void render(float fovY, float aspect, float nearZ, float alpha);
|
||||
// Renders the starfield from a local viewer's perspective.
|
||||
// The parameters specifiy the field of view.
|
||||
void render(float fovY, float aspect, float nearZ, float alpha);
|
||||
|
||||
//
|
||||
// Sets the resolution for FOV culling.
|
||||
//
|
||||
// The parameter determines the number of tiles in azimuthal
|
||||
// and altitudinal directions.
|
||||
//
|
||||
// GPU resources are updated upon change in which case 'true'
|
||||
// is returned.
|
||||
//
|
||||
bool setResolution(unsigned k);
|
||||
// Sets the resolution for FOV culling.
|
||||
//
|
||||
// The parameter determines the number of tiles in azimuthal
|
||||
// and altitudinal directions.
|
||||
//
|
||||
// GPU resources are updated upon change in which case 'true'
|
||||
// is returned.
|
||||
bool setResolution(unsigned k);
|
||||
|
||||
//
|
||||
// Allows to alter the number of stars to be rendered given a
|
||||
// factor. The least brightest ones are omitted first.
|
||||
//
|
||||
// The further parameters determine when GPU resources should
|
||||
// be reallocated. Its value is fractional in respect to the
|
||||
// last number of stars 'n' that caused 'n * (1+overalloc)' to
|
||||
// be allocated. When the next call to setLOD causes the total
|
||||
// number of stars that could be rendered to drop below 'n *
|
||||
// (1-realloc)' or rises above 'n * (1+realloc)' GPU resources
|
||||
// are updated. Note that all parameters must be fractions,
|
||||
// that is within the range [0;1] and that 'overalloc' must be
|
||||
// greater than or equal to 'realloc'.
|
||||
//
|
||||
// The current level of detail is returned as a float in [0;1].
|
||||
//
|
||||
float changeLOD(float factor,
|
||||
float overalloc = 0.25, float realloc = 0.15);
|
||||
// Returns true when stars have been loaded
|
||||
bool isStarsLoaded() const { return _starsLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
|
||||
bool getFileLoaded() const { return _fileLoaded; };
|
||||
private:
|
||||
// don't copy/assign
|
||||
Stars(Stars const&); // = delete;
|
||||
Stars& operator=(Stars const&); // delete;
|
||||
|
||||
// variables
|
||||
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _fileLoaded;
|
||||
starfield::Controller* _controller;
|
||||
|
||||
bool _starsLoaded;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -688,6 +688,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
jointNeckID = object.properties.at(0).toString();
|
||||
}
|
||||
glm::vec3 translation;
|
||||
glm::vec3 rotationOffset;
|
||||
glm::vec3 preRotation, rotation, postRotation;
|
||||
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
glm::vec3 scalePivot, rotationPivot;
|
||||
|
@ -701,6 +702,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationOffset") {
|
||||
rotationOffset = glm::vec3(property.properties.at(3).value<double>(),
|
||||
property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationPivot") {
|
||||
rotationPivot = glm::vec3(property.properties.at(3).value<double>(),
|
||||
property.properties.at(4).value<double>(),
|
||||
|
@ -741,6 +747,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
property.properties.at(5).value<double>(),
|
||||
property.properties.at(6).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationOffset") {
|
||||
rotationOffset = glm::vec3(property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>(),
|
||||
property.properties.at(6).value<double>());
|
||||
|
||||
} else if (property.properties.at(0) == "RotationPivot") {
|
||||
rotationPivot = glm::vec3(property.properties.at(4).value<double>(),
|
||||
property.properties.at(5).value<double>(),
|
||||
|
@ -776,13 +787,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
}
|
||||
}
|
||||
// see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html
|
||||
model.preRotation = glm::translate(translation) * glm::translate(rotationPivot) *
|
||||
glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
||||
model.preRotation = glm::translate(translation) * glm::translate(rotationOffset) *
|
||||
glm::translate(rotationPivot) * glm::mat4_cast(glm::quat(glm::radians(preRotation)));
|
||||
model.rotation = glm::quat(glm::radians(rotation));
|
||||
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) *
|
||||
glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot);
|
||||
models.insert(object.properties.at(0).toString(), model);
|
||||
|
||||
model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) *
|
||||
glm::translate(-rotationPivot) * glm::translate(scalePivot) *
|
||||
glm::scale(scale) * glm::translate(-scalePivot);
|
||||
models.insert(object.properties.at(0).value<qint64>(), model);
|
||||
|
||||
} else if (object.name == "Texture") {
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
if (subobject.name == "RelativeFilename") {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
project(starfield)
|
||||
|
||||
# Only headers (that are facaded by the Stars.cpp file) here -
|
||||
# hence declared as custom target.
|
||||
|
||||
file(GLOB_RECURSE STARFIELD_SRCS *.h)
|
||||
add_custom_target("starfield" SOURCES ${STARFIELD_SRCS})
|
||||
|
35
interface/src/starfield/Config.h
Normal file → Executable file
35
interface/src/starfield/Config.h
Normal file → Executable file
|
@ -9,30 +9,6 @@
|
|||
#ifndef __interface__starfield__Config__
|
||||
#define __interface__starfield__Config__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Compile time configuration:
|
||||
//
|
||||
|
||||
#ifndef STARFIELD_HEMISPHERE_ONLY
|
||||
#define STARFIELD_HEMISPHERE_ONLY 0 // set to 1 for hemisphere only
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_LOW_MEMORY
|
||||
#define STARFIELD_LOW_MEMORY 0 // set to 1 not to use 16-bit types
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_DEBUG_CULLING
|
||||
#define STARFIELD_DEBUG_CULLING 0 // set to 1 to peek behind the scenes
|
||||
#endif
|
||||
|
||||
#ifndef STARFIELD_MULTITHREADING
|
||||
#define STARFIELD_MULTITHREADING 0
|
||||
#endif
|
||||
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
|
@ -49,11 +25,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
@ -88,14 +59,8 @@ namespace starfield {
|
|||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
typedef uint16_t nuint;
|
||||
typedef uint32_t wuint;
|
||||
#else
|
||||
typedef uint32_t nuint;
|
||||
typedef uint64_t wuint;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
63
interface/src/starfield/Controller.cpp
Executable file
63
interface/src/starfield/Controller.cpp
Executable file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// starfield/Controller.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/16/13
|
||||
// Portions of code based on earlier work by Tobias Schwinger.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Controller.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool Controller::computeStars(unsigned numStars, unsigned seed) {
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
Generator::computeStarPositions(_inputSequence, numStars, seed);
|
||||
|
||||
this->retile(numStars, _tileResolution);
|
||||
|
||||
qDebug("Total time to generate stars: %llu msec\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::setResolution(unsigned tileResolution) {
|
||||
if (tileResolution <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tileResolution != _tileResolution) {
|
||||
|
||||
this->retile(_numStars, tileResolution);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
Renderer* renderer = _renderer;
|
||||
|
||||
if (renderer != 0l) {
|
||||
renderer->render(perspective, angle, orientation, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::retile(unsigned numStars, unsigned tileResolution) {
|
||||
Tiling tiling(tileResolution);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
|
||||
recreateRenderer(numStars, tileResolution);
|
||||
|
||||
_tileResolution = tileResolution;
|
||||
}
|
||||
|
||||
void Controller::recreateRenderer(unsigned numStars, unsigned tileResolution) {
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, numStars, tileResolution);
|
||||
}
|
416
interface/src/starfield/Controller.h
Normal file → Executable file
416
interface/src/starfield/Controller.h
Normal file → Executable file
|
@ -3,425 +3,39 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/16/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Controller__
|
||||
#define __interface__starfield__Confroller__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
//
|
||||
// Data pipeline
|
||||
// =============
|
||||
//
|
||||
// ->> readInput -(load)--+---- (get brightness & sort) ---> brightness LUT
|
||||
// | |
|
||||
// ->> setResolution --+ | >extractBrightnessLevels<
|
||||
// V |
|
||||
// (sort by (tile,brightness))
|
||||
// | |
|
||||
// ->> setLOD ---+ | >retile< ->> setLOD --> (just parameterize
|
||||
// V V when enough data on-GPU)
|
||||
// (filter by max-LOD brightness,
|
||||
// build tile info for rendering)
|
||||
// | |
|
||||
// V >recreateRenderer<
|
||||
// (set new renderer)/
|
||||
//
|
||||
//
|
||||
// (process), ->> entry point, ---> data flow, >internal routine<
|
||||
//
|
||||
// (member functions are ordered by data flow)
|
||||
|
||||
//
|
||||
// Still open
|
||||
// ==========
|
||||
//
|
||||
// o atomics/mutexes need to be added as annotated in the source to allow
|
||||
// concurrent threads to pull the strings to e.g. have a low priority
|
||||
// thread run the data pipeline for update -- rendering is wait-free
|
||||
//
|
||||
#include <time.h>
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
#include "starfield/Loader.h"
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
|
||||
Controller() :
|
||||
_tileResolution(20),
|
||||
_lodFraction(1.0),
|
||||
_lodLowWaterMark(0.8),
|
||||
_lodHighWaterMark(1.0),
|
||||
_lodOveralloc(1.2),
|
||||
_lodNalloc(0),
|
||||
_lodNRender(0),
|
||||
_lodBrightness(0),
|
||||
_lodAllocBrightness(0),
|
||||
_renderer(0l) {
|
||||
}
|
||||
|
||||
~Controller() {
|
||||
delete _renderer;
|
||||
}
|
||||
|
||||
#if !STARFIELD_MULTITHREADING
|
||||
#define lock
|
||||
#define _(x)
|
||||
#endif
|
||||
|
||||
bool readInput(const char* url, const char* cacheFile, unsigned limit)
|
||||
{
|
||||
InputVertices vertices;
|
||||
|
||||
if (! Loader().loadVertices(vertices, url, cacheFile, limit))
|
||||
return false;
|
||||
|
||||
BrightnessLevels brightness;
|
||||
extractBrightnessLevels(brightness, vertices);
|
||||
|
||||
// input is read, now run the entire data pipeline on the new input
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
_inputSequence.swap(vertices);
|
||||
#if STARFIELD_MULTITHREADING
|
||||
unsigned k = _tileResolution.load(memory_order_relaxed);
|
||||
#else
|
||||
unsigned k = _tileResolution;
|
||||
#endif
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double rcpChange;
|
||||
|
||||
// we'll have to build a new LOD state for a new total N,
|
||||
// ideally keeping allocation size and number of vertices
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
size_t newLast = _inputSequence.size() - 1;
|
||||
|
||||
// reciprocal change N_old/N_new tells us how to scale
|
||||
// the fractions
|
||||
rcpChange = min(1.0, double(vertices.size()) / _inputSequence.size());
|
||||
|
||||
// initialization? use defaults / previously set values
|
||||
if (rcpChange == 0.0) {
|
||||
|
||||
rcpChange = 1.0;
|
||||
|
||||
nRender = toBufSize(_lodFraction * newLast);
|
||||
n = min(newLast, toBufSize(_lodOveralloc * nRender));
|
||||
|
||||
} else {
|
||||
|
||||
// cannot allocate or render more than we have
|
||||
n = min(newLast, _lodNalloc);
|
||||
nRender = min(newLast, _lodNRender);
|
||||
}
|
||||
|
||||
// determine new minimum brightness levels
|
||||
bMin = brightness[n];
|
||||
b = brightness[nRender];
|
||||
|
||||
// adjust n
|
||||
n = std::upper_bound(
|
||||
brightness.begin() + n - 1,
|
||||
brightness.end(),
|
||||
bMin, GreaterBrightness() ) - brightness.begin();
|
||||
}
|
||||
|
||||
// invoke next stage
|
||||
try {
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
} catch (...) {
|
||||
|
||||
// rollback transaction and rethrow
|
||||
vertices.swap(_inputSequence);
|
||||
throw;
|
||||
}
|
||||
|
||||
// finally publish the new LOD state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodBrightnessSequence.swap(brightness);
|
||||
_lodFraction *= rcpChange;
|
||||
_lodLowWaterMark *= rcpChange;
|
||||
_lodHighWaterMark *= rcpChange;
|
||||
_lodOveralloc *= rcpChange;
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
_lodAllocBrightness = bMin;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setResolution(unsigned k) {
|
||||
|
||||
if (k <= 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// printLog("Stars.cpp: setResolution(%d)\n", k);
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
if (k != _tileResolution.load(memory_order_relaxed))
|
||||
#else
|
||||
if (k != _tileResolution)
|
||||
#endif
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
unsigned n;
|
||||
BrightnessLevel b, bMin;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
n = _lodNalloc;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
b = _lodBrightness;
|
||||
#endif
|
||||
bMin = _lodAllocBrightness;
|
||||
}
|
||||
|
||||
this->retile(n, k, b, bMin);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double changeLOD(double factor, double overalloc, double realloc) {
|
||||
|
||||
assert(overalloc >= realloc && realloc >= 0.0);
|
||||
assert(overalloc <= 1.0 && realloc <= 1.0);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: changeLOD(%lf, %lf, %lf)\n", factor, overalloc, realloc);
|
||||
|
||||
size_t n, nRender;
|
||||
BrightnessLevel bMin, b;
|
||||
double fraction, lwm, hwm;
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
Controller() : _tileResolution(20), _renderer(0l) { }
|
||||
|
||||
// acuire a consistent copy of the current LOD state
|
||||
fraction = _lodFraction;
|
||||
lwm = _lodLowWaterMark;
|
||||
hwm = _lodHighWaterMark;
|
||||
size_t last = _lodBrightnessSequence.size() - 1;
|
||||
|
||||
// apply factor
|
||||
fraction = max(0.0, min(1.0, fraction * factor));
|
||||
|
||||
// calculate allocation size and corresponding brightness
|
||||
// threshold
|
||||
double oaFract = std::min(fraction * (1.0 + overalloc), 1.0);
|
||||
n = toBufSize(oaFract * last);
|
||||
bMin = _lodBrightnessSequence[n];
|
||||
n = std::upper_bound(
|
||||
_lodBrightnessSequence.begin() + n - 1,
|
||||
_lodBrightnessSequence.end(),
|
||||
bMin, GreaterBrightness() ) - _lodBrightnessSequence.begin();
|
||||
|
||||
// also determine number of vertices to render and brightness
|
||||
nRender = toBufSize(fraction * last);
|
||||
// Note: nRender does not have to be accurate
|
||||
b = _lodBrightnessSequence[nRender];
|
||||
// this setting controls the renderer, also keep b as the
|
||||
// brightness becomes volatile as soon as the mutex is
|
||||
// released, so keep b
|
||||
#if STARFIELD_MULTITHREADING
|
||||
_lodBrightness.store(b, memory_order_relaxed);
|
||||
#else
|
||||
_lodBrightness = b;
|
||||
#endif
|
||||
|
||||
// printLog("Stars.cpp: "
|
||||
// "fraction = %lf, oaFract = %lf, n = %d, n' = %d, bMin = %d, b = %d\n",
|
||||
// fraction, oaFract, toBufSize(oaFract * last)), n, bMin, b);
|
||||
|
||||
// will not have to reallocate? set new fraction right away
|
||||
// (it is consistent with the rest of the state in this case)
|
||||
if (fraction >= _lodLowWaterMark
|
||||
&& fraction <= _lodHighWaterMark) {
|
||||
|
||||
_lodFraction = fraction;
|
||||
return fraction;
|
||||
}
|
||||
}
|
||||
|
||||
// reallocate
|
||||
|
||||
{ lock _(_inputMutex);
|
||||
|
||||
recreateRenderer(n, _tileResolution, b, bMin);
|
||||
|
||||
// printLog("Stars.cpp: LOD reallocation\n");
|
||||
|
||||
// publish new lod state
|
||||
|
||||
{ lock _(_lodStateMutex);
|
||||
|
||||
_lodNalloc = n;
|
||||
_lodNRender = nRender;
|
||||
|
||||
_lodFraction = fraction;
|
||||
_lodLowWaterMark = fraction * (1.0 - realloc);
|
||||
_lodHighWaterMark = fraction * (1.0 + realloc);
|
||||
_lodOveralloc = fraction * (1.0 + overalloc);
|
||||
_lodAllocBrightness = bMin;
|
||||
}
|
||||
}
|
||||
return fraction;
|
||||
}
|
||||
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check out renderer
|
||||
Renderer* renderer = _renderer.exchange(0l);
|
||||
#else
|
||||
Renderer* renderer = _renderer;
|
||||
#endif
|
||||
|
||||
// have it render
|
||||
if (renderer != 0l) {
|
||||
#if STARFIELD_MULTITHREADING
|
||||
BrightnessLevel b = _lodBrightness.load(memory_order_relaxed);
|
||||
#else
|
||||
BrightnessLevel b = _lodBrightness;
|
||||
#endif
|
||||
renderer->render(perspective, angle, orientation, b, alpha);
|
||||
}
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
// check in - or dispose if there is a new one
|
||||
Renderer* newOne = 0l;
|
||||
if (! _renderer.compare_exchange_strong(newOne, renderer)) {
|
||||
|
||||
assert(!! newOne);
|
||||
delete renderer;
|
||||
}
|
||||
#else
|
||||
# undef lock
|
||||
# undef _
|
||||
#endif
|
||||
}
|
||||
|
||||
~Controller() { delete _renderer; }
|
||||
|
||||
bool computeStars(unsigned numStars, unsigned seed);
|
||||
bool setResolution(unsigned tileResolution);
|
||||
void render(float perspective, float angle, mat4 const& orientation, float alpha);
|
||||
private:
|
||||
void retile(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
void retile(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
void recreateRenderer(unsigned numStars, unsigned tileResolution);
|
||||
|
||||
Tiling tiling(k);
|
||||
VertexOrder scanner(tiling);
|
||||
radix2InplaceSort(_inputSequence.begin(), _inputSequence.end(), scanner);
|
||||
|
||||
// printLog(
|
||||
// "Stars.cpp: recreateRenderer(%d, %d, %d, %d)\n", n, k, b, bMin);
|
||||
|
||||
recreateRenderer(n, k, b, bMin);
|
||||
|
||||
_tileResolution = k;
|
||||
}
|
||||
|
||||
void recreateRenderer(size_t n, unsigned k,
|
||||
BrightnessLevel b, BrightnessLevel bMin) {
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
delete _renderer.exchange(new Renderer(_inputSequence, n, k, b, bMin) );
|
||||
#else
|
||||
delete _renderer;
|
||||
_renderer = new Renderer(_inputSequence, n, k, b, bMin);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static inline size_t toBufSize(double f) {
|
||||
return size_t(floor(f + 0.5f));
|
||||
}
|
||||
|
||||
struct BrightnessSortScanner : Radix2IntegerScanner<BrightnessLevel> {
|
||||
|
||||
typedef Radix2IntegerScanner<BrightnessLevel> base;
|
||||
|
||||
BrightnessSortScanner() : base(BrightnessBits) { }
|
||||
|
||||
bool bit(BrightnessLevel const& k, state_type& s) {
|
||||
|
||||
// bit is inverted to achieve descending order
|
||||
return ! base::bit(k,s);
|
||||
}
|
||||
};
|
||||
|
||||
static void extractBrightnessLevels(BrightnessLevels& dst,
|
||||
InputVertices const& src) {
|
||||
dst.clear();
|
||||
dst.reserve(src.size());
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i)
|
||||
dst.push_back( getBrightness(i->getColor()) );
|
||||
|
||||
radix2InplaceSort(dst.begin(), dst.end(), BrightnessSortScanner());
|
||||
}
|
||||
|
||||
InputVertices _inputSequence;
|
||||
#if STARFIELD_MULTITHREADING
|
||||
mutex _inputMutex;
|
||||
atomic<unsigned> _tileResolution;
|
||||
|
||||
mutex _lodStateMutex;
|
||||
#else
|
||||
unsigned _tileResolution;
|
||||
#endif
|
||||
double _lodFraction;
|
||||
double _lodLowWaterMark;
|
||||
double _lodHighWaterMark;
|
||||
double _lodOveralloc;
|
||||
size_t _lodNalloc;
|
||||
size_t _lodNRender;
|
||||
BrightnessLevels _lodBrightnessSequence;
|
||||
|
||||
#if STARFIELD_MULTITHREADING
|
||||
atomic<BrightnessLevel> _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
atomic<Renderer*> _renderer;
|
||||
|
||||
typedef lock_guard<mutex> lock;
|
||||
#else
|
||||
BrightnessLevel _lodBrightness;
|
||||
BrightnessLevel _lodAllocBrightness;
|
||||
|
||||
Renderer* _renderer;
|
||||
|
||||
#undef lock
|
||||
#undef _
|
||||
#endif
|
||||
InputVertices _inputSequence;
|
||||
unsigned _tileResolution;
|
||||
unsigned _numStars;
|
||||
Renderer* _renderer;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
53
interface/src/starfield/Generator.cpp
Normal file
53
interface/src/starfield/Generator.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// starfield/Generator.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
const float Generator::STAR_COLORIZATION = 0.1;
|
||||
|
||||
void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) {
|
||||
InputVertices* vertices = & destination;
|
||||
//_limit = limit;
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
srand(seed);
|
||||
|
||||
vertices->clear();
|
||||
vertices->reserve(limit);
|
||||
|
||||
const unsigned NUM_DEGREES = 360;
|
||||
|
||||
|
||||
for(int star = 0; star < limit; ++star) {
|
||||
float azimuth, altitude;
|
||||
azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES;
|
||||
altitude = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES / 2) - NUM_DEGREES / 4;
|
||||
|
||||
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
|
||||
}
|
||||
|
||||
qDebug("Took %llu msec to generate stars.\n", (usecTimestampNow() - usecTimestamp(&startTime)) / 1000);
|
||||
}
|
||||
|
||||
// computeStarColor
|
||||
// - Generate a star color.
|
||||
//
|
||||
// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
|
||||
//
|
||||
// 0 = completely black & white
|
||||
// 1 = very colorful
|
||||
unsigned Generator::computeStarColor(float colorization) {
|
||||
unsigned char red = rand() % 256;
|
||||
unsigned char green = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
unsigned char blue = round((red * (1 - colorization)) + ((rand() % 256) * colorization));
|
||||
return red | green << 8 | blue << 16;
|
||||
}
|
37
interface/src/starfield/Generator.h
Normal file
37
interface/src/starfield/Generator.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// starfield/Generator.h
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__Generator__
|
||||
#define __interface__starfield__Generator__
|
||||
|
||||
#include <locale.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
class Generator {
|
||||
|
||||
public:
|
||||
Generator() {}
|
||||
~Generator() {}
|
||||
|
||||
static void computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed);
|
||||
static unsigned computeStarColor(float colorization);
|
||||
|
||||
private:
|
||||
static const float STAR_COLORIZATION;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
//
|
||||
// starfield/data/BrightnessLevel.h
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__BrightnessLevel__
|
||||
#define __interface__starfield__data__BrightnessLevel__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
typedef nuint BrightnessLevel;
|
||||
|
||||
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
const unsigned BrightnessBits = 16u;
|
||||
#else
|
||||
const unsigned BrightnessBits = 18u;
|
||||
#endif
|
||||
const BrightnessLevel BrightnessMask = (1u << (BrightnessBits)) - 1u;
|
||||
|
||||
typedef std::vector<BrightnessLevel> BrightnessLevels;
|
||||
|
||||
BrightnessLevel getBrightness(unsigned c) {
|
||||
|
||||
unsigned r = (c >> 16) & 0xff;
|
||||
unsigned g = (c >> 8) & 0xff;
|
||||
unsigned b = c & 0xff;
|
||||
#if STARFIELD_SAVE_MEMORY
|
||||
return BrightnessLevel((r*r+g*g+b*b) >> 2);
|
||||
#else
|
||||
return BrightnessLevel(r*r+g*g+b*b);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
struct GreaterBrightness {
|
||||
|
||||
bool operator()(InputVertex const& lhs, InputVertex const& rhs) const {
|
||||
return getBrightness(lhs.getColor())
|
||||
> getBrightness(rhs.getColor());
|
||||
}
|
||||
bool operator()(BrightnessLevel lhs, GpuVertex const& rhs) const {
|
||||
return lhs > getBrightness(rhs.getColor());;
|
||||
}
|
||||
bool operator()(BrightnessLevel lhs, BrightnessLevel rhs) const {
|
||||
return lhs > rhs;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
#endif
|
||||
|
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
23
interface/src/starfield/data/GpuVertex.cpp
Executable file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// starfield/data/GpuVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
GpuVertex::GpuVertex(InputVertex const& inputVertex) {
|
||||
_color = inputVertex.getColor();
|
||||
float azimuth = inputVertex.getAzimuth();
|
||||
float altitude = inputVertex.getAltitude();
|
||||
|
||||
// compute altitude/azimuth into X/Y/Z point on a sphere
|
||||
_valX = sin(azimuth) * cos(altitude);
|
||||
_valY = sin(altitude);
|
||||
_valZ = -cos(azimuth) * cos(altitude);
|
||||
}
|
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
32
interface/src/starfield/data/GpuVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__GpuVertex__
|
||||
#define __interface__starfield__data__GpuVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -21,33 +18,18 @@ namespace starfield {
|
|||
public:
|
||||
GpuVertex() { }
|
||||
|
||||
GpuVertex(InputVertex const& in) {
|
||||
|
||||
_color = in.getColor();
|
||||
float azi = in.getAzimuth();
|
||||
float alt = in.getAltitude();
|
||||
|
||||
// ground vector in x/z plane...
|
||||
float gx = sin(azi);
|
||||
float gz = -cos(azi);
|
||||
|
||||
// ...elevated in y direction by altitude
|
||||
float exz = cos(alt);
|
||||
_valX = gx * exz;
|
||||
_valY = sin(alt);
|
||||
_valZ = gz * exz;
|
||||
}
|
||||
GpuVertex(InputVertex const& inputVertex);
|
||||
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
unsigned _color;
|
||||
float _valX;
|
||||
float _valY;
|
||||
float _valZ;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
24
interface/src/starfield/data/InputVertex.cpp
Executable file
24
interface/src/starfield/data/InputVertex.cpp
Executable file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// starfield/data/InputVertex.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17.13.
|
||||
// Based on code by Tobias Schwinger 3/29/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/data/InputVertex.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
InputVertex::InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
_color = color | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
27
interface/src/starfield/data/InputVertex.h
Normal file → Executable file
|
@ -3,16 +3,13 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/29/13.
|
||||
// Modified by Chris Barnard 10/17/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__data__InputVertex__
|
||||
#define __interface__starfield__data__InputVertex__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
@ -20,33 +17,21 @@ namespace starfield {
|
|||
class InputVertex {
|
||||
public:
|
||||
|
||||
InputVertex(float azimuth, float altitude, unsigned color) {
|
||||
|
||||
_color = ((color >> 16) & 0xffu) | (color & 0xff00u) |
|
||||
((color << 16) & 0xff0000u) | 0xff000000u;
|
||||
|
||||
azimuth = angleConvert<Degrees,Radians>(azimuth);
|
||||
altitude = angleConvert<Degrees,Radians>(altitude);
|
||||
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
|
||||
_azimuth = azimuth;
|
||||
_altitude = altitude;
|
||||
}
|
||||
InputVertex(float azimuth, float altitude, unsigned color);
|
||||
|
||||
float getAzimuth() const { return _azimuth; }
|
||||
float getAltitude() const { return _altitude; }
|
||||
unsigned getColor() const { return _color; }
|
||||
|
||||
private:
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
unsigned _color;
|
||||
float _azimuth;
|
||||
float _altitude;
|
||||
};
|
||||
|
||||
typedef std::vector<InputVertex> InputVertices;
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
18
interface/src/starfield/data/Tile.h
Normal file → Executable file
|
@ -9,30 +9,22 @@
|
|||
#ifndef __interface__starfield__data__Tile__
|
||||
#define __interface__starfield__data__Tile__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
struct Tile {
|
||||
|
||||
nuint offset;
|
||||
nuint count;
|
||||
BrightnessLevel lod;
|
||||
nuint flags;
|
||||
nuint offset;
|
||||
nuint count;
|
||||
nuint flags;
|
||||
|
||||
// flags
|
||||
static uint16_t const checked = 1;
|
||||
static uint16_t const visited = 2;
|
||||
static uint16_t const render = 4;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
311
interface/src/starfield/renderer/Renderer.cpp
Executable file
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// starfield/renderer/Renderer.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on earlier work by Tobias Schwinger 3/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/Renderer.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
Renderer::Renderer(InputVertices const& stars, unsigned numStars, unsigned tileResolution) : _dataArray(0l),
|
||||
_tileArray(0l), _tiling(tileResolution) {
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(tileResolution);
|
||||
size_t numTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[numStars];
|
||||
_tileArray = new Tile[numTiles + 1];
|
||||
_batchOffs = new GLint[numTiles * 2];
|
||||
_batchCountArray = new GLsizei[numTiles * 2];
|
||||
|
||||
prepareVertexData(stars, numStars, tiling);
|
||||
|
||||
this->glUpload(numStars);
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void Renderer::render(float perspective, float aspect, mat4 const& orientation, float alpha) {
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude, -Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(), (TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch((unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
}
|
||||
|
||||
// renderer construction
|
||||
|
||||
void Renderer::prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i = vertices.begin(), e = vertices.end(); i != e; ++i) {
|
||||
size_t tileIndex = tiling.getTileIndex(i->getAzimuth(), i->getAltitude());
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
Tile* lastTile = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
tile->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offset = vertexIndex; ++tile != lastTile ;) {
|
||||
tile->offset = offset, tile->count = 0u, tile->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
lastTile->offset = vertexIndex;
|
||||
lastTile->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
++count_active;
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
assert(vertexIndex == numStars);
|
||||
|
||||
// flush last tile (see above)
|
||||
Tile* tile = _tileArray + currTileIndex;
|
||||
tile->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++tile != e;) {
|
||||
tile->offset = vertexIndex, tile->count = 0u, tile->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::visitTile(Tile* tile) {
|
||||
unsigned index = tile - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
return isTileVisible(index);
|
||||
}
|
||||
|
||||
bool Renderer::isTileVisible(unsigned index) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (index % stride) * slice;
|
||||
float altitude = (index / stride) * slice - Radians::halfPi();
|
||||
float groundX = sin(azimuth);
|
||||
float groundZ = -cos(azimuth);
|
||||
float elevation = cos(altitude);
|
||||
vec3 tileCenter = vec3(groundX * elevation, sin(altitude), groundZ * elevation);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
unsigned Renderer::prepareBatch(unsigned const* indices, unsigned const* indicesEnd) {
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs; i != indicesEnd; ++i) {
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
// GL API handling
|
||||
|
||||
void Renderer::glAlloc() {
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
" vec3 c = gl_Color.rgb * 1.22;\n"
|
||||
" float s = min(max(tan((c.r + c.g + c.b) / 3), 1.0), 3.0);\n"
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha * 1.5;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glFree() {
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void Renderer::glUpload(GLsizei numStars) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER, numStars * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS, _batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool Renderer::TileSelection::select(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (tile < _tileArray || tile >= _tilesEnd || !! (tile->flags & Tile::checked)) {
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
tile->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(tile)) {
|
||||
// good one -> remember (for batching) and propagate
|
||||
tile->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::process(Renderer::TileSelection::Cursor const& cursor) {
|
||||
Tile* tile = cursor.current;
|
||||
|
||||
if (! (tile->flags & Tile::visited)) {
|
||||
tile->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::right(Renderer::TileSelection::Cursor& cursor) const {
|
||||
cursor.current += 1;
|
||||
if (cursor.current == cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
cursor.current = cursor.firstInRow;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::left(Renderer::TileSelection::Cursor& cursor) const {
|
||||
if (cursor.current == cursor.firstInRow) {
|
||||
cursor.current = cursor.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
cursor.current -= 1;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::up(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current += numTiles;
|
||||
cursor.firstInRow += numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::down(Renderer::TileSelection::Cursor& cursor) const {
|
||||
unsigned numTiles = _rendererRef._tiling.getAzimuthalTiles();
|
||||
cursor.current -= numTiles;
|
||||
cursor.firstInRow -= numTiles;
|
||||
}
|
||||
|
||||
void Renderer::TileSelection::defer(Renderer::TileSelection::Cursor const& cursor) {
|
||||
*_stackPos++ = cursor;
|
||||
}
|
||||
|
||||
bool Renderer::TileSelection::deferred(Renderer::TileSelection::Cursor& cursor) {
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
506
interface/src/starfield/renderer/Renderer.h
Normal file → Executable file
|
@ -3,24 +3,18 @@
|
|||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 3/22/13.
|
||||
// Modified 10/17/13 Chris Barnard.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__starfield__renderer__Renderer__
|
||||
#define __interface__starfield__renderer__Renderer__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/data/BrightnessLevel.h"
|
||||
|
||||
#include "starfield/data/Tile.h"
|
||||
#include "starfield/data/GpuVertex.h"
|
||||
|
||||
#include "Tiling.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
//
|
||||
// FOV culling
|
||||
|
@ -66,174 +60,14 @@ namespace starfield {
|
|||
class Renderer {
|
||||
public:
|
||||
|
||||
Renderer(InputVertices const& src,
|
||||
size_t n,
|
||||
unsigned k,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) :
|
||||
|
||||
_dataArray(0l),
|
||||
_tileArray(0l),
|
||||
_tiling(k) {
|
||||
|
||||
this->glAlloc();
|
||||
|
||||
Tiling tiling(k);
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
|
||||
// REVISIT: could coalesce allocation for faster rebuild
|
||||
// REVISIT: batch arrays are probably oversized, but - hey - they
|
||||
// are not very large (unless for insane tiling) and we're better
|
||||
// off safe than sorry
|
||||
_dataArray = new GpuVertex[n];
|
||||
_tileArray = new Tile[nTiles + 1];
|
||||
_batchOffs = new GLint[nTiles * 2];
|
||||
_batchCountArray = new GLsizei[nTiles * 2];
|
||||
|
||||
prepareVertexData(src, n, tiling, b, bMin);
|
||||
|
||||
this->glUpload(n);
|
||||
}
|
||||
|
||||
~Renderer() {
|
||||
|
||||
delete[] _dataArray;
|
||||
delete[] _tileArray;
|
||||
delete[] _batchCountArray;
|
||||
delete[] _batchOffs;
|
||||
|
||||
this->glFree();
|
||||
}
|
||||
|
||||
void render(float perspective,
|
||||
float aspect,
|
||||
mat4 const& orientation,
|
||||
BrightnessLevel minBright,
|
||||
float alpha) {
|
||||
|
||||
// printLog("
|
||||
// Stars.cpp: rendering at minimal brightness %d\n", minBright);
|
||||
|
||||
float halfPersp = perspective * 0.5f;
|
||||
|
||||
// cancel all translation
|
||||
mat4 matrix = orientation;
|
||||
matrix[3][0] = 0.0f;
|
||||
matrix[3][1] = 0.0f;
|
||||
matrix[3][2] = 0.0f;
|
||||
|
||||
// extract local z vector
|
||||
vec3 ahead = vec3(matrix[2]);
|
||||
|
||||
float azimuth = atan2(ahead.x,-ahead.z) + Radians::pi();
|
||||
float altitude = atan2(-ahead.y, hypotf(ahead.x, ahead.z));
|
||||
angleHorizontalPolar<Radians>(azimuth, altitude);
|
||||
float const eps = 0.002f;
|
||||
altitude = glm::clamp(altitude,
|
||||
-Radians::halfPi() + eps, Radians::halfPi() - eps);
|
||||
#if STARFIELD_HEMISPHERE_ONLY
|
||||
altitude = std::max(0.0f, altitude);
|
||||
#endif
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
mat4 matrix_debug = glm::translate(vec3(0.0f, 0.0f, -4.0f)) *
|
||||
glm::affineInverse(matrix);
|
||||
#endif
|
||||
|
||||
matrix = glm::affineInverse(matrix);
|
||||
|
||||
this->_outIndexPos = (unsigned*) _batchOffs;
|
||||
this->_wRowVec = -vec3(row(matrix, 2));
|
||||
this->_halfPerspectiveAngle = halfPersp;
|
||||
this->_minBright = minBright;
|
||||
|
||||
TileSelection::Cursor cursor;
|
||||
cursor.current = _tileArray + _tiling.getTileIndex(azimuth, altitude);
|
||||
cursor.firstInRow = _tileArray + _tiling.getTileIndex(0.0f, altitude);
|
||||
|
||||
floodFill(cursor, TileSelection(*this, _tileArray, _tileArray + _tiling.getTileCount(),
|
||||
(TileSelection::Cursor*) _batchCountArray));
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# define matrix matrix_debug
|
||||
#endif
|
||||
this->glBatch(glm::value_ptr(matrix), prepareBatch(
|
||||
(unsigned*) _batchOffs, _outIndexPos), alpha);
|
||||
|
||||
#if STARFIELD_DEBUG_CULLING
|
||||
# undef matrix
|
||||
#endif
|
||||
}
|
||||
|
||||
Renderer(InputVertices const& src, unsigned numStars, unsigned tileResolution);
|
||||
~Renderer();
|
||||
void render(float perspective, float aspect, mat4 const& orientation, float alpha);
|
||||
|
||||
private:
|
||||
// renderer construction
|
||||
|
||||
void prepareVertexData(InputVertices const& src,
|
||||
size_t n, // <-- at bMin and brighter
|
||||
Tiling const& tiling,
|
||||
BrightnessLevel b,
|
||||
BrightnessLevel bMin) {
|
||||
|
||||
size_t nTiles = tiling.getTileCount();
|
||||
size_t vertexIndex = 0u, currTileIndex = 0u, count_active = 0u;
|
||||
|
||||
_tileArray[0].offset = 0u;
|
||||
_tileArray[0].lod = b;
|
||||
_tileArray[0].flags = 0u;
|
||||
|
||||
for (InputVertices::const_iterator i =
|
||||
src.begin(), e = src.end(); i != e; ++i) {
|
||||
|
||||
BrightnessLevel bv = getBrightness(i->getColor());
|
||||
// filter by alloc brightness
|
||||
if (bv >= bMin) {
|
||||
|
||||
size_t tileIndex = tiling.getTileIndex(
|
||||
i->getAzimuth(), i->getAltitude());
|
||||
|
||||
assert(tileIndex >= currTileIndex);
|
||||
|
||||
// moved on to another tile? -> flush
|
||||
if (tileIndex != currTileIndex) {
|
||||
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
Tile* tLast = _tileArray + tileIndex;
|
||||
|
||||
// set count of active vertices (upcoming lod)
|
||||
t->count = count_active;
|
||||
// generate skipped, empty tiles
|
||||
for(size_t offs = vertexIndex; ++t != tLast ;) {
|
||||
t->offset = offs, t->count = 0u,
|
||||
t->lod = b, t->flags = 0u;
|
||||
}
|
||||
|
||||
// initialize next (as far as possible here)
|
||||
tLast->offset = vertexIndex;
|
||||
tLast->lod = b;
|
||||
tLast->flags = 0u;
|
||||
|
||||
currTileIndex = tileIndex;
|
||||
count_active = 0u;
|
||||
}
|
||||
|
||||
if (bv >= b)
|
||||
++count_active;
|
||||
|
||||
// printLog("Stars.cpp: Vertex %d on tile #%d\n", vertexIndex, tileIndex);
|
||||
|
||||
// write converted vertex
|
||||
_dataArray[vertexIndex++] = *i;
|
||||
}
|
||||
}
|
||||
assert(vertexIndex == n);
|
||||
// flush last tile (see above)
|
||||
Tile* t = _tileArray + currTileIndex;
|
||||
t->count = count_active;
|
||||
for (Tile* e = _tileArray + nTiles + 1; ++t != e;) {
|
||||
t->offset = vertexIndex, t->count = 0u,
|
||||
t->lod = b, t->flags = 0;
|
||||
}
|
||||
}
|
||||
void prepareVertexData(InputVertices const& vertices, unsigned numStars, Tiling const& tiling);
|
||||
|
||||
// FOV culling / LOD
|
||||
|
||||
|
@ -242,299 +76,65 @@ namespace starfield {
|
|||
|
||||
class TileSelection {
|
||||
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
public:
|
||||
struct Cursor { Tile* current, * firstInRow; };
|
||||
|
||||
private:
|
||||
Renderer& _rendererRef;
|
||||
Cursor* const _stackArray;
|
||||
Cursor* _stackPos;
|
||||
Tile const* const _tileArray;
|
||||
Tile const* const _tilesEnd;
|
||||
|
||||
public:
|
||||
|
||||
TileSelection(Renderer& renderer, Tile const* tiles,
|
||||
Tile const* tiles_end, Cursor* stack) :
|
||||
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) {
|
||||
}
|
||||
public:
|
||||
TileSelection(Renderer& renderer, Tile const* tiles, Tile const* tiles_end, Cursor* stack) :
|
||||
_rendererRef(renderer),
|
||||
_stackArray(stack),
|
||||
_stackPos(stack),
|
||||
_tileArray(tiles),
|
||||
_tilesEnd(tiles_end) { }
|
||||
|
||||
protected:
|
||||
|
||||
// flood fill strategy
|
||||
|
||||
bool select(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (t < _tileArray || t >= _tilesEnd ||
|
||||
!! (t->flags & Tile::checked)) {
|
||||
|
||||
// out of bounds or been here already
|
||||
return false;
|
||||
}
|
||||
|
||||
// will check now and never again
|
||||
t->flags |= Tile::checked;
|
||||
if (_rendererRef.visitTile(t)) {
|
||||
|
||||
// good one -> remember (for batching) and propagate
|
||||
t->flags |= Tile::render;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool process(Cursor const& c) {
|
||||
Tile* t = c.current;
|
||||
|
||||
if (! (t->flags & Tile::visited)) {
|
||||
|
||||
t->flags |= Tile::visited;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void right(Cursor& c) const {
|
||||
|
||||
c.current += 1;
|
||||
if (c.current == c.firstInRow + _rendererRef._tiling.getAzimuthalTiles()) {
|
||||
c.current = c.firstInRow;
|
||||
}
|
||||
}
|
||||
void left(Cursor& c) const {
|
||||
|
||||
if (c.current == c.firstInRow) {
|
||||
c.current = c.firstInRow + _rendererRef._tiling.getAzimuthalTiles();
|
||||
}
|
||||
c.current -= 1;
|
||||
}
|
||||
void up(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current += d;
|
||||
c.firstInRow += d;
|
||||
}
|
||||
void down(Cursor& c) const {
|
||||
|
||||
unsigned d = _rendererRef._tiling.getAzimuthalTiles();
|
||||
c.current -= d;
|
||||
c.firstInRow -= d;
|
||||
}
|
||||
|
||||
void defer(Cursor const& t) {
|
||||
|
||||
*_stackPos++ = t;
|
||||
}
|
||||
|
||||
bool deferred(Cursor& cursor) {
|
||||
|
||||
if (_stackPos != _stackArray) {
|
||||
cursor = *--_stackPos;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected:
|
||||
bool select(Cursor const& cursor);
|
||||
bool process(Cursor const& cursor);
|
||||
void right(Cursor& cursor) const;
|
||||
void left(Cursor& cursor) const;
|
||||
void up(Cursor& cursor) const;
|
||||
void down(Cursor& cursor) const;
|
||||
void defer(Cursor const& cursor);
|
||||
bool deferred(Cursor& cursor);
|
||||
};
|
||||
|
||||
bool visitTile(Tile* t) {
|
||||
|
||||
unsigned index = t - _tileArray;
|
||||
*_outIndexPos++ = index;
|
||||
|
||||
if (! tileVisible(t, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t->lod != _minBright) {
|
||||
updateVertexCount(t, _minBright);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tileVisible(Tile* t, unsigned i) {
|
||||
|
||||
float slice = _tiling.getSliceAngle();
|
||||
float halfSlice = 0.5f * slice;
|
||||
unsigned stride = _tiling.getAzimuthalTiles();
|
||||
float azimuth = (i % stride) * slice;
|
||||
float altitude = (i / stride) * slice - Radians::halfPi();
|
||||
float gx = sin(azimuth);
|
||||
float gz = -cos(azimuth);
|
||||
float exz = cos(altitude);
|
||||
vec3 tileCenter = vec3(gx * exz, sin(altitude), gz * exz);
|
||||
float w = dot(_wRowVec, tileCenter);
|
||||
|
||||
float daz = halfSlice * cos(std::max(0.0f, abs(altitude) - halfSlice));
|
||||
float dal = halfSlice;
|
||||
float adjustedNear = cos(_halfPerspectiveAngle + sqrt(daz * daz + dal * dal));
|
||||
|
||||
// printLog("Stars.cpp: checking tile #%d, w = %f, near = %f\n", i, w, nearClip);
|
||||
|
||||
return w >= adjustedNear;
|
||||
}
|
||||
|
||||
void updateVertexCount(Tile* t, BrightnessLevel minBright) {
|
||||
|
||||
// a growing number of stars needs to be rendereed when the
|
||||
// minimum brightness decreases
|
||||
// perform a binary search in the so found partition for the
|
||||
// new vertex count of this tile
|
||||
|
||||
GpuVertex const* start = _dataArray + t[0].offset;
|
||||
GpuVertex const* end = _dataArray + t[1].offset;
|
||||
|
||||
assert(end >= start);
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
if (t->lod < minBright)
|
||||
end = start + t->count;
|
||||
else
|
||||
start += (t->count > 0 ? t->count - 1 : 0);
|
||||
|
||||
end = std::upper_bound(
|
||||
start, end, minBright, GreaterBrightness());
|
||||
|
||||
assert(end >= _dataArray + t[0].offset);
|
||||
|
||||
t->count = end - _dataArray - t[0].offset;
|
||||
t->lod = minBright;
|
||||
}
|
||||
|
||||
unsigned prepareBatch(unsigned const* indices,
|
||||
unsigned const* indicesEnd) {
|
||||
|
||||
unsigned nRanges = 0u;
|
||||
GLint* offs = _batchOffs;
|
||||
GLsizei* count = _batchCountArray;
|
||||
|
||||
for (unsigned* i = (unsigned*) _batchOffs;
|
||||
i != indicesEnd; ++i) {
|
||||
|
||||
Tile* t = _tileArray + *i;
|
||||
if ((t->flags & Tile::render) > 0u && t->count > 0u) {
|
||||
|
||||
*offs++ = t->offset;
|
||||
*count++ = t->count;
|
||||
++nRanges;
|
||||
}
|
||||
t->flags = 0;
|
||||
}
|
||||
return nRanges;
|
||||
}
|
||||
|
||||
bool visitTile(Tile* tile);
|
||||
bool isTileVisible(unsigned index);
|
||||
unsigned prepareBatch(unsigned const* indices, unsigned const* indicesEnd);
|
||||
|
||||
// GL API handling
|
||||
|
||||
void glAlloc() {
|
||||
|
||||
GLchar const* const VERTEX_SHADER =
|
||||
"#version 120\n"
|
||||
"uniform float alpha;\n"
|
||||
"void main(void) {\n"
|
||||
|
||||
" vec3 c = gl_Color.rgb * 1.0125;\n"
|
||||
" float s = max(1.0, dot(c, c) * 0.7);\n"
|
||||
|
||||
" gl_Position = ftransform();\n"
|
||||
" gl_FrontColor= gl_Color * alpha;\n"
|
||||
" gl_PointSize = s;\n"
|
||||
"}\n";
|
||||
|
||||
_program.addShaderFromSourceCode(QGLShader::Vertex, VERTEX_SHADER);
|
||||
GLchar const* const FRAGMENT_SHADER =
|
||||
"#version 120\n"
|
||||
"void main(void) {\n"
|
||||
" gl_FragColor = gl_Color;\n"
|
||||
"}\n";
|
||||
_program.addShaderFromSourceCode(QGLShader::Fragment, FRAGMENT_SHADER);
|
||||
_program.link();
|
||||
_alphaLocationHandle = _program.uniformLocation("alpha");
|
||||
|
||||
glGenBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glFree() {
|
||||
|
||||
glDeleteBuffersARB(1, & _vertexArrayHandle);
|
||||
}
|
||||
|
||||
void glUpload(GLsizei n) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
n * sizeof(GpuVertex), _dataArray, GL_STATIC_DRAW);
|
||||
//glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) {
|
||||
|
||||
// printLog("Stars.cpp: rendering %d-multibatch\n", n_ranges);
|
||||
|
||||
// for (int i = 0; i < n_ranges; ++i)
|
||||
// printLog("Stars.cpp: Batch #%d - %d stars @ %d\n", i,
|
||||
// _batchOffs[i], _batchCountArray[i]);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// setup modelview matrix
|
||||
glPushMatrix();
|
||||
glLoadMatrixf(matrix);
|
||||
|
||||
// set point size and smoothing + shader control
|
||||
glPointSize(1.0f);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
// select shader and vertex array
|
||||
_program.bind();
|
||||
_program.setUniformValue(_alphaLocationHandle, alpha);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, _vertexArrayHandle);
|
||||
glInterleavedArrays(GL_C4UB_V3F, sizeof(GpuVertex), 0l);
|
||||
|
||||
// render
|
||||
glMultiDrawArrays(GL_POINTS,
|
||||
_batchOffs, _batchCountArray, n_ranges);
|
||||
|
||||
// restore state
|
||||
glBindBufferARB(GL_ARRAY_BUFFER, 0);
|
||||
_program.release();
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glDisable(GL_POINT_SMOOTH);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void glAlloc();
|
||||
void glFree();
|
||||
void glUpload(GLsizei numStars);
|
||||
void glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha);
|
||||
|
||||
// variables
|
||||
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
GpuVertex* _dataArray;
|
||||
Tile* _tileArray;
|
||||
GLint* _batchOffs;
|
||||
GLsizei* _batchCountArray;
|
||||
GLuint _vertexArrayHandle;
|
||||
ProgramObject _program;
|
||||
int _alphaLocationHandle;
|
||||
|
||||
Tiling _tiling;
|
||||
Tiling _tiling;
|
||||
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
BrightnessLevel _minBright;
|
||||
unsigned* _outIndexPos;
|
||||
vec3 _wRowVec;
|
||||
float _halfPerspectiveAngle;
|
||||
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
64
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
64
interface/src/starfield/renderer/Tiling.h
Normal file → Executable file
|
@ -9,64 +9,38 @@
|
|||
#ifndef __interface__starfield__renderer__Tiling__
|
||||
#define __interface__starfield__renderer__Tiling__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
const float LOG2 = 1.4426950408889634;
|
||||
|
||||
class Tiling {
|
||||
public:
|
||||
|
||||
Tiling(unsigned k) :
|
||||
_valK(k),
|
||||
_rcpSlice(k / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * 1.4426950408889634); // log2
|
||||
}
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _valK; }
|
||||
unsigned getAltitudinalTiles() const { return _valK / 2 + 1; }
|
||||
Tiling(unsigned tileResolution) : _tileResolution(tileResolution), _rcpSlice(tileResolution / Radians::twicePi()) {
|
||||
_nBits = ceil(log(getTileCount()) * LOG2); }
|
||||
|
||||
unsigned getAzimuthalTiles() const { return _tileResolution; }
|
||||
unsigned getAltitudinalTiles() const { return _tileResolution / 2 + 1; }
|
||||
unsigned getTileIndexBits() const { return _nBits; }
|
||||
|
||||
unsigned getTileCount() const {
|
||||
return getAzimuthalTiles() * getAltitudinalTiles();
|
||||
}
|
||||
|
||||
unsigned getTileIndex(float azimuth, float altitude) const {
|
||||
return discreteAzimuth(azimuth) +
|
||||
_valK * discreteAltitude(altitude);
|
||||
}
|
||||
|
||||
float getSliceAngle() const {
|
||||
return 1.0f / _rcpSlice;
|
||||
}
|
||||
unsigned getTileCount() const { return getAzimuthalTiles() * getAltitudinalTiles(); }
|
||||
unsigned getTileIndex(float azimuth, float altitude) const { return discreteAzimuth(azimuth) +
|
||||
_tileResolution * discreteAltitude(altitude); }
|
||||
float getSliceAngle() const { return 1.0f / _rcpSlice; }
|
||||
|
||||
private:
|
||||
|
||||
unsigned discreteAngle(float unsigned_angle) const {
|
||||
|
||||
return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f));
|
||||
}
|
||||
|
||||
unsigned discreteAzimuth(float a) const {
|
||||
return discreteAngle(a) % _valK;
|
||||
}
|
||||
|
||||
unsigned discreteAltitude(float a) const {
|
||||
return min(getAltitudinalTiles() - 1,
|
||||
discreteAngle(a + Radians::halfPi()) );
|
||||
}
|
||||
unsigned discreteAngle(float unsigned_angle) const { return unsigned(floor(unsigned_angle * _rcpSlice + 0.5f)); }
|
||||
unsigned discreteAzimuth(float angle) const { return discreteAngle(angle) % _tileResolution; }
|
||||
unsigned discreteAltitude(float angle) const { return min( getAltitudinalTiles() - 1,
|
||||
discreteAngle(angle + Radians::halfPi()) ); }
|
||||
|
||||
// variables
|
||||
|
||||
unsigned _valK;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
unsigned _tileResolution;
|
||||
float _rcpSlice;
|
||||
unsigned _nBits;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
18
interface/src/starfield/renderer/VertexOrder.cpp
Executable file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// starfield/renderer/VertexOrder.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Chris Barnard on 10/17/13.
|
||||
// Based on code by Tobias Schwinger on 3/22/13.
|
||||
//
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "starfield/renderer/VertexOrder.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool VertexOrder::bit(InputVertex const& vertex, state_type const& state) const {
|
||||
unsigned key = _tiling.getTileIndex(vertex.getAzimuth(), vertex.getAltitude());
|
||||
return base::bit(key, state);
|
||||
}
|
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
24
interface/src/starfield/renderer/VertexOrder.h
Normal file → Executable file
|
@ -9,37 +9,23 @@
|
|||
#ifndef __interface__starfield__renderer__VertexOrder__
|
||||
#define __interface__starfield__renderer__VertexOrder__
|
||||
|
||||
#ifndef __interface__Starfield_impl__
|
||||
#error "This is an implementation file - not intended for direct inclusion."
|
||||
#endif
|
||||
|
||||
#include "starfield/Config.h"
|
||||
#include "starfield/data/InputVertex.h"
|
||||
#include "starfield/renderer/Tiling.h"
|
||||
|
||||
namespace starfield {
|
||||
|
||||
/**
|
||||
* Defines the vertex order for the renderer as a bit extractor for
|
||||
* binary in-place Radix Sort.
|
||||
*/
|
||||
// Defines the vertex order for the renderer as a bit extractor for
|
||||
//binary in-place Radix Sort.
|
||||
|
||||
class VertexOrder : public Radix2IntegerScanner<unsigned>
|
||||
{
|
||||
public:
|
||||
explicit VertexOrder(Tiling const& tiling) :
|
||||
|
||||
base(tiling.getTileIndexBits() + BrightnessBits),
|
||||
_tiling(tiling) {
|
||||
}
|
||||
base(tiling.getTileIndexBits()), _tiling(tiling) { }
|
||||
|
||||
bool bit(InputVertex const& v, state_type const& s) const {
|
||||
|
||||
// inspect (tile_index, brightness) tuples
|
||||
unsigned key = getBrightness(v.getColor()) ^ BrightnessMask;
|
||||
key |= _tiling.getTileIndex(
|
||||
v.getAzimuth(), v.getAltitude()) << BrightnessBits;
|
||||
return base::bit(key, s);
|
||||
}
|
||||
bool bit(InputVertex const& vertex, state_type const& state) const;
|
||||
|
||||
private:
|
||||
Tiling _tiling;
|
||||
|
|
|
@ -151,7 +151,7 @@ void NodeList::processNodeData(sockaddr* senderAddress, unsigned char* packetDat
|
|||
}
|
||||
case PACKET_TYPE_PING_REPLY: {
|
||||
// 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
|
||||
timePingReply(senderAddress, packetData);
|
||||
|
@ -199,6 +199,7 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
}
|
||||
|
||||
currentPosition += updateNodeWithData(matchingNode,
|
||||
NULL,
|
||||
packetHolder,
|
||||
numTotalBytes - (currentPosition - startPosition));
|
||||
|
||||
|
@ -206,35 +207,32 @@ void NodeList::processBulkNodeData(sockaddr *senderAddress, unsigned char *packe
|
|||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes) {
|
||||
// find the node by the sockaddr
|
||||
Node* matchingNode = nodeWithAddress(senderAddress);
|
||||
|
||||
if (matchingNode) {
|
||||
return updateNodeWithData(matchingNode, packetData, dataBytes);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int NodeList::updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes) {
|
||||
int NodeList::updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes) {
|
||||
node->lock();
|
||||
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
if (senderAddress) {
|
||||
activateSocketFromNodeCommunication(senderAddress);
|
||||
}
|
||||
|
||||
if (node->getActiveSocket() || !senderAddress) {
|
||||
node->recordBytesReceived(dataBytes);
|
||||
|
||||
if (!node->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(node);
|
||||
}
|
||||
|
||||
int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
||||
node->unlock();
|
||||
|
||||
return numParsedBytes;
|
||||
} else {
|
||||
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
|
||||
node->unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!node->getLinkedData() && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(node);
|
||||
}
|
||||
|
||||
int numParsedBytes = node->getLinkedData()->parseData(packetData, dataBytes);
|
||||
|
||||
node->unlock();
|
||||
|
||||
return numParsedBytes;
|
||||
}
|
||||
|
||||
Node* NodeList::nodeWithAddress(sockaddr *senderAddress) {
|
||||
|
@ -601,7 +599,6 @@ void NodeList::pingPublicAndLocalSocketsForInactiveNode(Node* node) const {
|
|||
currentTime = usecTimestampNow();
|
||||
memcpy(pingPacket + numHeaderBytes, ¤tTime, sizeof(currentTime));
|
||||
|
||||
qDebug() << "Attemping to ping" << *node << "\n";
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
_nodeSocket.send(node->getLocalSocket(), pingPacket, sizeof(pingPacket));
|
||||
_nodeSocket.send(node->getPublicSocket(), pingPacket, sizeof(pingPacket));
|
||||
|
@ -672,7 +669,25 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
|
|||
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++) {
|
||||
if (!node->getActiveSocket()) {
|
||||
// check both the public and local addresses for each node to see if we find a match
|
||||
|
@ -700,6 +715,22 @@ Node* NodeList::soloNodeOfType(char nodeType) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void NodeList::killNode(Node* node, bool mustLockNode) {
|
||||
if (mustLockNode) {
|
||||
node->lock();
|
||||
}
|
||||
|
||||
qDebug() << "Killed " << *node << "\n";
|
||||
|
||||
notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node->setAlive(false);
|
||||
|
||||
if (mustLockNode) {
|
||||
node->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void* removeSilentNodes(void *args) {
|
||||
NodeList* nodeList = (NodeList*) args;
|
||||
uint64_t checkTimeUsecs = 0;
|
||||
|
@ -713,12 +744,8 @@ void* removeSilentNodes(void *args) {
|
|||
node->lock();
|
||||
|
||||
if ((usecTimestampNow() - node->getLastHeardMicrostamp()) > NODE_SILENCE_THRESHOLD_USECS) {
|
||||
|
||||
qDebug() << "Killed " << *node << "\n";
|
||||
|
||||
nodeList->notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node->setAlive(false);
|
||||
// kill this node, don't lock - we already did it
|
||||
nodeList->killNode(&(*node), false);
|
||||
}
|
||||
|
||||
node->unlock();
|
||||
|
|
|
@ -113,12 +113,12 @@ public:
|
|||
Node* nodeWithUUID(const QUuid& nodeUUID);
|
||||
|
||||
Node* addOrUpdateNode(const QUuid& uuid, char nodeType, sockaddr* publicSocket, sockaddr* localSocket);
|
||||
void killNode(Node* node, bool mustLockNode = true);
|
||||
|
||||
void processNodeData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||
void processBulkNodeData(sockaddr *senderAddress, unsigned char *packetData, int numTotalBytes);
|
||||
|
||||
int updateNodeWithData(sockaddr *senderAddress, unsigned char *packetData, size_t dataBytes);
|
||||
int updateNodeWithData(Node *node, unsigned char *packetData, int dataBytes);
|
||||
int updateNodeWithData(Node *node, sockaddr* senderAddress, unsigned char *packetData, int dataBytes);
|
||||
|
||||
unsigned broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes);
|
||||
|
||||
|
@ -140,6 +140,7 @@ public:
|
|||
void addDomainListener(DomainChangeListener* listener);
|
||||
void removeDomainListener(DomainChangeListener* listener);
|
||||
|
||||
void possiblyPingInactiveNodes();
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
|
@ -172,7 +173,7 @@ private:
|
|||
uint16_t _publicPort;
|
||||
bool _shouldUseDomainServerAsSTUN;
|
||||
|
||||
void activateSocketFromPingReply(sockaddr *nodeAddress);
|
||||
void activateSocketFromNodeCommunication(sockaddr *nodeAddress);
|
||||
void timePingReply(sockaddr *nodeAddress, unsigned char *packetData);
|
||||
|
||||
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 {
|
||||
// send data via UDP
|
||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (sent_bytes != byteLength) {
|
||||
qDebug("Failed to send packet: %s\n", strerror(errno));
|
||||
return false;
|
||||
if (destAddress) {
|
||||
// send data via UDP
|
||||
int sent_bytes = sendto(handle, (const char*)data, byteLength,
|
||||
0, (sockaddr *) destAddress, sizeof(sockaddr_in));
|
||||
|
||||
if (sent_bytes != byteLength) {
|
||||
qDebug("Failed to send packet: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return sent_bytes;
|
||||
} else {
|
||||
qDebug("UDPSocket send called with NULL destination address - Likely a node with no active socket.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent_bytes;
|
||||
}
|
||||
|
||||
int UDPSocket::send(const char* destAddress, int destPort, const void* data, size_t byteLength) const {
|
||||
|
|
|
@ -327,6 +327,9 @@ void VoxelServer::run() {
|
|||
NodeList* nodeList = NodeList::getInstance();
|
||||
nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER);
|
||||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1);
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// tell our NodeList about our desire to get notifications
|
||||
|
@ -434,6 +437,9 @@ void VoxelServer::run() {
|
|||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
// ping our inactive nodes to punch holes with them
|
||||
nodeList->possiblyPingInactiveNodes();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
|
@ -445,23 +451,16 @@ void VoxelServer::run() {
|
|||
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
|
||||
NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
Node* node = NodeList::getInstance()->addOrUpdateNode(nodeUUID,
|
||||
NODE_TYPE_AGENT,
|
||||
&senderAddress,
|
||||
&senderAddress);
|
||||
Node* node = nodeList->nodeWithUUID(nodeUUID);
|
||||
|
||||
// temp activation of public socket before server ping/reply is setup
|
||||
if (!node->getActiveSocket()) {
|
||||
node->activatePublicSocket();
|
||||
if (node) {
|
||||
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
|
||||
|
||||
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
|
||||
|| packetData[0] == PACKET_TYPE_DOMAIN
|
||||
|| packetData[0] == PACKET_TYPE_STUN_RESPONSE) {
|
||||
|
|
Loading…
Reference in a new issue