diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index ba70fd58ec..552910e04d 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -36,4 +36,4 @@ private: std::vector _audioInjectors; }; -#endif /* defined(__hifi__Operative__) */ +#endif /* defined(__hifi__Agent__) */ diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 1b44c2e905..aab11b1317 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -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); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b515f1400c..4e58f0b014 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -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; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 39c68d550f..565dfb9e2b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -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::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::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) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 205d9e8f6e..45730847ac 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -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(); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 308ce33324..4709bf234d 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -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) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a157bcdedf..dd69880eea 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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 diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp old mode 100644 new mode 100755 index e663ef33bd..69340b2926 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -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); } diff --git a/interface/src/Stars.h b/interface/src/Stars.h old mode 100644 new mode 100755 index 83d55d3766..f6fe7cfb80 --- a/interface/src/Stars.h +++ b/interface/src/Stars.h @@ -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; }; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 9b6cf9dd78..f65ceac057 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -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(), property.properties.at(5).value()); + } else if (property.properties.at(0) == "RotationOffset") { + rotationOffset = glm::vec3(property.properties.at(3).value(), + property.properties.at(4).value(), + property.properties.at(5).value()); + } else if (property.properties.at(0) == "RotationPivot") { rotationPivot = glm::vec3(property.properties.at(3).value(), property.properties.at(4).value(), @@ -741,6 +747,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) property.properties.at(5).value(), property.properties.at(6).value()); + } else if (property.properties.at(0) == "RotationOffset") { + rotationOffset = glm::vec3(property.properties.at(4).value(), + property.properties.at(5).value(), + property.properties.at(6).value()); + } else if (property.properties.at(0) == "RotationPivot") { rotationPivot = glm::vec3(property.properties.at(4).value(), property.properties.at(5).value(), @@ -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(), model); + } else if (object.name == "Texture") { foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { diff --git a/interface/src/starfield/CMakeLists.txt b/interface/src/starfield/CMakeLists.txt deleted file mode 100644 index 1cd1dd34c5..0000000000 --- a/interface/src/starfield/CMakeLists.txt +++ /dev/null @@ -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}) - diff --git a/interface/src/starfield/Config.h b/interface/src/starfield/Config.h old mode 100644 new mode 100755 index 3cbff171c7..d2d93be407 --- a/interface/src/starfield/Config.h +++ b/interface/src/starfield/Config.h @@ -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 -#if STARFIELD_MULTITHREADING -#include -#include -#endif - #include #include #include @@ -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 } diff --git a/interface/src/starfield/Controller.cpp b/interface/src/starfield/Controller.cpp new file mode 100755 index 0000000000..2ce2a071cb --- /dev/null +++ b/interface/src/starfield/Controller.cpp @@ -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); +} diff --git a/interface/src/starfield/Controller.h b/interface/src/starfield/Controller.h old mode 100644 new mode 100755 index 186a23c742..c5ade48442 --- a/interface/src/starfield/Controller.h +++ b/interface/src/starfield/Controller.h @@ -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 +#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 { - - typedef Radix2IntegerScanner 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 _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 _lodBrightness; - BrightnessLevel _lodAllocBrightness; - - atomic _renderer; - - typedef lock_guard lock; -#else - BrightnessLevel _lodBrightness; - BrightnessLevel _lodAllocBrightness; - - Renderer* _renderer; - - #undef lock - #undef _ -#endif + InputVertices _inputSequence; + unsigned _tileResolution; + unsigned _numStars; + Renderer* _renderer; }; } - - #endif diff --git a/interface/src/starfield/Generator.cpp b/interface/src/starfield/Generator.cpp new file mode 100644 index 0000000000..1e161a19cf --- /dev/null +++ b/interface/src/starfield/Generator.cpp @@ -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; +} \ No newline at end of file diff --git a/interface/src/starfield/Generator.h b/interface/src/starfield/Generator.h new file mode 100644 index 0000000000..1029bd6a52 --- /dev/null +++ b/interface/src/starfield/Generator.h @@ -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 +#include + +#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 \ No newline at end of file diff --git a/interface/src/starfield/data/BrightnessLevel.h b/interface/src/starfield/data/BrightnessLevel.h deleted file mode 100644 index 3567b88478..0000000000 --- a/interface/src/starfield/data/BrightnessLevel.h +++ /dev/null @@ -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 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 - diff --git a/interface/src/starfield/data/GpuVertex.cpp b/interface/src/starfield/data/GpuVertex.cpp new file mode 100755 index 0000000000..fdec1b85d2 --- /dev/null +++ b/interface/src/starfield/data/GpuVertex.cpp @@ -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); +} diff --git a/interface/src/starfield/data/GpuVertex.h b/interface/src/starfield/data/GpuVertex.h old mode 100644 new mode 100755 index c194e8cdb9..39eeb19634 --- a/interface/src/starfield/data/GpuVertex.h +++ b/interface/src/starfield/data/GpuVertex.h @@ -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 diff --git a/interface/src/starfield/data/InputVertex.cpp b/interface/src/starfield/data/InputVertex.cpp new file mode 100755 index 0000000000..28bffb0ec0 --- /dev/null +++ b/interface/src/starfield/data/InputVertex.cpp @@ -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(azimuth); + altitude = angleConvert(altitude); + + angleHorizontalPolar(azimuth, altitude); + + _azimuth = azimuth; + _altitude = altitude; +} diff --git a/interface/src/starfield/data/InputVertex.h b/interface/src/starfield/data/InputVertex.h old mode 100644 new mode 100755 index ca247a3ba4..d8c21a37a2 --- a/interface/src/starfield/data/InputVertex.h +++ b/interface/src/starfield/data/InputVertex.h @@ -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(azimuth); - altitude = angleConvert(altitude); - - angleHorizontalPolar(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 InputVertices; -} // anonymous namespace +} #endif diff --git a/interface/src/starfield/data/Tile.h b/interface/src/starfield/data/Tile.h old mode 100644 new mode 100755 index a7c26c7c8f..bdef4e612f --- a/interface/src/starfield/data/Tile.h +++ b/interface/src/starfield/data/Tile.h @@ -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 diff --git a/interface/src/starfield/renderer/Renderer.cpp b/interface/src/starfield/renderer/Renderer.cpp new file mode 100755 index 0000000000..d1a8cbab8d --- /dev/null +++ b/interface/src/starfield/renderer/Renderer.cpp @@ -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(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; +} \ No newline at end of file diff --git a/interface/src/starfield/renderer/Renderer.h b/interface/src/starfield/renderer/Renderer.h old mode 100644 new mode 100755 index 7eab70d178..7504dd0182 --- a/interface/src/starfield/renderer/Renderer.h +++ b/interface/src/starfield/renderer/Renderer.h @@ -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(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 diff --git a/interface/src/starfield/renderer/Tiling.h b/interface/src/starfield/renderer/Tiling.h old mode 100644 new mode 100755 index fdec54dfc7..53b17a9844 --- a/interface/src/starfield/renderer/Tiling.h +++ b/interface/src/starfield/renderer/Tiling.h @@ -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 diff --git a/interface/src/starfield/renderer/VertexOrder.cpp b/interface/src/starfield/renderer/VertexOrder.cpp new file mode 100755 index 0000000000..78f5229f90 --- /dev/null +++ b/interface/src/starfield/renderer/VertexOrder.cpp @@ -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); +} \ No newline at end of file diff --git a/interface/src/starfield/renderer/VertexOrder.h b/interface/src/starfield/renderer/VertexOrder.h old mode 100644 new mode 100755 index d74864af18..85c738ce0d --- a/interface/src/starfield/renderer/VertexOrder.h +++ b/interface/src/starfield/renderer/VertexOrder.h @@ -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 { 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; diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 8d8d48dcdc..a7a6dac5aa 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -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(); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index ed7f0e637a..5361f12ca6 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -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 _hooks; diff --git a/libraries/shared/src/UDPSocket.cpp b/libraries/shared/src/UDPSocket.cpp index 73849847e4..1318fbc4a5 100644 --- a/libraries/shared/src/UDPSocket.cpp +++ b/libraries/shared/src/UDPSocket.cpp @@ -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 { diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index a24f13854b..3bacfd8462 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -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) {