3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-26 21:35:45 +02:00

Merge branch 'master' of https://github.com/worklist/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2013-12-05 17:53:21 -08:00
commit e97099a9b2
187 changed files with 8913 additions and 11093 deletions
CMakeLists.txt
animation-server
assignment-client
cmake/modules
domain-server/src
hardware/head_hand
interface
libraries

View file

@ -17,5 +17,4 @@ add_subdirectory(assignment-client)
add_subdirectory(domain-server)
add_subdirectory(interface)
add_subdirectory(pairing-server)
add_subdirectory(space-server)
add_subdirectory(voxel-edit)

View file

@ -20,6 +20,9 @@ setup_hifi_project(${TARGET_NAME} TRUE)
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi octree library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi voxels library
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})

View file

@ -159,7 +159,7 @@ static void renderMovingBug() {
}
// send the "erase message" first...
PACKET_TYPE message = PACKET_TYPE_ERASE_VOXEL;
PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
// Move the bug...
@ -219,7 +219,7 @@ static void renderMovingBug() {
}
// send the "create message" ...
message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
}
@ -254,7 +254,7 @@ static void sendVoxelBlinkMessage() {
detail.green = 0 * ::intensity;
detail.blue = 0 * ::intensity;
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
::voxelEditPacketSender->sendVoxelEditMessage(message, detail);
}
@ -271,7 +271,7 @@ unsigned char onColor[3] = { 0, 255, 255 };
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
static void sendBlinkingStringOfLights() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = STRING_OF_LIGHTS_SIZE;
static VoxelDetail details[LIGHTS_PER_SEGMENT];
@ -377,7 +377,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
void sendDanceFloor() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
@ -493,7 +493,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
};
static void sendBillboard() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = BILLBOARD_LIGHT_SIZE;
static VoxelDetail details[VOXELS_PER_PACKET];
@ -564,7 +564,7 @@ void doBuildStreet() {
return;
}
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
static VoxelDetail details[BRICKS_PER_PACKET];
for (int z = 0; z < ROAD_LENGTH; z++) {
@ -841,7 +841,7 @@ int main(int argc, const char * argv[])
pthread_t animateVoxelThread;
pthread_create(&animateVoxelThread, NULL, animateVoxels, NULL);
sockaddr nodePublicAddress;
HifiSockAddr nodeSockAddr;
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t receivedBytes;
@ -858,15 +858,18 @@ int main(int argc, const char * argv[])
}
// Nodes sending messages to us...
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) &&
if (nodeList->getNodeSocket().hasPendingDatagrams()
&& (receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
nodeSockAddr.getAddressPointer(),
nodeSockAddr.getPortPointer())) &&
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
if (::jurisdictionListener) {
::jurisdictionListener->queueReceivedPacket(nodePublicAddress, packetData, receivedBytes);
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
}
}
NodeList::getInstance()->processNodeData(&nodePublicAddress, packetData, receivedBytes);
NodeList::getInstance()->processNodeData(nodeSockAddr, packetData, receivedBytes);
}
}

View file

@ -24,8 +24,13 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR})
#testing
include_directories(${ROOT_DIR}/externals/civetweb/include)

View file

@ -6,7 +6,12 @@
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <curl/curl.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QEventLoop>
#include <QtCore/QTimer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <AvatarData.h>
#include <NodeList.h>
@ -14,31 +19,12 @@
#include <VoxelConstants.h>
#include "Agent.h"
#include "voxels/VoxelScriptingInterface.h"
Agent::Agent(const unsigned char* dataBuffer, int numBytes) :
Assignment(dataBuffer, numBytes),
_shouldStop(false)
ThreadedAssignment(dataBuffer, numBytes)
{
}
void Agent::stop() {
_shouldStop = true;
}
static size_t writeScriptDataToString(void *contents, size_t size, size_t nmemb, void *userdata) {
size_t realSize = size * nmemb;
QString* scriptContents = (QString*) userdata;
// append this chunk to the scriptContents
scriptContents->append(QByteArray((char*) contents, realSize));
// return the amount of data read
return realSize;
}
QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", vec3.x);
@ -53,6 +39,16 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
vec3.z = object.property("z").toVariant().toFloat();
}
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
if (dataByteArray[0] == PACKET_TYPE_JURISDICTION) {
_voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(),
dataByteArray.size());
} else {
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
}
}
void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
@ -60,149 +56,93 @@ void Agent::run() {
const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER };
nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST));
nodeList->getNodeSocket()->setBlocking(false);
// figure out the URL for the script for this agent assignment
QString scriptURLString("http://%1:8080/assignment/%2");
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
uuidStringWithoutCurlyBraces(_uuid));
// setup curl for script download
CURLcode curlResult;
CURL* curlHandle = curl_easy_init();
// tell curl which file to grab
curl_easy_setopt(curlHandle, CURLOPT_URL, scriptURLString.toStdString().c_str());
// send the data to the WriteMemoryCallback function
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeScriptDataToString);
QString scriptContents;
// pass the scriptContents QString to append data to
curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, (void *)&scriptContents);
// send a user agent since some servers will require it
curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
// make sure CURL fails on a 400 code
curl_easy_setopt(curlHandle, CURLOPT_FAILONERROR, true);
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
QNetworkReply *reply = networkManager->get(QNetworkRequest(QUrl(scriptURLString)));
qDebug() << "Downloading script at" << scriptURLString << "\n";
// blocking get for JS file
curlResult = curl_easy_perform(curlHandle);
if (curlResult == CURLE_OK) {
// cleanup curl
curl_easy_cleanup(curlHandle);
curl_global_cleanup();
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QString scriptContents(reply->readAll());
QScriptEngine engine;
// register meta-type for glm::vec3 conversions
qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue);
QScriptValue agentValue = engine.newQObject(this);
engine.globalObject().setProperty("Agent", agentValue);
QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
// let the VoxelPacketSender know how frequently we plan to call it
_voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
qDebug() << "Downloaded script:" << scriptContents << "\n";
QScriptValue result = engine.evaluate(scriptContents);
qDebug() << "Evaluated script.\n";
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
}
timeval startTime;
gettimeofday(&startTime, NULL);
int thisFrame = 0;
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
QTimer* silentNodeTimer = new QTimer(this);
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
QTimer* pingNodesTimer = new QTimer(this);
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
while (!_isFinished) {
QScriptEngine engine;
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
// register meta-type for glm::vec3 conversions
qScriptRegisterMetaType(&engine, vec3toScriptValue, vec3FromScriptValue);
QCoreApplication::processEvents();
QScriptValue agentValue = engine.newQObject(this);
engine.globalObject().setProperty("Agent", agentValue);
VoxelScriptingInterface voxelScripter;
QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
// let the VoxelPacketSender know how frequently we plan to call it
voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
// hook in a constructor for audio injectorss
AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
QScriptValue audioInjectorValue = engine.newQObject(&scriptedAudioInjector);
engine.globalObject().setProperty("AudioInjector", audioInjectorValue);
qDebug() << "Downloaded script:" << scriptContents << "\n";
QScriptValue result = engine.evaluate(scriptContents);
qDebug() << "Evaluated script.\n";
if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) {
// allow the scripter's call back to setup visual data
emit willSendVisualDataCallback();
// release the queue of edit voxel messages.
_voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();
// since we're in non-threaded mode, call process so that the packets are sent
_voxelScriptingInterface.getVoxelPacketSender()->process();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
}
timeval startTime;
gettimeofday(&startTime, NULL);
timeval lastDomainServerCheckIn = {};
sockaddr_in senderAddress;
unsigned char receivedData[MAX_PACKET_SIZE];
ssize_t receivedBytes;
int thisFrame = 0;
NodeList::getInstance()->startSilentNodeRemovalThread();
while (!_shouldStop) {
// if we're not hearing from the domain-server we should stop running
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
break;
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn();
}
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) {
timeval thisSend = {};
gettimeofday(&thisSend, NULL);
// allow the scripter's call back to setup visual data
emit willSendVisualDataCallback();
// release the queue of edit voxel messages.
voxelScripter.getVoxelPacketSender()->releaseQueuedMessages();
// since we're in non-threaded mode, call process so that the packets are sent
voxelScripter.getVoxelPacketSender()->process();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
}
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)
&& packetVersionMatch(receivedData)) {
if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
voxelScripter.getJurisdictionListener()->queueReceivedPacket((sockaddr&) senderAddress,
receivedData,
receivedBytes);
} else {
NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes);
}
}
}
NodeList::getInstance()->stopSilentNodeRemovalThread();
} else {
// error in curl_easy_perform
qDebug() << "curl_easy_perform for JS failed:" << curl_easy_strerror(curlResult) << "\n";
// cleanup curl
curl_easy_cleanup(curlHandle);
curl_global_cleanup();
}
}

View file

@ -15,25 +15,24 @@
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <AudioInjector.h>
#include <Assignment.h>
#include <ThreadedAssignment.h>
class Agent : public Assignment {
#include "voxels/VoxelScriptingInterface.h"
class Agent : public ThreadedAssignment {
Q_OBJECT
public:
Agent(const unsigned char* dataBuffer, int numBytes);
void run();
public slots:
void stop();
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
signals:
void willSendAudioDataCallback();
void willSendVisualDataCallback();
private:
static QScriptValue AudioInjectorConstructor(QScriptContext *context, QScriptEngine *engine);
bool volatile _shouldStop;
std::vector<AudioInjector*> _audioInjectors;
VoxelScriptingInterface _voxelScriptingInterface;
};
#endif /* defined(__hifi__Agent__) */

View file

@ -0,0 +1,140 @@
//
// AssignmentClient.cpp
// hifi
//
// Created by Stephen Birarda on 11/25/2013.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <Assignment.h>
#include <Logging.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include "AssignmentFactory.h"
#include "AssignmentClient.h"
const char ASSIGNMENT_CLIENT_TARGET_NAME[] = "assignment-client";
const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000;
AssignmentClient::AssignmentClient(int &argc, char **argv,
Assignment::Type requestAssignmentType,
const HifiSockAddr& customAssignmentServerSocket,
const char* requestAssignmentPool) :
QCoreApplication(argc, argv),
_requestAssignment(Assignment::RequestCommand, requestAssignmentType, requestAssignmentPool),
_currentAssignment(NULL)
{
// register meta type is required for queued invoke method on Assignment subclasses
qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
// set the logging target to the the CHILD_TARGET_NAME
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
// create a NodeList as an unassigned client
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
// set the custom assignment socket if we have it
if (!customAssignmentServerSocket.isNull()) {
nodeList->setAssignmentServerSocket(customAssignmentServerSocket);
}
// call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required
qDebug() << "Waiting for assignment -" << _requestAssignment << "\n";
QTimer* timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest()));
timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS);
// connect our readPendingDatagrams method to the readyRead() signal of the socket
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
void AssignmentClient::sendAssignmentRequest() {
if (!_currentAssignment) {
NodeList::getInstance()->sendAssignment(_requestAssignment);
}
}
void AssignmentClient::readPendingDatagrams() {
NodeList* nodeList = NodeList::getInstance();
static unsigned char packetData[1500];
static qint64 receivedBytes = 0;
static HifiSockAddr senderSockAddr;
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))
&& packetVersionMatch(packetData)) {
if (_currentAssignment) {
// have the threaded current assignment handle this datagram
QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) packetData, receivedBytes)),
Q_ARG(HifiSockAddr, senderSockAddr));
} else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
if (_currentAssignment) {
qDebug() << "Dropping received assignment since we are currently running one.\n";
} else {
// construct the deployed assignment from the packet data
_currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
qDebug() << "Received an assignment -" << *_currentAssignment << "\n";
// switch our nodelist domain IP and port to whoever sent us the assignment
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
nodeList->setDomainSockAddr(senderSockAddr);
nodeList->setOwnerUUID(_currentAssignment->getUUID());
qDebug("Destination IP for assignment is %s\n",
nodeList->getDomainIP().toString().toStdString().c_str());
// start the deployed assignment
QThread* workerThread = new QThread(this);
connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run()));
connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted()));
connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit()));
connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
_currentAssignment->moveToThread(workerThread);
// Starts an event loop, and emits workerThread->started()
workerThread->start();
} else {
qDebug("Received a bad destination socket for assignment.\n");
}
}
} else {
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, packetData, receivedBytes);
}
}
}
}
void AssignmentClient::assignmentCompleted() {
// reset the logging target to the the CHILD_TARGET_NAME
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
qDebug("Assignment finished or never started - waiting for new assignment\n");
_currentAssignment = NULL;
NodeList* nodeList = NodeList::getInstance();
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->reset();
}

View file

@ -0,0 +1,32 @@
//
// AssignmentClient.h
// hifi
//
// Created by Stephen Birarda on 11/25/2013.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#ifndef __hifi__AssignmentClient__
#define __hifi__AssignmentClient__
#include <QtCore/QCoreApplication>
#include "ThreadedAssignment.h"
class AssignmentClient : public QCoreApplication {
Q_OBJECT
public:
AssignmentClient(int &argc, char **argv,
Assignment::Type requestAssignmentType = Assignment::AllTypes,
const HifiSockAddr& customAssignmentServerSocket = HifiSockAddr(),
const char* requestAssignmentPool = NULL);
private slots:
void sendAssignmentRequest();
void readPendingDatagrams();
void assignmentCompleted();
private:
Assignment _requestAssignment;
ThreadedAssignment* _currentAssignment;
};
#endif /* defined(__hifi__AssignmentClient__) */

View file

@ -12,10 +12,11 @@
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
#include <VoxelServer.h>
#include <ParticleServer.h>
#include "AssignmentFactory.h"
Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer, int numBytes) {
int headerBytes = numBytesForPacketHeader(dataBuffer);
Assignment::Type assignmentType = Assignment::AllTypes;
@ -30,7 +31,9 @@ Assignment* AssignmentFactory::unpackAssignment(const unsigned char* dataBuffer,
return new Agent(dataBuffer, numBytes);
case Assignment::VoxelServerType:
return new VoxelServer(dataBuffer, numBytes);
case Assignment::ParticleServerType:
return new ParticleServer(dataBuffer, numBytes);
default:
return new Assignment(dataBuffer, numBytes);
return NULL;
}
}

View file

@ -9,11 +9,11 @@
#ifndef __hifi__AssignmentFactory__
#define __hifi__AssignmentFactory__
#include "Assignment.h"
#include <ThreadedAssignment.h>
class AssignmentFactory {
public:
static Assignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
static ThreadedAssignment* unpackAssignment(const unsigned char* dataBuffer, int numBytes);
};
#endif /* defined(__hifi__AssignmentFactory__) */

View file

@ -32,6 +32,9 @@
#include <glm/gtx/norm.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <Logging.h>
#include <NodeList.h>
#include <Node.h>
@ -51,7 +54,7 @@
const short JITTER_BUFFER_MSECS = 12;
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE) * 1000 * 1000);
const int MAX_SAMPLE_VALUE = std::numeric_limits<int16_t>::max();
const int MIN_SAMPLE_VALUE = std::numeric_limits<int16_t>::min();
@ -64,7 +67,9 @@ void attachNewBufferToNode(Node *newNode) {
}
}
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
AudioMixer::AudioMixer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
{
}
@ -217,78 +222,77 @@ void AudioMixer::prepareMixForListeningNode(Node* node) {
}
}
void AudioMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
// pull any new audio data from nodes off of the network stack
if (dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO
|| dataByteArray[0] == PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO
|| dataByteArray[0] == PACKET_TYPE_INJECT_AUDIO) {
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
NUM_BYTES_RFC4122_UUID));
NodeList* nodeList = NodeList::getInstance();
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
if (matchingNode) {
nodeList->updateNodeWithData(matchingNode, senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
if (!matchingNode->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
// this means they've heard from us and can reply, let's assume public is active
matchingNode->activatePublicSocket();
}
}
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
}
}
void AudioMixer::run() {
NodeList* nodeList = NodeList::getInstance();
// change the logging target name while this is running
Logging::setTargetName(AUDIO_MIXER_LOGGING_TARGET_NAME);
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;
nodeList->startSilentNodeRemovalThread();
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
unsigned char packetData[MAX_PACKET_SIZE] = {};
QTimer* silentNodeTimer = new QTimer(this);
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
sockaddr* nodeAddress = new sockaddr;
// make sure our node socket is non-blocking
nodeList->getNodeSocket()->setBlocking(false);
QTimer* pingNodesTimer = new QTimer(this);
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
int nextFrame = 0;
timeval startTime;
gettimeofday(&startTime, NULL);
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_MIXED_AUDIO);
unsigned char clientPacket[BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
populateTypeAndVersion(clientPacket, PACKET_TYPE_MIXED_AUDIO);
gettimeofday(&startTime, NULL);
timeval lastDomainServerCheckIn = {};
timeval beginSendTime, endSendTime;
float sumFrameTimePercentages = 0.0f;
int numStatCollections = 0;
// if we'll be sending stats, call the Logstash::socket() method to make it load the logstash IP outside the loop
if (Logging::shouldSendStats()) {
Logging::socket();
}
while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
while (!_isFinished) {
QCoreApplication::processEvents();
if (_isFinished) {
break;
}
if (Logging::shouldSendStats()) {
gettimeofday(&beginSendTime, NULL);
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn();
if (Logging::shouldSendStats() && numStatCollections > 0) {
// if we should be sending stats to Logstash send the appropriate average now
const char MIXER_LOGSTASH_METRIC_NAME[] = "audio-mixer-frame-time-usage";
float averageFrameTimePercentage = sumFrameTimePercentages / numStatCollections;
Logging::stashValue(STAT_TYPE_TIMER, MIXER_LOGSTASH_METRIC_NAME, averageFrameTimePercentage);
sumFrameTimePercentages = 0.0f;
numStatCollections = 0;
}
}
// get the NodeList to ping any inactive nodes, for hole punching
nodeList->possiblyPingInactiveNodes();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()) {
((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES);
@ -301,7 +305,9 @@ void AudioMixer::run() {
prepareMixForListeningNode(&(*node));
memcpy(clientPacket + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples));
nodeList->getNodeSocket()->send(node->getActiveSocket(), clientPacket, sizeof(clientPacket));
nodeList->getNodeSocket().writeDatagram((char*) clientPacket, sizeof(clientPacket),
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
}
}
@ -312,47 +318,6 @@ 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
|| packetData[0] == PACKET_TYPE_INJECT_AUDIO) {
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
NUM_BYTES_RFC4122_UUID));
Node* matchingNode = nodeList->nodeWithUUID(nodeUUID);
if (matchingNode) {
nodeList->updateNodeWithData(matchingNode, nodeAddress, packetData, receivedBytes);
if (!matchingNode->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
// this means they've heard from us and can reply, let's assume public is active
matchingNode->activatePublicSocket();
}
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(nodeAddress, packetData, receivedBytes);
}
}
if (Logging::shouldSendStats()) {
// send a packet to our logstash instance
// calculate the percentage value for time elapsed for this send (of the max allowable time)
gettimeofday(&endSendTime, NULL);
float percentageOfMaxElapsed = ((float) (usecTimestamp(&endSendTime) - usecTimestamp(&beginSendTime))
/ BUFFER_SEND_INTERVAL_USECS) * 100.0f;
sumFrameTimePercentages += percentageOfMaxElapsed;
numStatCollections++;
}
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
@ -360,5 +325,6 @@ void AudioMixer::run() {
} else {
qDebug("Took too much time, not sleeping!\n");
}
}
}

View file

@ -9,19 +9,23 @@
#ifndef __hifi__AudioMixer__
#define __hifi__AudioMixer__
#include <Assignment.h>
#include <AudioRingBuffer.h>
#include <ThreadedAssignment.h>
class PositionalAudioRingBuffer;
class AvatarAudioRingBuffer;
/// Handles assignments of type AudioMixer - mixing streams of audio and re-distributing to various clients.
class AudioMixer : public Assignment {
class AudioMixer : public ThreadedAssignment {
Q_OBJECT
public:
AudioMixer(const unsigned char* dataBuffer, int numBytes);
/// runs the audio mixer
public slots:
/// threaded run of assignment
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
private:
/// adds one buffer to the mix for a listening node
void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd,

View file

@ -10,6 +10,9 @@
// The avatar mixer receives head, hand and positional data from all connected
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <Logging.h>
#include <NodeList.h>
#include <PacketHeaders.h>
@ -22,6 +25,14 @@
const char AVATAR_MIXER_LOGGING_NAME[] = "avatar-mixer";
const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
{
}
unsigned char* addNodeToBroadcastPacket(unsigned char *currentPosition, Node *nodeToAdd) {
QByteArray rfcUUID = nodeToAdd->getUUID().toRfc4122();
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
@ -46,47 +57,107 @@ void attachAvatarDataToNode(Node* newNode) {
// 3) if we need to rate limit the amount of data we send, we can use a distance weighted "semi-random" function to
// determine which avatars are included in the packet stream
// 4) we should optimize the avatar data format to be more compact (100 bytes is pretty wasteful).
void broadcastAvatarData(NodeList* nodeList, const QUuid& receiverUUID, sockaddr* receiverAddress) {
static unsigned char broadcastPacketBuffer[MAX_PACKET_SIZE];
void broadcastAvatarData() {
static unsigned char broadcastPacket[MAX_PACKET_SIZE];
static unsigned char avatarDataBuffer[MAX_PACKET_SIZE];
unsigned char* broadcastPacket = (unsigned char*)&broadcastPacketBuffer[0];
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_BULK_AVATAR_DATA);
unsigned char* currentBufferPosition = broadcastPacket + numHeaderBytes;
int packetLength = currentBufferPosition - broadcastPacket;
int packetsSent = 0;
// send back a packet with other active node data to this node
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData() && node->getUUID() != receiverUUID) {
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0], &*node);
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT && node->getActiveSocket()) {
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
} else {
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
// reset the packet
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
// copy the avatar that didn't fit into the next packet
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
// reset packet pointers for this node
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
// this is an AGENT we have received head data from
// send back a packet with other active node data to this node
for (NodeList::iterator otherNode = nodeList->begin(); otherNode != nodeList->end(); otherNode++) {
if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()) {
unsigned char* avatarDataEndpoint = addNodeToBroadcastPacket((unsigned char*)&avatarDataBuffer[0],
&*otherNode);
int avatarDataLength = avatarDataEndpoint - (unsigned char*)&avatarDataBuffer;
if (avatarDataLength + packetLength <= MAX_PACKET_SIZE) {
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
} else {
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
currentBufferPosition - broadcastPacket,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
// reset the packet
currentBufferPosition = broadcastPacket + numHeaderBytes;
packetLength = currentBufferPosition - broadcastPacket;
// copy the avatar that didn't fit into the next packet
memcpy(currentBufferPosition, &avatarDataBuffer[0], avatarDataLength);
packetLength += avatarDataLength;
currentBufferPosition += avatarDataLength;
}
}
}
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) broadcastPacket, currentBufferPosition - broadcastPacket,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
}
}
packetsSent++;
//printf("packetsSent=%d packetLength=%d\n", packetsSent, packetLength);
nodeList->getNodeSocket()->send(receiverAddress, broadcastPacket, currentBufferPosition - broadcastPacket);
}
AvatarMixer::AvatarMixer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes) {
void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
NodeList* nodeList = NodeList::getInstance();
switch (dataByteArray[0]) {
case PACKET_TYPE_HEAD_DATA: {
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
NUM_BYTES_RFC4122_UUID));
// add or update the node in our list
Node* avatarNode = nodeList->nodeWithUUID(nodeUUID);
if (avatarNode) {
// parse positional data from an node
nodeList->updateNodeWithData(avatarNode, senderSockAddr,
(unsigned char*) dataByteArray.data(), dataByteArray.size());
} else {
break;
}
}
case PACKET_TYPE_KILL_NODE:
case PACKET_TYPE_AVATAR_URLS:
case PACKET_TYPE_AVATAR_FACE_VIDEO: {
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()),
NUM_BYTES_RFC4122_UUID));
// let everyone else know about the update
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
nodeList->getNodeSocket().writeDatagram(dataByteArray,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
}
}
// let node kills fall through to default behavior
}
default:
// hand this off to the NodeList
nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size());
break;
}
}
@ -101,71 +172,39 @@ void AvatarMixer::run() {
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
nodeList->startSilentNodeRemovalThread();
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
sockaddr nodeAddress = {};
ssize_t receivedBytes = 0;
QTimer* pingNodesTimer = new QTimer(this);
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
unsigned char packetData[MAX_PACKET_SIZE];
QTimer* silentNodeTimer = new QTimer(this);
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
QUuid nodeUUID;
Node* avatarNode = NULL;
int nextFrame = 0;
timeval startTime;
timeval lastDomainServerCheckIn = {};
gettimeofday(&startTime, NULL);
while (true) {
while (!_isFinished) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
QCoreApplication::processEvents();
if (_isFinished) {
break;
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn();
}
broadcastAvatarData();
nodeList->possiblyPingInactiveNodes();
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * AVATAR_DATA_SEND_INTERVAL_USECS) - usecTimestampNow();
if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) &&
packetVersionMatch(packetData)) {
switch (packetData[0]) {
case PACKET_TYPE_HEAD_DATA:
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
NUM_BYTES_RFC4122_UUID));
// add or update the node in our list
avatarNode = nodeList->nodeWithUUID(nodeUUID);
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;
case PACKET_TYPE_KILL_NODE:
case PACKET_TYPE_AVATAR_URLS:
case PACKET_TYPE_AVATAR_FACE_VIDEO:
nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData),
NUM_BYTES_RFC4122_UUID));
// let everyone else know about the update
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getActiveSocket() && node->getUUID() != nodeUUID) {
nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes);
}
}
// let node kills fall through to default behavior
default:
// hand this off to the NodeList
nodeList->processNodeData(&nodeAddress, packetData, receivedBytes);
break;
}
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
qDebug() << "Took too much time, not sleeping!\n";
}
}
nodeList->stopSilentNodeRemovalThread();
}

View file

@ -9,15 +9,18 @@
#ifndef __hifi__AvatarMixer__
#define __hifi__AvatarMixer__
#include <Assignment.h>
#include <ThreadedAssignment.h>
/// Handles assignments of type AvatarMixer - distribution of avatar data to various clients
class AvatarMixer : public Assignment {
class AvatarMixer : public ThreadedAssignment {
public:
AvatarMixer(const unsigned char* dataBuffer, int numBytes);
public slots:
/// runs the avatar mixer
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
};
#endif /* defined(__hifi__AvatarMixer__) */

View file

@ -20,16 +20,14 @@
#include "Agent.h"
#include "Assignment.h"
#include "AssignmentFactory.h"
#include "AssignmentClient.h"
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000;
const char PARENT_TARGET_NAME[] = "assignment-client-monitor";
const char CHILD_TARGET_NAME[] = "assignment-client";
pid_t* childForks = NULL;
sockaddr_in customAssignmentSocket = {};
HifiSockAddr customAssignmentSocket;
int numForks = 0;
Assignment::Type overiddenAssignmentType = Assignment::AllTypes;
const char* assignmentPool = NULL;
@ -37,84 +35,9 @@ const char* assignmentPool = NULL;
int argc = 0;
char** argv = NULL;
void childClient() {
QCoreApplication application(::argc, ::argv);
// set the logging target to the the CHILD_TARGET_NAME
Logging::setTargetName(CHILD_TARGET_NAME);
// create a NodeList as an unassigned client
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED);
// set the custom assignment socket if we have it
if (customAssignmentSocket.sin_addr.s_addr != 0) {
nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket);
}
// change the timeout on the nodelist socket to be as often as we want to re-request
nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS);
timeval lastRequest = {};
unsigned char packetData[MAX_PACKET_SIZE];
ssize_t receivedBytes = 0;
sockaddr_in senderSocket = {};
// create a request assignment, accept assignments defined by the overidden type
Assignment requestAssignment(Assignment::RequestCommand, ::overiddenAssignmentType, ::assignmentPool);
qDebug() << "Waiting for assignment -" << requestAssignment << "\n";
while (true) {
if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) {
gettimeofday(&lastRequest, NULL);
// if we're here we have no assignment, so send a request
nodeList->sendAssignment(requestAssignment);
}
if (nodeList->getNodeSocket()->receive((sockaddr*) &senderSocket, packetData, &receivedBytes) &&
(packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT)
&& packetVersionMatch(packetData)) {
// construct the deployed assignment from the packet data
Assignment* deployedAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes);
qDebug() << "Received an assignment -" << *deployedAssignment << "\n";
// switch our nodelist domain IP and port to whoever sent us the assignment
if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
nodeList->setDomainIP(QHostAddress((sockaddr*) &senderSocket));
nodeList->setDomainPort(ntohs(senderSocket.sin_port));
nodeList->setOwnerUUID(deployedAssignment->getUUID());
qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str());
// run the deployed assignment
deployedAssignment->run();
} else {
qDebug("Received a bad destination socket for assignment.\n");
}
qDebug("Assignment finished or never started - waiting for new assignment\n");
// delete the deployedAssignment
delete deployedAssignment;
// reset our NodeList by switching back to unassigned and clearing the list
nodeList->setOwnerType(NODE_TYPE_UNASSIGNED);
nodeList->reset();
// set the NodeList socket back to blocking
nodeList->getNodeSocket()->setBlocking(true);
// reset the logging target to the the CHILD_TARGET_NAME
Logging::setTargetName(CHILD_TARGET_NAME);
}
}
int childClient() {
AssignmentClient client(::argc, ::argv, ::overiddenAssignmentType, customAssignmentSocket, ::assignmentPool);
return client.exec();
}
void sigchldHandler(int sig) {
@ -203,10 +126,10 @@ int main(int argc, char* argv[]) {
// set the custom hostname or default if it wasn't passed
if (!customAssignmentServerHostname) {
customAssignmentServerHostname = LOCAL_ASSIGNMENT_SERVER_HOSTNAME;
customAssignmentServerHostname = DEFAULT_ASSIGNMENT_SERVER_HOSTNAME;
}
::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServerHostname, assignmentServerPort);
::customAssignmentSocket = HifiSockAddr(customAssignmentServerHostname, assignmentServerPort);
}
const char ASSIGNMENT_TYPE_OVVERIDE_OPTION[] = "-t";
@ -247,7 +170,7 @@ int main(int argc, char* argv[]) {
}
if (processID == 0 || ::numForks == 0) {
childClient();
return childClient();
} else {
parentMonitor();
}

View file

@ -22,7 +22,7 @@ void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float sca
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
// queue the packet
queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail);
queueVoxelAdd(PACKET_TYPE_VOXEL_SET, addVoxelDetail);
}
void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale,
@ -31,7 +31,7 @@ void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
// queue the destructive add
queueVoxelAdd(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, addVoxelDetail);
queueVoxelAdd(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, addVoxelDetail);
}
void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float scale) {
@ -39,6 +39,6 @@ void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float
// setup a VoxelDetail struct with data
VoxelDetail deleteVoxelDetail = {x, y, z, scale, 0, 0, 0};
_voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_ERASE_VOXEL, 1, &deleteVoxelDetail);
_voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_VOXEL_ERASE, 1, &deleteVoxelDetail);
}

View file

@ -1,82 +0,0 @@
# - Try to find jack-2.6
# Once done this will define
#
# JACK_FOUND - system has jack
# JACK_INCLUDE_DIRS - the jack include directory
# JACK_LIBRARIES - Link these to use jack
# JACK_DEFINITIONS - Compiler switches required for using jack
#
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
# Modified for other libraries by Lasse Kärkkäinen <tronic>
#
# Redistribution and use is allowed according to the terms of the New
# BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
if (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
# in cache already
set(JACK_FOUND TRUE)
else (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
include(UsePkgConfig)
pkgconfig(jack _JACK_INCLUDEDIR _JACK_LIBDIR _JACK_LDFLAGS _JACK_CFLAGS)
else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(_JACK jack)
endif (PKG_CONFIG_FOUND)
endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
find_path(JACK_INCLUDE_DIR
NAMES
jack/jack.h
PATHS
${_JACK_INCLUDEDIR}
/usr/include
/usr/local/include
/opt/local/include
/sw/include
)
find_library(JACK_LIBRARY
NAMES
jack
PATHS
${_JACK_LIBDIR}
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
)
if (JACK_LIBRARY AND JACK_INCLUDE_DIR)
set(JACK_FOUND TRUE)
set(JACK_INCLUDE_DIRS
${JACK_INCLUDE_DIR}
)
set(JACK_LIBRARIES
${JACK_LIBRARIES}
${JACK_LIBRARY}
)
endif (JACK_LIBRARY AND JACK_INCLUDE_DIR)
if (JACK_FOUND)
if (NOT JACK_FIND_QUIETLY)
message(STATUS "Found jack: ${JACK_LIBRARY}")
endif (NOT JACK_FIND_QUIETLY)
else (JACK_FOUND)
if (JACK_FIND_REQUIRED)
message(FATAL_ERROR "Could not find JACK")
endif (JACK_FIND_REQUIRED)
endif (JACK_FOUND)
# show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view
mark_as_advanced(JACK_INCLUDE_DIRS JACK_LIBRARIES)
endif (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)

View file

@ -1,61 +0,0 @@
# You may redistribute this program and/or modify it under the terms of
# the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(NOT LIBRT_FOUND)
find_path(LIBRT_INCLUDE_DIR
NAMES
time.h
PATHS
${LIBRTDIR}/include/
)
find_file(
LIBRT_LIBRARIES librt.a
PATHS
${LIBRTDIR}/lib/
/usr/local/lib64/
/usr/local/lib/
/usr/lib/i386-linux-gnu/
/usr/lib/x86_64-linux-gnu/
/usr/lib64/
/usr/lib/
)
set (LIBRT_DYNAMIC "Using static library.")
if (NOT LIBRT_LIBRARIES)
find_library(
LIBRT_LIBRARIES rt
PATHS
${LIBRTDIR}/lib/
/usr/local/lib64/
/usr/local/lib/
/usr/lib/i386-linux-gnu/
/usr/lib/x86_64-linux-gnu/
/usr/lib64/
/usr/lib/
)
set (LIBRT_DYNAMIC "Using dynamic library.")
endif (NOT LIBRT_LIBRARIES)
if (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES)
set (LIBRT_FOUND TRUE)
endif (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES)
if (LIBRT_FOUND)
message(STATUS "Found librt: ${LIBRT_INCLUDE_DIR}, ${LIBRT_LIBRARIES} ${LIBRT_DYNAMIC}")
else (LIBRT_FOUND)
if (Librt_FIND_REQUIRED)
message (FATAL_ERROR "Could not find librt, try to setup LIBRT_PREFIX accordingly")
endif (Librt_FIND_REQUIRED)
endif (LIBRT_FOUND)
endif (NOT LIBRT_FOUND)

View file

@ -1,44 +0,0 @@
# Find the static PortAudio library
#
# You must provide a PORTAUDIO_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# PORTAUDIO_FOUND - system found PortAudio
# PORTAUDIO_INCLUDE_DIRS - the PortAudio include directory
# PORTAUDIO_LIBRARIES - Link this to use PortAudio
#
# Created on 5/14/2013 by Stephen Birarda
# Copyright (c) 2013 High Fidelity
#
if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS)
# in cache already
set(PORTAUDIO_FOUND TRUE)
else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS)
find_path(PORTAUDIO_INCLUDE_DIRS portaudio.h ${PORTAUDIO_ROOT_DIR}/include)
if (APPLE)
find_library(PORTAUDIO_LIBRARIES libportaudio.a ${PORTAUDIO_ROOT_DIR}/lib/MacOS/)
elseif (UNIX)
find_library(PORTAUDIO_LIBRARIES libportaudio.a ${PORTAUDIO_ROOT_DIR}/lib/UNIX/)
endif ()
if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES)
set(PORTAUDIO_FOUND TRUE)
endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES)
if (PORTAUDIO_FOUND)
if (NOT PortAudio_FIND_QUIETLY)
message(STATUS "Found PortAudio: ${PORTAUDIO_LIBRARIES}")
endif (NOT PortAudio_FIND_QUIETLY)
else (PORTAUDIO_FOUND)
if (PortAudio_FIND_REQUIRED)
message(FATAL_ERROR "Could not find PortAudio")
endif (PortAudio_FIND_REQUIRED)
endif (PORTAUDIO_FOUND)
# show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view
mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES)
endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS)

View file

@ -12,6 +12,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QStringList>
#include <QtCore/QTimer>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -19,25 +20,235 @@
#include "DomainServer.h"
const int RESTART_HOLD_TIME_MSECS = 5 * 1000;
DomainServer* DomainServer::domainServerInstance = NULL;
void DomainServer::signalHandler(int signal) {
domainServerInstance->cleanup();
exit(1);
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_assignmentQueueMutex(),
_assignmentQueue(),
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
_staticAssignmentFileData(NULL),
_voxelServerConfig(NULL),
_hasCompletedRestartHold(false)
{
DomainServer::setDomainServerInstance(this);
const char CUSTOM_PORT_OPTION[] = "-p";
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
// setup the mongoose web server
struct mg_callbacks callbacks = {};
QString documentRootString = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
char documentRoot[documentRootString.size() + 1];
strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
// list of options. Last element must be NULL.
const char* options[] = {"listening_ports", "8080",
"document_root", documentRoot, NULL};
callbacks.begin_request = civetwebRequestHandler;
callbacks.upload = civetwebUploadHandler;
// Start the web server.
mg_start(&callbacks, NULL, options);
nodeList->addHook(this);
if (!_staticAssignmentFile.exists() || _voxelServerConfig) {
if (_voxelServerConfig) {
// we have a new VS config, clear the existing file to start fresh
_staticAssignmentFile.remove();
}
prepopulateStaticAssignmentFile();
}
_staticAssignmentFile.open(QIODevice::ReadWrite);
_staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size());
_staticAssignments = (Assignment*) _staticAssignmentFileData;
QTimer* silentNodeTimer = new QTimer(this);
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
// fire a single shot timer to add static assignments back into the queue after a restart
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
}
void DomainServer::exit(int retCode) {
cleanup();
}
void DomainServer::readAvailableDatagrams() {
NodeList* nodeList = NodeList::getInstance();
HifiSockAddr senderSockAddr, nodePublicAddress, nodeLocalAddress;
static unsigned char packetData[MAX_PACKET_SIZE];
static unsigned char broadcastPacket[MAX_PACKET_SIZE];
static unsigned char* currentBufferPos;
static unsigned char* startPointer;
int receivedBytes = 0;
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))
&& packetVersionMatch((unsigned char*) packetData)) {
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
// this is an RFD or domain list request packet, and there is a version match
int numBytesSenderHeader = numBytesForPacketHeader((unsigned char*) packetData);
NODE_TYPE nodeType = *(packetData + numBytesSenderHeader);
int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE);
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID));
packetIndex += NUM_BYTES_RFC4122_UUID;
int numBytesPrivateSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodePublicAddress);
packetIndex += numBytesPrivateSocket;
if (nodePublicAddress.getAddress().isNull()) {
// this node wants to use us its STUN server
// so set the node public address to whatever we perceive the public address to be
// if the sender is on our box then leave its public address to 0 so that
// other users attempt to reach it on the same address they have for the domain-server
if (senderSockAddr.getAddress().isLoopback()) {
nodePublicAddress.setAddress(QHostAddress());
} else {
nodePublicAddress.setAddress(senderSockAddr.getAddress());
}
}
int numBytesPublicSocket = HifiSockAddr::unpackSockAddr(packetData + packetIndex, nodeLocalAddress);
packetIndex += numBytesPublicSocket;
const char STATICALLY_ASSIGNED_NODES[3] = {
NODE_TYPE_AUDIO_MIXER,
NODE_TYPE_AVATAR_MIXER,
NODE_TYPE_VOXEL_SERVER
};
Assignment* matchingStaticAssignment = NULL;
if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|| checkInWithUUIDMatchesExistingNode(nodePublicAddress,
nodeLocalAddress,
nodeUUID)))
{
Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID,
nodeType,
nodePublicAddress,
nodeLocalAddress);
if (matchingStaticAssignment) {
// this was a newly added node with a matching static assignment
if (_hasCompletedRestartHold) {
// remove the matching assignment from the assignment queue so we don't take the next check in
removeAssignmentFromQueue(matchingStaticAssignment);
}
// set the linked data for this node to a copy of the matching assignment
// so we can re-queue it should the node die
Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment);
checkInNode->setLinkedData(nodeCopyOfMatchingAssignment);
}
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + packetIndex + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getUUID() != nodeUUID &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
}
}
}
// update last receive to now
uint64_t timeNow = usecTimestampNow();
checkInNode->setLastHeardMicrostamp(timeNow);
// send the constructed list back to this node
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes,
senderSockAddr.getAddress(), senderSockAddr.getPort());
}
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
qDebug("Received a request for assignment.\n");
if (_assignmentQueue.size() > 0) {
// construct the requested assignment from the packet data
Assignment requestAssignment(packetData, receivedBytes);
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
if (assignmentToDeploy) {
// give this assignment out, either the type matches or the requestor said they will take any
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes);
nodeList->getNodeSocket().writeDatagram((char*) broadcastPacket, numHeaderBytes + numAssignmentBytes,
senderSockAddr.getAddress(), senderSockAddr.getPort());
if (assignmentToDeploy->getNumberOfInstances() == 0) {
// there are no more instances of this script to send out, delete it
delete assignmentToDeploy;
}
}
}
}
}
}
}
void DomainServer::setDomainServerInstance(DomainServer* domainServer) {
domainServerInstance = domainServer;
}
QJsonObject jsonForSocket(sockaddr* socket) {
QJsonObject jsonForSocket(const HifiSockAddr& socket) {
QJsonObject socketJSON;
if (socket->sa_family == AF_INET) {
sockaddr_in* socketIPv4 = (sockaddr_in*) socket;
socketJSON["ip"] = QString(inet_ntoa(socketIPv4->sin_addr));
socketJSON["port"] = (int) ntohs(socketIPv4->sin_port);
}
socketJSON["ip"] = socket.getAddress().toString();
socketJSON["port"] = ntohs(socket.getPort());
return socketJSON;
}
@ -288,59 +499,13 @@ unsigned char* DomainServer::addNodeToBroadcastPacket(unsigned char* currentPosi
memcpy(currentPosition, rfcUUID.constData(), rfcUUID.size());
currentPosition += rfcUUID.size();
currentPosition += packSocket(currentPosition, nodeToAdd->getPublicSocket());
currentPosition += packSocket(currentPosition, nodeToAdd->getLocalSocket());
currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getPublicSocket());
currentPosition += HifiSockAddr::packSockAddr(currentPosition, nodeToAdd->getLocalSocket());
// return the new unsigned char * for broadcast packet
return currentPosition;
}
DomainServer::DomainServer(int argc, char* argv[]) :
_assignmentQueueMutex(),
_assignmentQueue(),
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
_staticAssignmentFileData(NULL),
_voxelServerConfig(NULL),
_hasCompletedRestartHold(false)
{
DomainServer::setDomainServerInstance(this);
const char CUSTOM_PORT_OPTION[] = "-p";
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort);
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = DomainServer::signalHandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
// setup the mongoose web server
struct mg_callbacks callbacks = {};
QString documentRootString = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
char documentRoot[documentRootString.size() + 1];
strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
// list of options. Last element must be NULL.
const char* options[] = {"listening_ports", "8080",
"document_root", documentRoot, NULL};
callbacks.begin_request = civetwebRequestHandler;
callbacks.upload = civetwebUploadHandler;
// Start the web server.
mg_start(&callbacks, NULL, options);
}
void DomainServer::prepopulateStaticAssignmentFile() {
int numFreshStaticAssignments = 0;
@ -393,6 +558,48 @@ void DomainServer::prepopulateStaticAssignmentFile() {
Assignment rootVoxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType);
freshStaticAssignments[numFreshStaticAssignments++] = rootVoxelServerAssignment;
}
// Handle Domain/Particle Server configuration command line arguments
if (_particleServerConfig) {
qDebug("Reading Particle Server Configuration.\n");
qDebug() << "config: " << _particleServerConfig << "\n";
QString multiConfig((const char*) _particleServerConfig);
QStringList multiConfigList = multiConfig.split(";");
// read each config to a payload for a VS assignment
for (int i = 0; i < multiConfigList.size(); i++) {
QString config = multiConfigList.at(i);
qDebug("config[%d]=%s\n", i, config.toLocal8Bit().constData());
// Now, parse the config to check for a pool
const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool";
QString assignmentPool;
int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION);
if (poolIndex >= 0) {
int spaceBeforePoolIndex = config.indexOf(' ', poolIndex);
int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex);
assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex);
qDebug() << "The pool for this particle-assignment is" << assignmentPool << "\n";
}
Assignment particleServerAssignment(Assignment::CreateCommand,
Assignment::ParticleServerType,
(assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
int payloadLength = config.length() + sizeof(char);
particleServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength);
freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment;
}
} else {
Assignment rootParticleServerAssignment(Assignment::CreateCommand, Assignment::ParticleServerType);
freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment;
}
qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.\n";
@ -506,15 +713,15 @@ void DomainServer::removeAssignmentFromQueue(Assignment* removableAssignment) {
_assignmentQueueMutex.unlock();
}
bool DomainServer::checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket,
sockaddr* nodeLocalSocket,
bool DomainServer::checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
const HifiSockAddr& nodeLocalSocket,
const QUuid& checkInUUID) {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()
&& socketMatch(node->getPublicSocket(), nodePublicSocket)
&& socketMatch(node->getLocalSocket(), nodeLocalSocket)
&& nodePublicSocket == node->getPublicSocket()
&& nodeLocalSocket == node->getLocalSocket()
&& node->getUUID() == checkInUUID) {
// this is a matching existing node if the public socket, local socket, and UUID match
return true;
@ -524,271 +731,51 @@ bool DomainServer::checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket
return false;
}
void DomainServer::possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime) {
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
_hasCompletedRestartHold = true;
// if the domain-server has just restarted,
// check if there are static assignments in the file that we need to
// throw into the assignment queue
const uint64_t RESTART_HOLD_TIME_USECS = 5 * 1000 * 1000;
if (!_hasCompletedRestartHold && usecTimestampNow() - usecTimestamp(startTime) > RESTART_HOLD_TIME_USECS) {
_hasCompletedRestartHold = true;
// pull anything in the static assignment file that isn't spoken for and add to the assignment queue
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
if (_staticAssignments[i].getUUID().isNull()) {
// reached the end of static assignments, bail
break;
}
// pull anything in the static assignment file that isn't spoken for and add to the assignment queue
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
if (_staticAssignments[i].getUUID().isNull()) {
// reached the end of static assignments, bail
break;
}
bool foundMatchingAssignment = false;
NodeList* nodeList = NodeList::getInstance();
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()) {
Assignment* linkedAssignment = (Assignment*) node->getLinkedData();
if (linkedAssignment->getUUID() == _staticAssignments[i].getUUID()) {
foundMatchingAssignment = true;
break;
}
bool foundMatchingAssignment = false;
NodeList* nodeList = NodeList::getInstance();
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()) {
Assignment* linkedAssignment = (Assignment*) node->getLinkedData();
if (linkedAssignment->getUUID() == _staticAssignments[i].getUUID()) {
foundMatchingAssignment = true;
break;
}
}
}
if (!foundMatchingAssignment) {
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
_staticAssignments[i].resetUUID();
if (!foundMatchingAssignment) {
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
_staticAssignments[i].resetUUID();
qDebug() << "Adding static assignment to queue -" << _staticAssignments[i] << "\n";
_assignmentQueueMutex.lock();
_assignmentQueue.push_back(&_staticAssignments[i]);
_assignmentQueueMutex.unlock();
}
qDebug() << "Adding static assignment to queue -" << _staticAssignments[i] << "\n";
_assignmentQueueMutex.lock();
_assignmentQueue.push_back(&_staticAssignments[i]);
_assignmentQueueMutex.unlock();
}
}
}
void DomainServer::cleanup() {
qDebug() << "cleanup called!\n";
_staticAssignmentFile.unmap(_staticAssignmentFileData);
_staticAssignmentFile.close();
}
int DomainServer::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->addHook(this);
ssize_t receivedBytes = 0;
char nodeType = '\0';
unsigned char broadcastPacket[MAX_PACKET_SIZE];
unsigned char packetData[MAX_PACKET_SIZE];
unsigned char* currentBufferPos;
unsigned char* startPointer;
sockaddr_in senderAddress, nodePublicAddress, nodeLocalAddress;
nodePublicAddress.sin_family = AF_INET;
nodeLocalAddress.sin_family = AF_INET;
nodeList->startSilentNodeRemovalThread();
if (!_staticAssignmentFile.exists() || _voxelServerConfig) {
if (_voxelServerConfig) {
// we have a new VS config, clear the existing file to start fresh
_staticAssignmentFile.remove();
}
prepopulateStaticAssignmentFile();
}
_staticAssignmentFile.open(QIODevice::ReadWrite);
_staticAssignmentFileData = _staticAssignmentFile.map(0, _staticAssignmentFile.size());
_staticAssignments = (Assignment*) _staticAssignmentFileData;
timeval startTime;
gettimeofday(&startTime, NULL);
while (true) {
while (nodeList->getNodeSocket()->receive((sockaddr *)&senderAddress, packetData, &receivedBytes) &&
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY || packetData[0] == PACKET_TYPE_DOMAIN_LIST_REQUEST) {
// this is an RFD or domain list request packet, and there is a version match
int numBytesSenderHeader = numBytesForPacketHeader(packetData);
nodeType = *(packetData + numBytesSenderHeader);
int packetIndex = numBytesSenderHeader + sizeof(NODE_TYPE);
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray(((char*) packetData + packetIndex), NUM_BYTES_RFC4122_UUID));
packetIndex += NUM_BYTES_RFC4122_UUID;
int numBytesPrivateSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodePublicAddress);
packetIndex += numBytesPrivateSocket;
if (nodePublicAddress.sin_addr.s_addr == 0) {
// this node wants to use us its STUN server
// so set the node public address to whatever we perceive the public address to be
nodePublicAddress = senderAddress;
// if the sender is on our box then leave its public address to 0 so that
// other users attempt to reach it on the same address they have for the domain-server
if (senderAddress.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
nodePublicAddress.sin_addr.s_addr = 0;
}
}
int numBytesPublicSocket = unpackSocket(packetData + packetIndex, (sockaddr*) &nodeLocalAddress);
packetIndex += numBytesPublicSocket;
const char STATICALLY_ASSIGNED_NODES[3] = {
NODE_TYPE_AUDIO_MIXER,
NODE_TYPE_AVATAR_MIXER,
NODE_TYPE_VOXEL_SERVER
};
Assignment* matchingStaticAssignment = NULL;
if (memchr(STATICALLY_ASSIGNED_NODES, nodeType, sizeof(STATICALLY_ASSIGNED_NODES)) == NULL
|| ((matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|| checkInWithUUIDMatchesExistingNode((sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress,
nodeUUID)))
{
Node* checkInNode = nodeList->addOrUpdateNode(nodeUUID,
nodeType,
(sockaddr*) &nodePublicAddress,
(sockaddr*) &nodeLocalAddress);
if (matchingStaticAssignment) {
// this was a newly added node with a matching static assignment
if (_hasCompletedRestartHold) {
// remove the matching assignment from the assignment queue so we don't take the next check in
removeAssignmentFromQueue(matchingStaticAssignment);
}
// set the linked data for this node to a copy of the matching assignment
// so we can re-queue it should the node die
Assignment* nodeCopyOfMatchingAssignment = new Assignment(*matchingStaticAssignment);
checkInNode->setLinkedData(nodeCopyOfMatchingAssignment);
}
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_DOMAIN);
currentBufferPos = broadcastPacket + numHeaderBytes;
startPointer = currentBufferPos;
unsigned char* nodeTypesOfInterest = packetData + packetIndex + sizeof(unsigned char);
int numInterestTypes = *(nodeTypesOfInterest - 1);
if (numInterestTypes > 0) {
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (!node->matches((sockaddr*) &nodePublicAddress, (sockaddr*) &nodeLocalAddress, nodeType) &&
memchr(nodeTypesOfInterest, node->getType(), numInterestTypes)) {
// don't send avatar nodes to other avatars, that will come from avatar mixer
if (nodeType != NODE_TYPE_AGENT || node->getType() != NODE_TYPE_AGENT) {
currentBufferPos = addNodeToBroadcastPacket(currentBufferPos, &(*node));
}
}
}
}
// update last receive to now
uint64_t timeNow = usecTimestampNow();
checkInNode->setLastHeardMicrostamp(timeNow);
// send the constructed list back to this node
nodeList->getNodeSocket()->send((sockaddr*)&senderAddress,
broadcastPacket,
(currentBufferPos - startPointer) + numHeaderBytes);
}
} else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) {
qDebug("Received a request for assignment.\n");
if (!_hasCompletedRestartHold) {
possiblyAddStaticAssignmentsBackToQueueAfterRestart(&startTime);
}
if (_assignmentQueue.size() > 0) {
// construct the requested assignment from the packet data
Assignment requestAssignment(packetData, receivedBytes);
Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment);
if (assignmentToDeploy) {
// give this assignment out, either the type matches or the requestor said they will take any
int numHeaderBytes = populateTypeAndVersion(broadcastPacket, PACKET_TYPE_CREATE_ASSIGNMENT);
int numAssignmentBytes = assignmentToDeploy->packToBuffer(broadcastPacket + numHeaderBytes);
nodeList->getNodeSocket()->send((sockaddr*) &senderAddress,
broadcastPacket,
numHeaderBytes + numAssignmentBytes);
if (assignmentToDeploy->getNumberOfInstances() == 0) {
// there are no more instances of this script to send out, delete it
delete assignmentToDeploy;
}
}
}
} else if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) {
// this is a create assignment likely recieved from a server needed more clients to help with load
// unpack it
Assignment* createAssignment = new Assignment(packetData, receivedBytes);
qDebug() << "Received a create assignment -" << *createAssignment << "\n";
// make sure we have a matching node with the UUID packed with the assignment
// if the node has sent no types of interest, assume they want nothing but their own ID back
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (node->getLinkedData()
&& socketMatch((sockaddr*) &senderAddress, node->getPublicSocket())
&& ((Assignment*) node->getLinkedData())->getUUID() == createAssignment->getUUID()) {
// give the create assignment a new UUID
createAssignment->resetUUID();
// add the assignment at the back of the queue
_assignmentQueueMutex.lock();
_assignmentQueue.push_back(createAssignment);
_assignmentQueueMutex.unlock();
// find the first available spot in the static assignments and put this assignment there
for (int i = 0; i < MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS; i++) {
if (_staticAssignments[i].getUUID().isNull()) {
_staticAssignments[i] = *createAssignment;
// we've stuck the assignment in, break out
break;
}
}
// we found the matching node that asked for create assignment, break out
break;
}
}
}
}
if (!_hasCompletedRestartHold) {
possiblyAddStaticAssignmentsBackToQueueAfterRestart(&startTime);
}
}
this->cleanup();
return 0;
}

View file

@ -22,20 +22,20 @@
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
class DomainServer : public NodeListHook {
class DomainServer : public QCoreApplication, public NodeListHook {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
int run();
static void signalHandler(int signal);
void exit(int retCode = 0);
static void setDomainServerInstance(DomainServer* domainServer);
/// Called by NodeList to inform us that a node has been added.
void nodeAdded(Node* node);
/// Called by NodeList to inform us that a node has been killed.
void nodeKilled(Node* node);
private:
private:
static int civetwebRequestHandler(struct mg_connection *connection);
static void civetwebUploadHandler(struct mg_connection *connection, const char *path);
@ -45,8 +45,9 @@ private:
Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType);
Assignment* deployableAssignmentForRequest(Assignment& requestAssignment);
void removeAssignmentFromQueue(Assignment* removableAssignment);
bool checkInWithUUIDMatchesExistingNode(sockaddr* nodePublicSocket, sockaddr* nodeLocalSocket, const QUuid& checkInUUI);
void possiblyAddStaticAssignmentsBackToQueueAfterRestart(timeval* startTime);
bool checkInWithUUIDMatchesExistingNode(const HifiSockAddr& nodePublicSocket,
const HifiSockAddr& nodeLocalSocket,
const QUuid& checkInUUI);
void addReleasedAssignmentBackToQueue(Assignment* releasedAssignment);
void cleanup();
@ -62,8 +63,12 @@ private:
Assignment* _staticAssignments;
const char* _voxelServerConfig;
const char* _particleServerConfig;
bool _hasCompletedRestartHold;
private slots:
void readAvailableDatagrams();
void addStaticAssignmentsBackToQueueAfterRestart();
};
#endif /* defined(__hifi__DomainServer__) */

View file

@ -24,9 +24,8 @@ int main(int argc, char* argv[]) {
qInstallMessageHandler(Logging::verboseMessageHandler);
QCoreApplication application(argc, argv);
DomainServer domainServer(argc, argv);
return domainServer.run();
return domainServer.exec();
}

View file

@ -1,81 +0,0 @@
//
// Read Gyro and accelerometer data, send over serialUSB to computer for processing.
//
// Written by Philip, 2012, for High Fidelity, Inc.
//
// PIN WIRING: Connect input sensors to the channels in following manner
//
// AIN 10: Yaw Gyro (shaking your head 'no')
// AIN 16: Pitch Gyro (nodding your head 'yes')
// AIN 17: Roll Gyro (looking quizzical, tilting your head)
// AIN 18: Lateral acceleration (moving from side-to-side in front of your monitor)
// AIN 19: Up/Down acceleration (sitting up/ducking in front of your monitor)
// AIN 20: Forward/Back acceleration (Toward or away from your monitor)
#define NUM_CHANNELS 6
#define MSECS_PER_SAMPLE 10
#define LED_PIN 12
int inputPins[NUM_CHANNELS] = {10,16,17,18,19,20};
int LED = 0;
unsigned int samplesSent = 0;
unsigned int time;
int measured[NUM_CHANNELS];
float accumulate[NUM_CHANNELS];
int sampleCount = 0;
void setup()
{
int i;
for (i = 0; i < NUM_CHANNELS; i++) {
pinMode(inputPins[i], INPUT_ANALOG);
measured[i] = analogRead(inputPins[i]);
accumulate[i] = measured[i];
}
pinMode(BOARD_LED_PIN, OUTPUT);
pinMode(LED_PIN,OUTPUT);
time = millis();
}
void loop()
{
int i;
sampleCount++;
for (i = 0; i < NUM_CHANNELS; i++) {
accumulate[i] += analogRead(inputPins[i]);
}
if ((millis() - time) >= MSECS_PER_SAMPLE) {
samplesSent++;
time = millis();
for (i = 0; i < NUM_CHANNELS; i++) {
measured[i] = accumulate[i] / sampleCount;
SerialUSB.print(measured[i]);
SerialUSB.print(" ");
accumulate[i] = 0;
}
if ((samplesSent % 100 == 0) && (samplesSent % 150 == 0)) {
LED = !LED;
digitalWrite(LED_PIN, LED);
digitalWrite(BOARD_LED_PIN, LED);
}
SerialUSB.print(sampleCount);
SerialUSB.print(" ");
if (LED)
SerialUSB.print("1");
else
SerialUSB.print("0");
SerialUSB.println("");
sampleCount = 0;
}
}

View file

@ -13,7 +13,6 @@ set(LIBOVR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibOVR)
set(LIBVPX_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/LibVPX)
set(LEAP_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Leap)
set(MOTIONDRIVER_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/MotionDriver)
set(PORTAUDIO_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/PortAudio)
set(OPENCV_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/OpenCV)
set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense)
set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl)
@ -83,6 +82,7 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
# link required hifi libraries
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
@ -158,8 +158,6 @@ target_link_libraries(
if (APPLE)
# link in required OS X frameworks and include the right GL headers
find_library(AppKit AppKit)
find_library(AudioToolbox AudioToolbox)
find_library(AudioUnit AudioUnit)
find_library(CoreAudio CoreAudio)
find_library(CoreServices CoreServices)
find_library(Carbon Carbon)
@ -175,9 +173,7 @@ if (APPLE)
target_link_libraries(
${TARGET_NAME}
${AppKit}
${AudioToolbox}
${AudioUnit}
${CoreAudio}
${CoreAudio}
${CoreServices}
${Carbon}
${Foundation}
@ -210,24 +206,14 @@ if (WIN32)
wsock32.lib
)
else (WIN32)
# link the PortAudio library
find_package(PortAudio REQUIRED)
include_directories(${PORTAUDIO_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${PORTAUDIO_LIBRARIES})
# link required libraries on UNIX
if (UNIX AND NOT APPLE)
find_package(Threads REQUIRED)
find_package(Librt REQUIRED)
find_package(ALSA)
find_package(Jack)
target_link_libraries(${TARGET_NAME}
${CMAKE_THREAD_LIBS_INIT}
${LIBRT_LIBRARIES}
${JACK_LIBRARIES}
${ALSA_LIBRARIES}
${GLUT_LIBRARY}
target_link_libraries(
${TARGET_NAME}
${CMAKE_THREAD_LIBS_INIT}
${GLUT_LIBRARY}
)
endif (UNIX AND NOT APPLE)
endif (WIN32)

File diff suppressed because it is too large Load diff

View file

@ -10,15 +10,7 @@
#include <stdlib.h>
#include <cmath>
#ifdef _WIN32
#include "Syssocket.h"
#include "Systime.h"
#else
#include <sys/time.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#endif
#include <glm/gtx/component_wise.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
@ -47,8 +39,6 @@
#include <QDesktopServices>
#include <NodeTypes.h>
#include <AudioInjectionManager.h>
#include <AudioInjector.h>
#include <Logging.h>
#include <OctalCode.h>
#include <PacketHeaders.h>
@ -126,6 +116,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_mouseVoxelScale(1.0f / 1024.0f),
_mouseVoxelScaleInitialized(false),
_justEditedVoxel(false),
_isHighlightVoxel(false),
_nudgeStarted(false),
_lookingAlongX(false),
_lookingAwayFromOrigin(true),
@ -133,11 +124,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_lookatIndicatorScale(1.0f),
_perfStatsOn(false),
_chatEntryOn(false),
#ifndef _WIN32
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
#endif
_stopNetworkReceiveThread(false),
_voxelProcessor(),
_voxelHideShowThread(&_voxels),
_voxelEditSender(this),
_packetCount(0),
_packetsPerSecond(0),
@ -162,17 +152,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
NodeList::createInstance(NODE_TYPE_AGENT, listenPort);
// put the audio processing on a separate thread
QThread* audioThread = new QThread(this);
_audio.moveToThread(audioThread);
connect(audioThread, SIGNAL(started()), &_audio, SLOT(start()));
audioThread->start();
NodeList::getInstance()->addHook(&_voxels);
NodeList::getInstance()->addHook(this);
NodeList::getInstance()->addDomainListener(this);
NodeList::getInstance()->addDomainListener(&_voxels);
// network receive thread and voxel parsing thread are both controlled by the --nonblocking command line
_enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking");
if (!_enableNetworkThread) {
NodeList::getInstance()->getNodeSocket()->setBlocking(false);
}
// setup QSettings
#ifdef Q_OS_MAC
@ -238,6 +232,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
}
Application::~Application() {
// ask the audio thread to quit and wait until it is done
_audio.thread()->quit();
_audio.thread()->wait();
storeSizeAndPosition();
NodeList::getInstance()->removeHook(&_voxels);
NodeList::getInstance()->removeHook(this);
@ -245,9 +243,7 @@ Application::~Application() {
_sharedVoxelSystem.changeTree(new VoxelTree);
_audio.shutdown();
VoxelNode::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
delete Menu::getInstance();
delete _settings;
@ -307,16 +303,6 @@ void Application::initializeGL() {
init();
qDebug( "Init() complete.\n" );
// Check to see if the user passed in a command line option for randomizing colors
bool wantColorRandomizer = !arguments().contains("--NoColorRandomizer");
// Check to see if the user passed in a command line option for loading a local
// Voxel File. If so, load it now.
if (!_voxelsFilename.isEmpty()) {
_voxels.loadVoxelsFile(_voxelsFilename.constData(), wantColorRandomizer);
qDebug("Local Voxel File loaded.\n");
}
// create thread for receipt of data via UDP
if (_enableNetworkThread) {
pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL);
@ -326,6 +312,7 @@ void Application::initializeGL() {
// create thread for parsing of voxel data independent of the main network and rendering threads
_voxelProcessor.initialize(_enableProcessVoxelsThread);
_voxelEditSender.initialize(_enableProcessVoxelsThread);
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
if (_enableProcessVoxelsThread) {
qDebug("Voxel parsing thread created.\n");
}
@ -622,12 +609,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
return;
}
//this is for switching between modes for the leap rave glove test
if (Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand)
|| Menu::getInstance()->isOptionChecked(MenuOption::TestRaveGlove)) {
_myAvatar.getHand().setRaveGloveEffectsMode((QKeyEvent*)event);
}
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
switch (event->key()) {
@ -648,9 +629,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Period:
Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key());
break;
case Qt::Key_Semicolon:
_audio.ping();
break;
case Qt::Key_Apostrophe:
_audioScope.inputPaused = !_audioScope.inputPaused;
break;
@ -1395,6 +1373,7 @@ void Application::terminate() {
}
_voxelProcessor.terminate();
_voxelHideShowThread.terminate();
_voxelEditSender.terminate();
}
@ -1455,8 +1434,9 @@ void Application::checkBandwidthMeterClick() {
// ... to be called upon button release
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) &&
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH &&
_bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))
<= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH
&& _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
// The bandwidth meter is visible, the click didn't get dragged too far and
// we actually hit the bandwidth meter
@ -1487,7 +1467,7 @@ void Application::removeVoxel(glm::vec3 position,
voxel.y = position.y / TREE_SCALE;
voxel.z = position.z / TREE_SCALE;
voxel.s = scale / TREE_SCALE;
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxel);
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
@ -1507,7 +1487,7 @@ void Application::makeVoxel(glm::vec3 position,
voxel.red = red;
voxel.green = green;
voxel.blue = blue;
PACKET_TYPE message = isDestructive ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL;
PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET;
_voxelEditSender.sendVoxelEditMessage(message, voxel);
// create the voxel locally so it appears immediately
@ -1561,10 +1541,11 @@ struct SendVoxelsOperationArgs {
const unsigned char* newBaseOctCode;
};
bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
SendVoxelsOperationArgs* args = (SendVoxelsOperationArgs*)extraData;
if (node->isColored()) {
const unsigned char* nodeOctalCode = node->getOctalCode();
if (voxel->isColored()) {
const unsigned char* nodeOctalCode = voxel->getOctalCode();
unsigned char* codeColorBuffer = NULL;
int codeLength = 0;
int bytesInCode = 0;
@ -1585,10 +1566,10 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) {
}
// copy the colors over
codeColorBuffer[bytesInCode + RED_INDEX] = node->getColor()[RED_INDEX];
codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX];
codeColorBuffer[bytesInCode + BLUE_INDEX] = node->getColor()[BLUE_INDEX];
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE,
codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX];
codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX];
codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX];
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE,
codeColorBuffer, codeAndColorLength);
delete[] codeColorBuffer;
@ -1604,7 +1585,7 @@ void Application::exportVoxels() {
tr("Sparse Voxel Octree Files (*.svo)"));
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data();
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (selectedNode) {
VoxelTree exportTree;
_voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true);
@ -1635,12 +1616,12 @@ void Application::copyVoxels() {
// switch to and clear the clipboard first...
_sharedVoxelSystem.killLocalVoxels();
if (_sharedVoxelSystem.getTree() != &_clipboard) {
_clipboard.eraseAllVoxels();
_clipboard.eraseAllOctreeElements();
_sharedVoxelSystem.changeTree(&_clipboard);
}
// then copy onto it if there is something to copy
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (selectedNode) {
_voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true);
}
@ -1663,7 +1644,7 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati
void Application::pasteVoxels() {
unsigned char* calculatedOctCode = NULL;
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
@ -1702,7 +1683,7 @@ void Application::findAxisAlignment() {
}
void Application::nudgeVoxels() {
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (!Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && selectedNode) {
Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode);
}
@ -1716,7 +1697,7 @@ void Application::nudgeVoxels() {
// calculate nudgeVec
glm::vec3 nudgeVec(_nudgeGuidePosition.x - _nudgeVoxel.x, _nudgeGuidePosition.y - _nudgeVoxel.y, _nudgeGuidePosition.z - _nudgeVoxel.z);
VoxelNode* nodeToNudge = _voxels.getVoxelAt(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s);
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s);
if (nodeToNudge) {
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
@ -1749,7 +1730,7 @@ void Application::init() {
_sharedVoxelSystemViewFrustum.calculate();
_sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum);
VoxelNode::removeUpdateHook(&_sharedVoxelSystem);
VoxelTreeElement::removeUpdateHook(&_sharedVoxelSystem);
_sharedVoxelSystem.init();
VoxelTree* tmpTree = _sharedVoxelSystem.getTree();
@ -1765,8 +1746,6 @@ void Application::init() {
_voxelShader.init();
_pointShader.init();
_handControl.setScreenDimensions(_glWidget->width(), _glWidget->height());
_headMouseX = _mouseX = _glWidget->width() / 2;
_headMouseY = _mouseY = _glWidget->height() / 2;
QCursor::setPos(_headMouseX, _headMouseY);
@ -1991,6 +1970,20 @@ void Application::renderFollowIndicator() {
}
}
void Application::renderHighlightVoxel(VoxelDetail voxel) {
glDisable(GL_LIGHTING);
glPushMatrix();
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
const float EDGE_EXPAND = 1.02f;
glColor3ub(voxel.red + 128, voxel.green + 128, voxel.blue + 128);
glTranslatef(voxel.x + voxel.s * 0.5f,
voxel.y + voxel.s * 0.5f,
voxel.z + voxel.s * 0.5f);
glLineWidth(2.0f);
glutWireCube(voxel.s * EDGE_EXPAND);
glPopMatrix();
}
void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
@ -1998,7 +1991,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
node->lock();
if (node->getLinkedData() != NULL) {
if (node->getLinkedData()) {
Avatar *avatar = (Avatar *)node->getLinkedData();
if (!avatar->isInitialized()) {
avatar->init();
@ -2103,7 +2096,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
// If we have clicked on a voxel, update it's color
if (_isHoverVoxelSounding) {
VoxelNode* hoveredNode = _voxels.getVoxelAt(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
VoxelTreeElement* hoveredNode = _voxels.getVoxelAt(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
if (hoveredNode) {
float bright = _audio.getCollisionSoundMagnitude();
nodeColor clickColor = { 255 * bright + _hoverVoxelOriginalColor[0] * (1.f - bright),
@ -2236,11 +2229,6 @@ void Application::updateHandAndTouch(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()");
// walking triggers the handControl to stop
if (_myAvatar.getMode() == AVATAR_MODE_WALKING) {
_handControl.stop();
}
// Update from Touch
if (_isTouchPressed) {
float TOUCH_YAW_SCALE = -0.25f;
@ -2258,7 +2246,6 @@ void Application::updateLeap(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::updateLeap()");
LeapManager::enableFakeFingers(Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand));
_myAvatar.getHand().setRaveGloveActive(Menu::getInstance()->isOptionChecked(MenuOption::TestRaveGlove));
LeapManager::nextFrame();
}
@ -2290,6 +2277,7 @@ void Application::updateThreads(float deltaTime) {
// parse voxel packets
if (!_enableProcessVoxelsThread) {
_voxelProcessor.threadRoutine();
_voxelHideShowThread.threadRoutine();
_voxelEditSender.threadRoutine();
}
}
@ -2422,11 +2410,8 @@ void Application::updateAudio(float deltaTime) {
PerformanceWarning warn(showWarnings, "Application::updateAudio()");
// Update audio stats for procedural sounds
#ifndef _WIN32
_audio.setLastAcceleration(_myAvatar.getThrust());
_audio.setLastVelocity(_myAvatar.getVelocity());
_audio.eventuallyAnalyzePing();
#endif
}
void Application::updateCursor(float deltaTime) {
@ -2566,10 +2551,8 @@ void Application::updateAvatar(float deltaTime) {
}
// Get audio loudness data from audio input device
#ifndef _WIN32
_myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness());
#endif
_myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness());
NodeList* nodeList = NodeList::getInstance();
// send head/hand data to the avatar mixer and voxel server
@ -2629,7 +2612,7 @@ void Application::queryVoxels() {
_voxelQuery.setCameraNearClip(_viewFrustum.getNearClip());
_voxelQuery.setCameraFarClip(_viewFrustum.getFarClip());
_voxelQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition());
_voxelQuery.setVoxelSizeScale(Menu::getInstance()->getVoxelSizeScale());
_voxelQuery.setOctreeSizeScale(Menu::getInstance()->getVoxelSizeScale());
_voxelQuery.setBoundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust());
unsigned char voxelQueryPacket[MAX_PACKET_SIZE];
@ -2699,7 +2682,6 @@ void Application::queryVoxels() {
qDebug("perServerPPS: %d perUnknownServer: %d\n", perServerPPS, perUnknownServer);
}
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
if (node->getActiveSocket() != NULL && node->getType() == NODE_TYPE_VOXEL_SERVER) {
@ -2743,7 +2725,7 @@ void Application::queryVoxels() {
}
if (inView) {
_voxelQuery.setMaxVoxelPacketsPerSecond(perServerPPS);
_voxelQuery.setMaxOctreePacketsPerSecond(perServerPPS);
} else if (unknownView) {
if (wantExtraDebugging) {
qDebug() << "no known jurisdiction for node " << *node << ", give it budget of "
@ -2767,9 +2749,9 @@ void Application::queryVoxels() {
qDebug() << "Using regular camera position for node " << *node << "\n";
}
}
_voxelQuery.setMaxVoxelPacketsPerSecond(perUnknownServer);
_voxelQuery.setMaxOctreePacketsPerSecond(perUnknownServer);
} else {
_voxelQuery.setMaxVoxelPacketsPerSecond(0);
_voxelQuery.setMaxOctreePacketsPerSecond(0);
}
// set up the packet for sending...
unsigned char* endOfVoxelQueryPacket = voxelQueryPacket;
@ -2785,7 +2767,8 @@ void Application::queryVoxels() {
int packetLength = endOfVoxelQueryPacket - voxelQueryPacket;
nodeSocket->send(node->getActiveSocket(), voxelQueryPacket, packetLength);
nodeList->getNodeSocket().writeDatagram((char*) voxelQueryPacket, packetLength,
node->getActiveSocket()->getAddress(), node->getActiveSocket()->getPort());
// Feed number of bytes to corresponding channel of the bandwidth meter
_bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(packetLength);
@ -3021,10 +3004,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
}
// restore default, white specular
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
// Render the highlighted voxel
if (_isHighlightVoxel) {
renderHighlightVoxel(_highlightVoxel);
}
// indicate what we'll be adding/removing in mouse mode, if anything
if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
@ -3048,7 +3035,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
} else {
renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) {
// use a contrasting color so that we can see what we're doing
glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128);
@ -3092,12 +3079,8 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
}
_myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS);
renderAvatars(whichCamera.getMode() == CAMERA_MODE_MIRROR, selfAvatarOnly);
_myAvatar.renderScreenTint(SCREEN_TINT_AFTER_AVATARS);
if (!selfAvatarOnly) {
// Render the world box
if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
@ -3196,14 +3179,12 @@ void Application::displayOverlay() {
}
}
#ifndef _WIN32
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
_audio.render(_glWidget->width(), _glWidget->height());
if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) {
_audioScope.render(45, _glWidget->height() - 200);
}
}
#endif
//noiseTest(_glWidget->width(), _glWidget->height());
@ -3452,7 +3433,7 @@ void Application::displayStats() {
}
// calculate server node totals
totalNodes += stats.getTotalVoxels();
totalNodes += stats.getTotalElements();
totalInternal += stats.getTotalInternal();
totalLeaves += stats.getTotalLeaves();
}
@ -3479,9 +3460,9 @@ void Application::displayStats() {
statsVerticalOffset += PELS_PER_LINE;
drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str());
unsigned long localTotal = VoxelNode::getNodeCount();
unsigned long localInternal = VoxelNode::getInternalNodeCount();
unsigned long localLeaves = VoxelNode::getLeafNodeCount();
unsigned long localTotal = VoxelTreeElement::getNodeCount();
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
unsigned long localLeaves = VoxelTreeElement::getLeafNodeCount();
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
QString localInternalString = locale.toString((uint)localInternal);
QString localLeavesString = locale.toString((uint)localLeaves);
@ -3498,7 +3479,7 @@ void Application::displayStats() {
// Local Voxel Memory Usage
voxelStats.str("");
voxelStats <<
"Voxels Memory Nodes: " << VoxelNode::getTotalMemoryUsage() / 1000000.f << "MB "
"Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
"Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
"VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
if (_voxels.hasVoxelMemoryUsageGPU()) {
@ -3680,7 +3661,7 @@ void Application::renderCoverageMap() {
void Application::renderCoverageMapsRecursively(CoverageMap* map) {
for (int i = 0; i < map->getPolygonCount(); i++) {
VoxelProjectedPolygon* polygon = map->getPolygon(i);
OctreeProjectedPolygon* polygon = map->getPolygon(i);
if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) {
glColor3f(.5,0,0); // dark red
@ -3933,62 +3914,6 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) {
}
}
void Application::injectVoxelAddedSoundEffect() {
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025);
if (voxelInjector) {
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
//voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw());
voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value
/* for (int i = 0; i
< 22050; i++) {
if (i % 4 == 0) {
voxelInjector->addSample(4000);
} else if (i % 4 == 1) {
voxelInjector->addSample(0);
} else if (i % 4 == 2) {
voxelInjector->addSample(-4000);
} else {
voxelInjector->addSample(0);
}
*/
const float BIG_VOXEL_MIN_SIZE = .01f;
for (int i = 0; i < 11025; i++) {
/*
A440 square wave
if (sin(i * 2 * PIE / 50)>=0) {
voxelInjector->addSample(4000);
} else {
voxelInjector->addSample(-4000);
}
*/
if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) {
voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200))));
} else {
voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808
}
}
//voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80
//voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808
//voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave
//voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation
//FM library
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep
//voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep
//voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse
AudioInjectionManager::threadInjector(voxelInjector);
}
}
bool Application::maybeEditVoxelUnderCursor() {
if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)
|| Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode)) {
@ -4001,9 +3926,6 @@ bool Application::maybeEditVoxelUnderCursor() {
_mouseVoxel.green,
_mouseVoxel.blue,
Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel));
// inject a sound effect
injectVoxelAddedSoundEffect();
// remember the position for drag detection
_justEditedVoxel = true;
@ -4032,33 +3954,18 @@ bool Application::maybeEditVoxelUnderCursor() {
void Application::deleteVoxelUnderCursor() {
if (_mouseVoxel.s != 0) {
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, _mouseVoxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000);
if (voxelInjector) {
voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z));
//voxelInjector->setBearing(0); //straight down the z axis
voxelInjector->setVolume (255); //255 is max, and also default value
for (int i = 0; i < 5000; i++) {
voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse
//voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter
}
AudioInjectionManager::threadInjector(voxelInjector);
}
}
// remember the position for drag detection
_justEditedVoxel = true;
}
void Application::eyedropperVoxelUnderCursor() {
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (selectedNode && selectedNode->isColored()) {
QColor selectedColor(selectedNode->getColor()[RED_INDEX],
selectedNode->getColor()[GREEN_INDEX],
@ -4094,14 +4001,18 @@ void Application::resetSensors() {
_webcam.reset();
_faceshift.reset();
LeapManager::reset();
OculusManager::reset();
if (OculusManager::isConnected()) {
OculusManager::reset();
}
QCursor::setPos(_headMouseX, _headMouseY);
_myAvatar.reset();
_myTransmitter.resetLevels();
_myAvatar.setVelocity(glm::vec3(0,0,0));
_myAvatar.setThrust(glm::vec3(0,0,0));
_audio.reset();
QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection);
}
static void setShortcutsEnabled(QWidget* widget, bool enabled) {
@ -4206,10 +4117,10 @@ void Application::nodeKilled(Node* node) {
}
void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
sockaddr senderAddress, bool wasStatsPacket) {
const HifiSockAddr& senderSockAddr, bool wasStatsPacket) {
// Attempt to identify the sender from it's address.
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
if (voxelServer) {
QUuid nodeUUID = voxelServer->getUUID();
@ -4217,16 +4128,16 @@ void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t m
_voxelSceneStatsLock.lockForWrite();
if (_voxelServerSceneStats.find(nodeUUID) != _voxelServerSceneStats.end()) {
VoxelSceneStats& stats = _voxelServerSceneStats[nodeUUID];
stats.trackIncomingVoxelPacket(messageData, messageLength, wasStatsPacket);
stats.trackIncomingOctreePacket(messageData, messageLength, wasStatsPacket);
}
_voxelSceneStatsLock.unlock();
}
}
int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress) {
int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr) {
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
// parse the incoming stats datas stick it in a temporary object for now, while we
// determine which server it belongs to
@ -4279,12 +4190,16 @@ void* Application::networkReceive(void* args) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()");
sockaddr senderAddress;
HifiSockAddr senderSockAddr;
ssize_t bytesReceived;
Application* app = Application::getInstance();
while (!app->_stopNetworkReceiveThread) {
if (NodeList::getInstance()->getNodeSocket()->receive(&senderAddress, app->_incomingPacket, &bytesReceived)) {
if (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() &&
(bytesReceived = NodeList::getInstance()->getNodeSocket().readDatagram((char*) app->_incomingPacket,
MAX_PACKET_SIZE,
senderSockAddr.getAddressPointer(),
senderSockAddr.getPortPointer()))) {
app->_packetCount++;
app->_bytesCount += bytesReceived;
@ -4298,11 +4213,12 @@ void* Application::networkReceive(void* args) {
break;
case PACKET_TYPE_MIXED_AUDIO:
app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived);
QMetaObject::invokeMethod(&app->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray((char*) app->_incomingPacket, bytesReceived)));
break;
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_ERASE_VOXEL:
case PACKET_TYPE_VOXEL_STATS:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
@ -4323,11 +4239,11 @@ void* Application::networkReceive(void* args) {
}
// add this packet to our list of voxel packets and process them on the voxel processing
app->_voxelProcessor.queueReceivedPacket(senderAddress, app->_incomingPacket, bytesReceived);
app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived);
break;
}
case PACKET_TYPE_BULK_AVATAR_DATA:
NodeList::getInstance()->processBulkNodeData(&senderAddress,
NodeList::getInstance()->processBulkNodeData(senderSockAddr,
app->_incomingPacket,
bytesReceived);
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
@ -4345,7 +4261,7 @@ void* Application::networkReceive(void* args) {
DataServerClient::processMessageFromDataServer(app->_incomingPacket, bytesReceived);
break;
default:
NodeList::getInstance()->processNodeData(&senderAddress, app->_incomingPacket, bytesReceived);
NodeList::getInstance()->processNodeData(senderSockAddr, app->_incomingPacket, bytesReceived);
break;
}
}

View file

@ -41,13 +41,13 @@
#include "ViewFrustum.h"
#include "VoxelFade.h"
#include "VoxelEditPacketSender.h"
#include "VoxelHideShowThread.h"
#include "VoxelPacketProcessor.h"
#include "VoxelSystem.h"
#include "VoxelImporter.h"
#include "avatar/Avatar.h"
#include "avatar/MyAvatar.h"
#include "avatar/Profile.h"
#include "avatar/HandControl.h"
#include "devices/Faceshift.h"
#include "devices/SerialInterface.h"
#include "devices/SixenseManager.h"
@ -188,6 +188,10 @@ public:
glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
void pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination);
/// set a voxel which is to be rendered with a highlight
void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; }
void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; }
public slots:
void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data);
@ -231,12 +235,13 @@ private slots:
void shrinkMirrorView();
void resetSensors();
private:
void resetCamerasOnResizeGL(Camera& camera, int width, int height);
void updateProjectionMatrix();
void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true);
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
static bool sendVoxelsOperation(OctreeElement* node, void* extraData);
static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes);
static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes);
static void sendPingPackets();
@ -272,9 +277,11 @@ private:
Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection,
glm::vec3& eyePosition, QUuid &nodeUUID);
bool isLookingAtMyAvatar(Avatar* avatar);
void renderLookatIndicator(glm::vec3 pointOfInterest);
void renderFollowIndicator();
void renderHighlightVoxel(VoxelDetail voxel);
void updateAvatar(float deltaTime);
void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection);
void queryVoxels();
@ -293,7 +300,6 @@ private:
bool maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void eyedropperVoxelUnderCursor();
void injectVoxelAddedSoundEffect();
void setMenuShortcutsEnabled(bool enabled);
@ -371,8 +377,6 @@ private:
int _headMouseX, _headMouseY;
HandControl _handControl;
int _mouseX;
int _mouseY;
int _mouseDragStartedX;
@ -405,6 +409,9 @@ private:
glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit
bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel
VoxelDetail _highlightVoxel;
bool _isHighlightVoxel;
VoxelDetail _nudgeVoxel; // details of the voxel to be nudged
bool _nudgeStarted;
bool _lookingAlongX;
@ -440,8 +447,9 @@ private:
bool _stopNetworkReceiveThread;
bool _enableProcessVoxelsThread;
VoxelPacketProcessor _voxelProcessor;
VoxelEditPacketSender _voxelEditSender;
VoxelPacketProcessor _voxelProcessor;
VoxelHideShowThread _voxelHideShowThread;
VoxelEditPacketSender _voxelEditSender;
unsigned char _incomingPacket[MAX_PACKET_SIZE];
int _packetCount;
@ -462,9 +470,9 @@ private:
PieMenu _pieMenu;
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress);
void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
sockaddr senderAddress, bool wasStatsPacket);
int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderAddress);
void trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength,
const HifiSockAddr& senderSockAddr, bool wasStatsPacket);
NodeToJurisdictionMap _voxelServerJurisdictions;
NodeToVoxelSceneStats _voxelServerSceneStats;

File diff suppressed because it is too large Load diff

View file

@ -14,9 +14,7 @@
#include "InterfaceConfig.h"
#include <QObject>
#include <portaudio.h>
#include <QtCore/QObject>
#include <AudioRingBuffer.h>
#include <StdDev.h>
@ -32,19 +30,18 @@ static const int PACKET_LENGTH_BYTES_PER_CHANNEL = PACKET_LENGTH_BYTES / 2;
static const int PACKET_LENGTH_SAMPLES = PACKET_LENGTH_BYTES / sizeof(int16_t);
static const int PACKET_LENGTH_SAMPLES_PER_CHANNEL = PACKET_LENGTH_SAMPLES / 2;
class QAudioInput;
class QAudioOutput;
class QIODevice;
class Audio : public QObject {
Q_OBJECT
public:
// initializes audio I/O
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples);
// setup for audio I/O
Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0);
void shutdown();
void reset();
void render(int screenWidth, int screenHeight);
void addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBytes);
float getLastInputLoudness() const { return _lastInputLoudness; }
void setLastAcceleration(const glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; }
@ -56,24 +53,28 @@ public:
void lowPassFilter(int16_t* inputBuffer);
void startCollisionSound(float magnitude, float frequency, float noise, float duration, bool flashScreen);
void startDrumSound(float volume, float frequency, float duration, float decay);
float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }
bool getCollisionFlashesScreen() { return _collisionFlashesScreen; }
void ping();
void init(QGLWidget *parent = 0);
bool mousePressEvent(int x, int y);
// Call periodically to eventually perform round trip time analysis,
// in which case 'true' is returned - otherwise the return value is 'false'.
// The results of the analysis are written to the log.
bool eventuallyAnalyzePing();
public slots:
void start();
void addReceivedAudioToBuffer(const QByteArray& audioByteArray);
void handleAudioInput();
void reset();
private:
PaStream* _stream;
QAudioInput* _audioInput;
QIODevice* _inputDevice;
QAudioOutput* _audioOutput;
QIODevice* _outputDevice;
bool _isBufferSendCallback;
int16_t* _nextOutputSamples;
AudioRingBuffer _ringBuffer;
Oscilloscope* _scope;
StDev _stdev;
@ -82,33 +83,26 @@ private:
float _averagedLatency;
float _measuredJitter;
int16_t _jitterBufferSamples;
int _wasStarved;
int _numStarves;
float _lastInputLoudness;
glm::vec3 _lastVelocity;
glm::vec3 _lastAcceleration;
int _totalPacketsReceived;
timeval _firstPacketReceivedTime;
int _packetsReceivedThisPlayback;
// Ping analysis
int16_t* _echoSamplesLeft;
volatile bool _isSendingEchoPing;
volatile bool _pingAnalysisPending;
int _pingFramesToRecord;
// Flange effect
int _samplesLeftForFlange;
int _lastYawMeasuredMaximum;
float _flangeIntensity;
float _flangeRate;
float _flangeWeight;
float _collisionSoundMagnitude;
float _collisionSoundFrequency;
float _collisionSoundNoise;
float _collisionSoundDuration;
bool _collisionFlashesScreen;
// Drum sound generator
float _drumSoundVolume;
float _drumSoundFrequency;
float _drumSoundDuration;
float _drumSoundDecay;
int _drumSoundSample;
int _proceduralEffectSample;
float _heartbeatMagnitude;
int _numFramesDisplayStarve;
bool _muted;
bool _localEcho;
GLuint _micTextureId;
@ -117,28 +111,12 @@ private:
// Audio callback in class context.
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
// When requested, sends/receives a signal for round trip time determination.
// Called from 'performIO'.
inline void eventuallySendRecvPing(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
// Determines round trip time of the audio system. Called from 'eventuallyAnalyzePing'.
inline void analyzePing();
// Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
void addProceduralSounds(int16_t* inputBuffer, int16_t* outputLeft, int16_t* outputRight, int numSamples);
// Audio callback called by portaudio. Calls 'performIO'.
static int audioCallback(const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
void addProceduralSounds(int16_t* monoInput, int16_t* stereoUpsampledOutput, int numSamples);
void renderToolIcon(int screenHeight);
};
#endif /* defined(__interface__audio__) */
#endif /* defined(__interface__audio__) */

View file

@ -7,10 +7,10 @@
//
#include <QtCore/QUrl>
#include <QtNetwork/QUdpSocket>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <UDPSocket.h>
#include <UUID.h>
#include "Application.h"
@ -24,7 +24,12 @@ const char MULTI_KEY_VALUE_SEPARATOR = '|';
const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
const unsigned short DATA_SERVER_PORT = 3282;
const sockaddr_in DATA_SERVER_SOCKET = socketForHostnameAndHostOrderPort(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
const HifiSockAddr& DataServerClient::dataServerSockAddr() {
static HifiSockAddr dsSockAddr = HifiSockAddr(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
return dsSockAddr;
}
void DataServerClient::putValueForKey(const QString& key, const char* value) {
QString clientString = Application::getInstance()->getProfile()->getUserString();
@ -57,7 +62,9 @@ void DataServerClient::putValueForKey(const QString& key, const char* value) {
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(putPacket, numPacketBytes));
// send this put request to the data server
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, putPacket, numPacketBytes);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes,
dataServerSockAddr().getAddress(),
dataServerSockAddr().getPort());
}
}
@ -96,7 +103,9 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co
// _unmatchedPackets.insert(std::pair<unsigned char*, int>(getPacket, numPacketBytes));
// send the get to the data server
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET, getPacket, numPacketBytes);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) getPacket, numPacketBytes,
dataServerSockAddr().getAddress(),
dataServerSockAddr().getPort());
}
}
@ -236,8 +245,8 @@ void DataServerClient::resendUnmatchedPackets() {
mapIterator != _unmatchedPackets.end();
++mapIterator) {
// send the unmatched packet to the data server
NodeList::getInstance()->getNodeSocket()->send((sockaddr*) &DATA_SERVER_SOCKET,
mapIterator->first,
mapIterator->second);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) mapIterator->first, mapIterator->second,
dataServerSockAddr().getAddress(),
dataServerSockAddr().getPort());
}
}

View file

@ -17,6 +17,7 @@
class DataServerClient {
public:
static const HifiSockAddr& dataServerSockAddr();
static void putValueForKey(const QString& key, const char* value);
static void getClientValueForKey(const QString& key);

View file

@ -18,24 +18,13 @@
#include "renderer/ProgramObject.h"
#include "world.h"
uint qHash(const sockaddr& address) {
const sockaddr_in* inetAddress = reinterpret_cast<const sockaddr_in*>(&address);
if (inetAddress->sin_family != AF_INET) {
uint qHash(const HifiSockAddr& sockAddr) {
if (sockAddr.getAddress().isNull()) {
return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash
}
return inetAddress->sin_port + qHash(QByteArray::fromRawData(
reinterpret_cast<const char*>(&inetAddress->sin_addr), sizeof(in_addr)));
}
bool operator== (const sockaddr& addr1, const sockaddr& addr2) {
return socketMatch(&addr1, &addr2);
}
static sockaddr getZeroAddress() {
sockaddr addr;
memset(&addr, 0, sizeof(sockaddr));
addr.sa_family = AF_INET;
return addr;
quint32 address = sockAddr.getAddress().toIPv4Address();
return sockAddr.getPort() + qHash(QByteArray::fromRawData((char*) &address,
sizeof(address)));
}
Environment::Environment()
@ -60,14 +49,14 @@ void Environment::init() {
_skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations);
// start off with a default-constructed environment data
_data[getZeroAddress()][0];
_data[HifiSockAddr()][0];
_initialized = true;
}
void Environment::resetToDefault() {
_data.clear();
_data[getZeroAddress()][0];
_data[HifiSockAddr()][0];
}
void Environment::renderAtmospheres(Camera& camera) {
@ -159,7 +148,7 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
return found;
}
int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes) {
int Environment::parseData(const HifiSockAddr& senderAddress, unsigned char* sourceBuffer, int numBytes) {
// push past the packet header
unsigned char* start = sourceBuffer;
@ -175,14 +164,14 @@ int Environment::parseData(sockaddr *senderAddress, unsigned char* sourceBuffer,
int dataLength = newData.parseData(sourceBuffer, numBytes);
// update the mapping by address/ID
_data[*senderAddress][newData.getID()] = newData;
_data[senderAddress][newData.getID()] = newData;
sourceBuffer += dataLength;
numBytes -= dataLength;
}
// remove the default mapping, if any
_data.remove(getZeroAddress());
_data.remove(HifiSockAddr());
return sourceBuffer - start;
}

View file

@ -12,7 +12,7 @@
#include <QHash>
#include <QMutex>
#include <UDPSocket.h>
#include <HifiSockAddr.h>
#include "EnvironmentData.h"
#include "InterfaceConfig.h"
@ -34,7 +34,7 @@ public:
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
int parseData(sockaddr *senderAddress, unsigned char* sourceBuffer, int numBytes);
int parseData(const HifiSockAddr& senderSockAddr, unsigned char* sourceBuffer, int numBytes);
private:
@ -71,7 +71,7 @@ private:
typedef QHash<int, EnvironmentData> ServerData;
QHash<sockaddr, ServerData> _data;
QHash<HifiSockAddr, ServerData> _data;
QMutex _mutex;
};

View file

@ -67,7 +67,7 @@ Menu::Menu() :
_voxelStatsDialog(NULL),
_lodToolsDialog(NULL),
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
_voxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE),
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_boundaryLevelAdjust(0),
_maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS)
{
@ -363,12 +363,12 @@ Menu::Menu() :
appInstance->getWebcam()->getGrabber(),
SLOT(setDepthOnly(bool)));
QMenu* raveGloveOptionsMenu = developerMenu->addMenu("Rave Glove Options");
QMenu* raveGloveOptionsMenu = developerMenu->addMenu("Hand Options");
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::DisplayLeapHands, 0, true);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::LeapDrive, 0, false);
addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove);
QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options");
addCheckableActionToQMenuAndActionHash(trackingOptionsMenu,
@ -518,7 +518,7 @@ void Menu::loadSettings(QSettings* settings) {
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_VOXEL_SIZE_SCALE);
_voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE);
_boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0);
settings->beginGroup("View Frustum Offset Camera");

View file

@ -239,7 +239,6 @@ namespace MenuOption {
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString TestPing = "Test Ping";
const QString TestRaveGlove = "Test Rave Glove";
const QString TreeStats = "Calculate Tree Stats";
const QString TransmitterDrive = "Transmitter Drive";
const QString Quit = "Quit";

View file

@ -6,13 +6,16 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "Oscilloscope.h"
#include "InterfaceConfig.h"
#include <limits>
#include <cstring>
#include <algorithm>
#include <QtCore/QDebug>
#include "InterfaceConfig.h"
#include "Oscilloscope.h"
// Reimplemented 4/26/13 (tosh) - don't blame Philip for bugs
using namespace std;
@ -65,37 +68,50 @@ Oscilloscope::~Oscilloscope() {
delete[] _samples;
}
void Oscilloscope::addSamples(unsigned ch, short const* data, unsigned n) {
void Oscilloscope::addStereoSamples(const QByteArray& audioByteArray, bool isInput) {
if (! enabled || inputPaused) {
return;
}
// determine start/end offset of this channel's region
unsigned baseOffs = MAX_SAMPLES_PER_CHANNEL * ch;
unsigned endOffs = baseOffs + MAX_SAMPLES_PER_CHANNEL;
// fetch write position for this channel
unsigned writePos = _writePos[ch];
// determine write position after adding the samples
unsigned newWritePos = writePos + n;
unsigned n2 = 0;
if (newWritePos >= endOffs) {
// passed boundary of the circular buffer? -> we need to copy two blocks
n2 = newWritePos - endOffs;
newWritePos = baseOffs + n2;
n -= n2;
unsigned int numSamplesPerChannel = audioByteArray.size() / (sizeof(int16_t) * 2);
int16_t samples[numSamplesPerChannel];
const int16_t* stereoSamples = (int16_t*) audioByteArray.constData();
for (int channel = 0; channel < (isInput ? 1 : 2); channel++) {
// add samples for each of the channels
// enumerate the interleaved stereoSamples array and pull out the samples for this channel
for (int i = 0; i < audioByteArray.size() / sizeof(int16_t); i += 2) {
samples[i / 2] = stereoSamples[i + channel];
}
// determine start/end offset of this channel's region
unsigned baseOffs = MAX_SAMPLES_PER_CHANNEL * (channel + !isInput);
unsigned endOffs = baseOffs + MAX_SAMPLES_PER_CHANNEL;
// fetch write position for this channel
unsigned writePos = _writePos[channel + !isInput];
// determine write position after adding the samples
unsigned newWritePos = writePos + numSamplesPerChannel;
unsigned n2 = 0;
if (newWritePos >= endOffs) {
// passed boundary of the circular buffer? -> we need to copy two blocks
n2 = newWritePos - endOffs;
newWritePos = baseOffs + n2;
numSamplesPerChannel -= n2;
}
// copy data
memcpy(_samples + writePos, samples, numSamplesPerChannel * sizeof(int16_t));
if (n2 > 0) {
memcpy(_samples + baseOffs, samples + numSamplesPerChannel, n2 * sizeof(int16_t));
}
// set new write position for this channel
_writePos[channel + !isInput] = newWritePos;
}
// copy data
memcpy(_samples + writePos, data, n * sizeof(short));
if (n2 > 0) {
memcpy(_samples + baseOffs, data + n, n2 * sizeof(short));
}
// set new write position for this channel
_writePos[ch] = newWritePos;
}
void Oscilloscope::render(int x, int y) {

View file

@ -11,13 +11,14 @@
#include <cassert>
class Oscilloscope {
#include <QtCore/QObject>
class Oscilloscope : public QObject {
Q_OBJECT
public:
Oscilloscope(int width, int height, bool isEnabled);
~Oscilloscope();
void addSamples(unsigned ch, short const* data, unsigned n);
void render(int x, int y);
// Switches: On/Off, Stop Time
@ -57,7 +58,8 @@ public:
// Sets the number of input samples per output sample. Without filtering
// just uses every nTh sample.
void setDownsampleRatio(unsigned n) { assert(n > 0); _downsampleRatio = n; }
public slots:
void addStereoSamples(const QByteArray& audioByteArray, bool isInput);
private:
// don't copy/assign
Oscilloscope(Oscilloscope const&); // = delete;

View file

@ -10,6 +10,9 @@
#include <string.h>
#include <stdio.h>
#include <QtNetwork/QHostInfo>
#include <HifiSockAddr.h>
#include <NodeList.h>
#include "PairingHandler.h"
@ -27,14 +30,14 @@ PairingHandler* PairingHandler::getInstance() {
return instance;
}
void PairingHandler::sendPairRequest() {
// grab the node socket from the NodeList singleton
UDPSocket *nodeSocket = NodeList::getInstance()->getNodeSocket();
void PairingHandler::sendPairRequest() {
// prepare the pairing request packet
NodeList* nodeList = NodeList::getInstance();
// use the getLocalAddress helper to get this client's listening address
int localAddress = getLocalAddress();
quint32 localAddress = htonl(getHostOrderLocalAddress());
char pairPacket[24] = {};
sprintf(pairPacket, "Find %d.%d.%d.%d:%hu",
@ -42,19 +45,13 @@ void PairingHandler::sendPairRequest() {
(localAddress >> 8) & 0xFF,
(localAddress >> 16) & 0xFF,
(localAddress >> 24) & 0xFF,
NodeList::getInstance()->getSocketListenPort());
NodeList::getInstance()->getNodeSocket().localPort());
qDebug("Sending pair packet: %s\n", pairPacket);
sockaddr_in pairingServerSocket;
pairingServerSocket.sin_family = AF_INET;
// lookup the pairing server IP by the hostname
struct hostent* hostInfo = gethostbyname(PAIRING_SERVER_HOSTNAME);
memcpy(&pairingServerSocket.sin_addr, hostInfo->h_addr_list[0], hostInfo->h_length);
pairingServerSocket.sin_port = htons(PAIRING_SERVER_PORT);
HifiSockAddr pairingServerSocket(PAIRING_SERVER_HOSTNAME, PAIRING_SERVER_PORT);
// send the pair request to the pairing server
nodeSocket->send((sockaddr*) &pairingServerSocket, pairPacket, strlen(pairPacket));
nodeList->getNodeSocket().writeDatagram((char*) pairPacket, strlen(pairPacket),
pairingServerSocket.getAddress(), pairingServerSocket.getPort());
}

View file

@ -0,0 +1,46 @@
//
// VoxelHideShowThread.cpp
// interface
//
// Created by Brad Hefta-Gaub on 12/1/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree.
//
#include <QDebug>
#include <NodeList.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "Menu.h"
#include "VoxelHideShowThread.h"
VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
_theSystem(theSystem) {
}
bool VoxelHideShowThread::process() {
const uint64_t MSECS_TO_USECS = 1000;
const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS;
const uint64_t FRAME_RATE = 60;
const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps
uint64_t start = usecTimestampNow();
_theSystem->checkForCulling();
uint64_t end = usecTimestampNow();
uint64_t elapsed = end - start;
bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed);
}
if (isStillRunning()) {
if (elapsed < USECS_PER_FRAME) {
uint64_t sleepFor = USECS_PER_FRAME - elapsed;
usleep(sleepFor);
}
}
return isStillRunning(); // keep running till they terminate us
}

View file

@ -0,0 +1,31 @@
//
// VoxelHideShowThread.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 12/1/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
//
#ifndef __interface__VoxelHideShowThread__
#define __interface__VoxelHideShowThread__
#include <GenericThread.h>
#include "VoxelSystem.h"
/// Generalized threaded processor for handling received inbound packets.
class VoxelHideShowThread : public virtual GenericThread {
public:
VoxelHideShowThread(VoxelSystem* theSystem);
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
VoxelSystem* _theSystem;
};
#endif // __interface__VoxelHideShowThread__

View file

@ -51,7 +51,7 @@ VoxelImporter::~VoxelImporter() {
}
void VoxelImporter::reset() {
_voxelTree.eraseAllVoxels();
_voxelTree.eraseAllOctreeElements();
_importDialog.reset();
_filename = "";
@ -78,7 +78,7 @@ int VoxelImporter::exec() {
if (_importDialog.getImportIntoClipboard()) {
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->rootNode,
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
Application::getInstance()->getClipboard(),
true);
voxelSystem->changeTree(Application::getInstance()->getClipboard());
@ -184,5 +184,5 @@ void ImportTask::run() {
qDebug("[ERROR] Invalid file extension.\n");
}
voxelSystem->getTree()->reaverageVoxelColors(voxelSystem->getTree()->rootNode);
voxelSystem->getTree()->reaverageOctreeElements();
}

View file

@ -14,7 +14,7 @@
#include "Menu.h"
#include "VoxelPacketProcessor.h"
void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"VoxelPacketProcessor::processPacket()");
@ -34,12 +34,12 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
app->_wantToKillLocalVoxels = false;
}
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
// note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first
// then process any remaining bytes as if it was another packet
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
if (packetData[0] == PACKET_TYPE_OCTREE_STATS) {
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderAddress);
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderSockAddr);
wasStatsPacket = true;
if (messageLength > statsMessageLength) {
packetData += statsMessageLength;
@ -54,13 +54,12 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
} // fall through to piggyback message
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
app->trackIncomingVoxelPacket(packetData, messageLength, senderAddress, wasStatsPacket);
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress);
if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) {
Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) {
if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
app->_environment.parseData(&senderAddress, packetData, messageLength);
app->_environment.parseData(senderSockAddr, packetData, messageLength);
} else {
app->_voxels.setDataSourceUUID(voxelServer->getUUID());
app->_voxels.parseData(packetData, messageLength);

View file

@ -17,6 +17,6 @@
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class VoxelPacketProcessor : public ReceivedPacketProcessor {
protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
};
#endif // __shared__VoxelPacketProcessor__

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,6 @@
#include <glm/glm.hpp>
#include <SharedUtil.h>
#include <UDPSocket.h>
#include <CoverageMapV2.h>
#include <NodeData.h>
@ -37,9 +36,12 @@ struct VoxelShaderVBOData
};
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public VoxelNodeUpdateHook,
class VoxelSystem : public NodeData, public OctreeElementDeleteHook, public OctreeElementUpdateHook,
public NodeListHook, public DomainChangeListener {
Q_OBJECT
friend class VoxelHideShowThread;
public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM);
~VoxelSystem();
@ -64,8 +66,7 @@ public:
ViewFrustum* getLastCulledViewFrustum() { return &_lastCulledViewFrustum; }
void loadVoxelsFile(const char* fileName,bool wantColorRandomizer);
void writeToSVOFile(const char* filename, VoxelNode* node) const;
void writeToSVOFile(const char* filename, VoxelTreeElement* element) const;
bool readFromSVOFile(const char* filename);
bool readFromSquareARGB32Pixels(const char* filename);
bool readFromSchematicFile(const char* filename);
@ -76,12 +77,6 @@ public:
unsigned long getVoxelMemoryUsageVBO() const { return _memoryUsageVBO; }
bool hasVoxelMemoryUsageGPU() const { return _hasMemoryUsageGPU; }
unsigned long getVoxelMemoryUsageGPU();
long int getVoxelsCreated();
long int getVoxelsColored();
long int getVoxelsBytesRead();
float getVoxelsCreatedPerSecondAverage();
float getVoxelsColoredPerSecondAverage();
float getVoxelsBytesReadPerSecondAverage();
void killLocalVoxels();
void redrawInViewVoxels();
@ -98,29 +93,31 @@ public:
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
void deleteVoxelAt(float x, float y, float z, float s);
VoxelNode* getVoxelAt(float x, float y, float z, float s) const;
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
void createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
void createLine(glm::vec3 point1, glm::vec3 point2, float unitSize, rgbColor color, bool destructive = false);
void createSphere(float r,float xc, float yc, float zc, float s, bool solid,
creationMode mode, bool destructive = false, bool debug = false);
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelSystem* destinationTree, bool rebaseToRoot);
void copySubTreeIntoNewTree(VoxelNode* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destinationNode);
void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelSystem* destinationTree, bool rebaseToRoot);
void copySubTreeIntoNewTree(VoxelTreeElement* startNode, VoxelTree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelTreeElement* destinationNode);
void recurseTreeWithOperation(RecurseVoxelTreeOperation operation, void* extraData=NULL);
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData=NULL);
CoverageMapV2 myCoverageMapV2;
CoverageMap myCoverageMap;
virtual void voxelDeleted(VoxelNode* node);
virtual void voxelUpdated(VoxelNode* node);
virtual void elementDeleted(OctreeElement* element);
virtual void elementUpdated(OctreeElement* element);
virtual void nodeAdded(Node* node);
virtual void nodeKilled(Node* node);
virtual void domainChanged(QString domain);
bool treeIsBusy() const { return _treeIsBusy; }
VoxelTreeElement* getVoxelEnclosing(const glm::vec3& point);
signals:
void importSize(float x, float y, float z);
@ -176,33 +173,34 @@ private:
bool _initialized;
int _callsToTreesToArrays;
VoxelNodeBag _removedVoxels;
OctreeElementBag _removedVoxels;
// Operation functions for tree recursion methods
static int _nodeCount;
static bool randomColorOperation(VoxelNode* node, void* extraData);
static bool falseColorizeRandomOperation(VoxelNode* node, void* extraData);
static bool trueColorizeOperation(VoxelNode* node, void* extraData);
static bool falseColorizeInViewOperation(VoxelNode* node, void* extraData);
static bool falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData);
static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData);
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData);
static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData);
static bool falseColorizeOccludedOperation(VoxelNode* node, void* extraData);
static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData);
static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData);
static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData);
static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData);
static bool forceRedrawEntireTreeOperation(VoxelNode* node, void* extraData);
static bool clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData);
static bool hideOutOfViewOperation(VoxelNode* node, void* extraData);
static bool hideAllSubTreeOperation(VoxelNode* node, void* extraData);
static bool showAllSubTreeOperation(VoxelNode* node, void* extraData);
static bool showAllLocalVoxelsOperation(VoxelNode* node, void* extraData);
static bool randomColorOperation(OctreeElement* element, void* extraData);
static bool falseColorizeRandomOperation(OctreeElement* element, void* extraData);
static bool trueColorizeOperation(OctreeElement* element, void* extraData);
static bool falseColorizeInViewOperation(OctreeElement* element, void* extraData);
static bool falseColorizeDistanceFromViewOperation(OctreeElement* element, void* extraData);
static bool getDistanceFromViewRangeOperation(OctreeElement* element, void* extraData);
static bool removeOutOfViewOperation(OctreeElement* element, void* extraData);
static bool falseColorizeRandomEveryOtherOperation(OctreeElement* element, void* extraData);
static bool collectStatsForTreesAndVBOsOperation(OctreeElement* element, void* extraData);
static bool falseColorizeOccludedOperation(OctreeElement* element, void* extraData);
static bool falseColorizeSubTreeOperation(OctreeElement* element, void* extraData);
static bool falseColorizeOccludedV2Operation(OctreeElement* element, void* extraData);
static bool falseColorizeBySourceOperation(OctreeElement* element, void* extraData);
static bool killSourceVoxelsOperation(OctreeElement* element, void* extraData);
static bool forceRedrawEntireTreeOperation(OctreeElement* element, void* extraData);
static bool clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData);
static bool hideOutOfViewOperation(OctreeElement* element, void* extraData);
static bool hideAllSubTreeOperation(OctreeElement* element, void* extraData);
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData);
static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData);
int updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw);
int forceRemoveNodeFromArrays(VoxelNode* node);
int updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw);
int forceRemoveNodeFromArrays(VoxelTreeElement* node);
void copyWrittenDataToReadArraysFullVBOs();
void copyWrittenDataToReadArraysPartialVBOs();
@ -271,7 +269,7 @@ private:
void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]);
int newTreeToArrays(VoxelNode *currentNode);
int newTreeToArrays(VoxelTreeElement *currentNode);
void cleanupRemovedVoxels();
void copyWrittenDataToReadArrays(bool fullVBOs);

View file

@ -442,10 +442,6 @@ static TextRenderer* textRenderer() {
void Avatar::render(bool forceRenderHead) {
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
}
// render a simple round on the ground projected down from the avatar's position
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
@ -785,7 +781,7 @@ void Avatar::renderBody(bool forceRenderHead) {
_head.render(alpha, false);
}
}
_hand.render();
_hand.render(false);
}
void Avatar::getSkinColors(glm::vec3& lighter, glm::vec3& darker) {

View file

@ -83,7 +83,7 @@ void AvatarVoxelSystem::init() {
_boneIndicesLocation = _skinProgram.attributeLocation("boneIndices");
_boneWeightsLocation = _skinProgram.attributeLocation("boneWeights");
VoxelNode::removeUpdateHook(this); // we don't want this
VoxelTreeElement::removeUpdateHook(this); // we don't want this
_initialized = true;
}

View file

@ -15,21 +15,19 @@
#include "Util.h"
#include "renderer/ProgramObject.h"
using namespace std;
Hand::Hand(Avatar* owningAvatar) :
HandData((AvatarData*)owningAvatar),
_raveGloveClock(0.0f),
_raveGloveInitialized(false),
_owningAvatar(owningAvatar),
_renderAlpha(1.0),
_ballColor(0.0, 0.0, 0.4)
_ballColor(0.0, 0.0, 0.4),
_collisionCenter(0,0,0),
_collisionAge(0),
_collisionDuration(0)
{
// initialize all finger particle emitters with an invalid id as default
for (int f = 0; f< NUM_FINGERS; f ++ ) {
_raveGloveEmitter[f] = NULL_EMITTER;
}
}
void Hand::init() {
@ -40,9 +38,6 @@ void Hand::init() {
else {
_ballColor = glm::vec3(0.0, 0.0, 0.4);
}
_raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE;
_raveGloveEffectsModeChanged = false;
}
void Hand::reset() {
@ -51,45 +46,90 @@ void Hand::reset() {
void Hand::simulate(float deltaTime, bool isMine) {
calculateGeometry();
if (_isRaveGloveActive) {
if (_raveGloveEffectsModeChanged && _raveGloveInitialized) {
activateNewRaveGloveMode();
_raveGloveEffectsModeChanged = false;
}
updateRaveGloveParticles(deltaTime);
if (_collisionAge > 0.f) {
_collisionAge += deltaTime;
}
// Create a voxel at fingertip if controller button is pressed
const float FINGERTIP_VOXEL_SIZE = 0.0125;
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
FingerData& finger = palm.getFingers()[0];
glm::vec3 newVoxelPosition = finger.getTipPosition();
if (palm.getControllerButtons() & BUTTON_1) {
if (glm::length(newVoxelPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
Application::getInstance()->makeVoxel(newVoxelPosition,
FINGERTIP_VOXEL_SIZE,
paintColor.red(),
paintColor.green(),
paintColor.blue(),
true);
_lastFingerAddVoxel = newVoxelPosition;
calculateGeometry();
if (isMine) {
// Create a voxel at fingertip if controller button is pressed
const float FINGERTIP_VOXEL_SIZE = 0.0125;
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
FingerData& finger = palm.getFingers()[0]; // Sixense has only one finger
glm::vec3 fingerTipPosition = finger.getTipPosition();
if (palm.getControllerButtons() & BUTTON_1) {
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
Application::getInstance()->makeVoxel(fingerTipPosition,
FINGERTIP_VOXEL_SIZE,
paintColor.red(),
paintColor.green(),
paintColor.blue(),
true);
_lastFingerAddVoxel = fingerTipPosition;
}
} else if (palm.getControllerButtons() & BUTTON_2) {
if (glm::length(fingerTipPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
Application::getInstance()->removeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE);
_lastFingerDeleteVoxel = fingerTipPosition;
}
}
} else if (palm.getControllerButtons() & BUTTON_2) {
if (glm::length(newVoxelPosition - _lastFingerDeleteVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
Application::getInstance()->removeVoxel(newVoxelPosition, FINGERTIP_VOXEL_SIZE);
_lastFingerDeleteVoxel = newVoxelPosition;
// Check if the finger is intersecting with a voxel in the client voxel tree
VoxelTreeElement* fingerNode = Application::getInstance()->getVoxels()->getVoxelEnclosing(
glm::vec3(fingerTipPosition / (float)TREE_SCALE));
if (fingerNode) {
if (!palm.getIsCollidingWithVoxel()) {
// Collision has just started
palm.setIsCollidingWithVoxel(true);
handleVoxelCollision(&palm, fingerTipPosition, fingerNode, deltaTime);
// Set highlight voxel
VoxelDetail voxel;
glm::vec3 pos = fingerNode->getCorner();
voxel.x = pos.x;
voxel.y = pos.y;
voxel.z = pos.z;
voxel.s = fingerNode->getScale();
voxel.red = fingerNode->getColor()[0];
voxel.green = fingerNode->getColor()[1];
voxel.blue = fingerNode->getColor()[2];
Application::getInstance()->setHighlightVoxel(voxel);
Application::getInstance()->setIsHighlightVoxel(true);
}
} else {
if (palm.getIsCollidingWithVoxel()) {
// Collision has just ended
palm.setIsCollidingWithVoxel(false);
Application::getInstance()->setIsHighlightVoxel(false);
}
}
}
}
}
}
void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) {
// Collision between finger and a voxel plays sound
const float LOWEST_FREQUENCY = 100.f;
const float HERTZ_PER_RGB = 3.f;
const float DECAY_PER_SAMPLE = 0.0005f;
const float DURATION_MAX = 2.0f;
const float MIN_VOLUME = 0.1f;
float volume = MIN_VOLUME + glm::clamp(glm::length(palm->getVelocity()), 0.f, (1.f - MIN_VOLUME));
float duration = volume;
_collisionCenter = fingerTipPosition;
_collisionAge = deltaTime;
_collisionDuration = duration;
int voxelBrightness = voxel->getColor()[0] + voxel->getColor()[1] + voxel->getColor()[2];
float frequency = LOWEST_FREQUENCY + (voxelBrightness * HERTZ_PER_RGB);
Application::getInstance()->getAudio()->startDrumSound(volume,
frequency,
DURATION_MAX,
DECAY_PER_SAMPLE);
}
void Hand::calculateGeometry() {
const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it
@ -105,7 +145,7 @@ void Hand::calculateGeometry() {
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
const float standardBallRadius = 0.005f;
const float standardBallRadius = 0.010f;
_leapFingerTipBalls.resize(_leapFingerTipBalls.size() + 1);
HandBall& ball = _leapFingerTipBalls.back();
ball.rotation = _baseOrientation;
@ -113,6 +153,7 @@ void Hand::calculateGeometry() {
ball.radius = standardBallRadius;
ball.touchForce = 0.0;
ball.isCollidable = true;
ball.isColliding = false;
}
}
}
@ -134,89 +175,70 @@ void Hand::calculateGeometry() {
ball.radius = standardBallRadius;
ball.touchForce = 0.0;
ball.isCollidable = true;
ball.isColliding = false;
}
}
}
}
}
void Hand::setRaveGloveEffectsMode(QKeyEvent* event) {
_raveGloveEffectsModeChanged = true;
switch (event->key()) {
case Qt::Key_0: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR; break;
case Qt::Key_1: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_TRAILS; break;
case Qt::Key_2: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE; break;
case Qt::Key_3: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_WATER; break;
case Qt::Key_4: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FLASHY; break;
case Qt::Key_5: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER; break;
case Qt::Key_6: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER; break;
case Qt::Key_7: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_SNAKE; break;
case Qt::Key_8: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_PULSE; break;
case Qt::Key_9: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROB; break;
};
}
void Hand::render() {
void Hand::render( bool isMine) {
_renderAlpha = 1.0;
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) {
if (!isRaveGloveActive()) {
renderLeapFingerTrails();
}
if (isRaveGloveActive()) {
// Use mood lighting for the hand itself
setRaveLights(RAVE_LIGHTS_AVATAR);
}
renderLeapHands();
}
if (_isRaveGloveActive) {
if (_raveGloveInitialized) {
updateRaveGloveEmitters(); // do this after calculateGeometry
// Use normal lighting for the particles
setRaveLights(RAVE_LIGHTS_PARTICLES);
_raveGloveParticleSystem.render();
}
}
// If hand controller buttons pressed, render stuff as needed
if (getPalms().size() > 0) {
for (size_t i = 0; i < getPalms().size(); ++i) {
PalmData& palm = getPalms()[i];
// If trigger pulled, thrust in that direction and draw beam
const float MAX_THRUSTER_BEAM_LENGTH = 5.f;
const float THRUSTER_MARKER_SIZE = 0.0125f;
if (palm.getJoystickY() != 0.f) {
FingerData& finger = palm.getFingers()[0];
if (finger.isActive()) {
if (palm.getJoystickY() > 0.f) {
glColor3f(0, 1, 0);
} else {
glColor3f(1, 0, 0);
}
glm::vec3 palmPosition = palm.getPosition();
glm::vec3 pointerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH;
glPushMatrix();
glm::vec3 markerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH *
(0.5f + palm.getJoystickY() / 2.f);
if (isMine) {
// If hand/voxel collision has happened, render a little expanding sphere
if (_collisionAge > 0.f) {
float opacity = glm::clamp(1.f - (_collisionAge / _collisionDuration), 0.f, 1.f);
glColor4f(1, 0, 0, 0.5 * opacity);
glPushMatrix();
glTranslatef(_collisionCenter.x, _collisionCenter.y, _collisionCenter.z);
glutSolidSphere(_collisionAge * 0.25f, 20, 20);
glPopMatrix();
if (_collisionAge > _collisionDuration) {
_collisionAge = 0.f;
}
}
glTranslatef(markerPosition.x, markerPosition.y, markerPosition.z);
glutSolidSphere(THRUSTER_MARKER_SIZE, 10, 10);
glPopMatrix();
glLineWidth(2.0);
glBegin(GL_LINES);
glVertex3f(palmPosition.x, palmPosition.y, palmPosition.z);
glVertex3f(pointerPosition.x, pointerPosition.y, pointerPosition.z);
glEnd();
// If hand controller buttons pressed, render stuff as needed
if (getPalms().size() > 0) {
for (size_t i = 0; i < getPalms().size(); ++i) {
PalmData& palm = getPalms()[i];
// If trigger pulled, thrust in that direction and draw beam
const float MAX_THRUSTER_BEAM_LENGTH = 5.f;
const float THRUSTER_MARKER_SIZE = 0.0125f;
if (palm.getJoystickY() != 0.f) {
FingerData& finger = palm.getFingers()[0];
if (finger.isActive()) {
if (palm.getJoystickY() > 0.f) {
glColor3f(0, 1, 0);
} else {
glColor3f(1, 0, 0);
}
glm::vec3 palmPosition = palm.getPosition();
glm::vec3 pointerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH;
glPushMatrix();
glm::vec3 markerPosition = palmPosition +
glm::normalize(finger.getTipPosition() - palmPosition) *
MAX_THRUSTER_BEAM_LENGTH *
(0.5f + palm.getJoystickY() / 2.f);
glTranslatef(markerPosition.x, markerPosition.y, markerPosition.z);
glutSolidSphere(THRUSTER_MARKER_SIZE, 10, 10);
glPopMatrix();
glLineWidth(2.0);
glBegin(GL_LINES);
glVertex3f(palmPosition.x, palmPosition.y, palmPosition.z);
glVertex3f(pointerPosition.x, pointerPosition.y, pointerPosition.z);
glEnd();
}
}
}
}
@ -228,65 +250,6 @@ void Hand::render() {
}
void Hand::setRaveLights(RaveLightsSetting setting) {
if (setting == RAVE_LIGHTS_AVATAR) {
// Set some mood lighting
GLfloat ambient_color[] = { 0.0, 0.0, 0.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
GLfloat diffuse_color[] = { 0.4, 0.0, 0.0 };
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
GLfloat specular_color[] = { 0.0, 0.0, 0.0, 0.0};
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
glMateriali(GL_FRONT, GL_SHININESS, 0);
}
else if (setting == RAVE_LIGHTS_PARTICLES) {
// particles use a brighter light setting
GLfloat ambient_color[] = { 0.7, 0.7, 0.8 };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 };
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
glMateriali(GL_FRONT, GL_SHININESS, 96);
}
}
void Hand::renderRaveGloveStage() {
// Draw a simple fullscreen triangle fan, darkest in the center.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBegin(GL_TRIANGLE_FAN);
// Dark center vertex
glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// Lighter outer vertices
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glVertex3f( 1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glEnd();
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void Hand::renderLeapHands() {
const float alpha = 1.0f;
@ -299,8 +262,11 @@ void Hand::renderLeapHands() {
// Draw the leap balls
for (size_t i = 0; i < _leapFingerTipBalls.size(); i++) {
if (alpha > 0.0f) {
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
if (_leapFingerTipBalls[i].isColliding) {
glColor4f(handColor.r, 0, 0, alpha);
} else {
glColor4f(handColor.r, handColor.g, handColor.b, alpha);
}
glPushMatrix();
glTranslatef(_leapFingerTipBalls[i].position.x, _leapFingerTipBalls[i].position.y, _leapFingerTipBalls[i].position.z);
glutSolidSphere(_leapFingerTipBalls[i].radius, 20.0f, 20.0f);
@ -341,38 +307,6 @@ void Hand::renderLeapHands() {
glPopMatrix();
}
void Hand::renderLeapFingerTrails() {
// Draw the finger root cones
glDisable(GL_LIGHTING);
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];
if (palm.isActive()) {
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
int numPositions = finger.getTrailNumPositions() - 1;
if (numPositions > 0) {
glBegin(GL_TRIANGLE_STRIP);
for (int t = 0; t < numPositions; ++t)
{
const glm::vec3& center = finger.getTrailPosition(t);
const float halfWidth = 0.004f;
const glm::vec3 edgeDirection(1.0f, 0.0f, 0.0f);
glm::vec3 edge0 = center + edgeDirection * halfWidth;
glm::vec3 edge1 = center - edgeDirection * halfWidth;
float alpha = 1.0f - ((float)t / (float)(numPositions - 1));
alpha *= 0.25f;
glColor4f(1.0f, 1.0f, 1.0f, alpha);
glVertex3fv((float*)&edge0);
glVertex3fv((float*)&edge1);
}
glEnd();
}
}
}
}
glEnable(GL_LIGHTING);
}
void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals) {
@ -390,410 +324,6 @@ void Hand::setLeapHands(const std::vector<glm::vec3>& handPositions,
}
// call this soon after the geometry of the leap hands are set
void Hand::updateRaveGloveEmitters() {
int emitterIndex = 0;
for (size_t i = 0; i < NUM_FINGERS; i++) {
_raveGloveParticleSystem.setEmitterActive(_raveGloveEmitter[i], false);
}
for (size_t palmIndex = 0; palmIndex < getNumPalms(); ++palmIndex) {
PalmData& palm = getPalms()[palmIndex];
if (palm.isActive()) {
for (size_t f = 0; f < palm.getNumFingers(); ++f) {
FingerData& finger = palm.getFingers()[f];
if (finger.isActive()) {
if (emitterIndex < NUM_FINGERS) { // safety, stop at the array size
glm::vec3 fingerDirection = finger.getTipPosition() - finger.getRootPosition();
float fingerLength = glm::length(fingerDirection);
if (fingerLength > 0.0f) {
fingerDirection /= fingerLength;
} else {
fingerDirection = IDENTITY_UP;
}
_raveGloveParticleSystem.setEmitterActive (_raveGloveEmitter[emitterIndex], true);
_raveGloveParticleSystem.setEmitterPosition (_raveGloveEmitter[emitterIndex], finger.getTipPosition());
_raveGloveParticleSystem.setEmitterDirection(_raveGloveEmitter[emitterIndex], fingerDirection);
}
}
emitterIndex++;
}
}
}
}
// call this from within the simulate method
void Hand::updateRaveGloveParticles(float deltaTime) {
if (!_raveGloveInitialized) {
// start up the rave glove finger particles...
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
_raveGloveEmitter[f] = _raveGloveParticleSystem.addEmitter();
assert( _raveGloveEmitter[f] >= 0 );
assert( _raveGloveEmitter[f] != NULL_EMITTER );
}
setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE);
activateNewRaveGloveMode();
_raveGloveParticleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f));
_raveGloveInitialized = true;
} else {
_raveGloveParticleSystem.simulate(deltaTime);
}
}
// The rave glove mode has changed, so activate the effects.
void Hand::activateNewRaveGloveMode() {
if (!_raveGloveInitialized) {
return;
}
int mode = _raveGloveEffectsMode;
_raveGloveParticleSystem.killAllParticles();
for ( int f = 0; f< NUM_FINGERS; f ++ ) {
ParticleSystem::ParticleAttributes attributes;
//-----------------------------------------
// throbbing color cycle
//-----------------------------------------
if (mode == RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.03f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0f );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.modulationAmplitude = 1.0;
attributes.modulationRate = 0.33;
attributes.modulationStyle = COLOR_MODULATION_STYLE_RAINBOW_CYCLE;
attributes.color = glm::vec4( 0.5f, 0.5f, 0.5f, 1.0f);
attributes.radius = 0.02f;
attributes.gravity = 0.0f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// trails
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_TRAILS) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 50.0f );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 5 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 1.0f, 0.5f, 0.2f, 1.0f);
attributes.gravity = 0.005f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.002f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.color = glm::vec4( 1.0f, 0.2f, 0.2f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.color = glm::vec4( 1.0f, 0.2f, 0.2f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
}
//-----------------------------------------
// Fire!
//-----------------------------------------
if (mode == RAVE_GLOVE_EFFECTS_MODE_FIRE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 120.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 6 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.005f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.5f, 0.5f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.003f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.jitter = 0.0f;
attributes.gravity = -0.005f;
attributes.color = glm::vec4( 1.0f, 0.2f, 0.0f, 0.4f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.gravity = 0.0f;
attributes.color = glm::vec4( 0.4f, 0.4f, 0.4f, 0.2f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.02f;
attributes.color = glm::vec4( 0.4f, 0.6f, 0.9f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// water
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_WATER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.6f );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.001f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 5 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.5f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.004f;
attributes.bounce = 1.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.gravity = 0.01f;
attributes.jitter = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.2f);
attributes.radius = 0.002f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.color = glm::vec4( 0.8f, 0.9f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// flashy
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_FLASHY) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.1 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 12 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.05f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 0.0f, 1.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.0f, 0.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// Bozo sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.2 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 12 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
attributes.airFriction = 0.0f;
attributes.jitter = 0.01f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 1.0f, 0.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 1.0f, 0.0f, .0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.0f;
attributes.color = glm::vec4( 0.0f, 0.0f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// long sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_RIBBON );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], false );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 7 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.4f);
attributes.radius = 0.0f;
attributes.airFriction = 0.0f;
attributes.jitter = 0.0001f;
attributes.bounce = 1.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.005f;
attributes.color = glm::vec4( 0.0f, 0.5f, 0.5f, 0.8f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.007f;
attributes.color = glm::vec4( 0.5f, 0.0f, 0.5f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.02f;
attributes.color = glm::vec4( 0.0f, 0.0f, 1.0f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// bubble snake
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_SNAKE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 1.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.002f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 100.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 7 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.001f;
attributes.color = glm::vec4( 0.5f, 1.0f, 0.5f, 1.0f);
attributes.airFriction = 0.01f;
attributes.jitter = 0.0f;
attributes.emitterAttraction = 0.0f;
attributes.tornadoForce = 1.1f;
attributes.neighborAttraction = 1.1f;
attributes.neighborRepulsion = 1.1f;
attributes.bounce = 0.0f;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
attributes.radius = 0.002f;
attributes.color = glm::vec4( 1.0f, 1.0f, 1.0f, 1.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
attributes.radius = 0.003f;
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.5f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
attributes.radius = 0.004f;
attributes.color = glm::vec4( 0.3f, 0.3f, 0.3f, 0.0f);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// pulse
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_PULSE) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.1f, 0.2f, 0.4f, 0.5f);
attributes.modulationAmplitude = 0.9;
attributes.modulationRate = 7.0;
attributes.modulationStyle = COLOR_MODULATION_STYLE_LIGHNTESS_PULSE;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// long sparkler
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.0 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.5f, 0.4f, 0.3f, 0.5f);
attributes.modulationAmplitude = 0.3;
attributes.modulationRate = 1.0;
attributes.modulationStyle = COLOR_MODULATION_STYLE_LIGHTNESS_WAVE;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
//-----------------------------------------
// throb
//-----------------------------------------
} else if (mode == RAVE_GLOVE_EFFECTS_MODE_THROB) {
_raveGloveParticleSystem.setParticleRenderStyle (_raveGloveEmitter[f], PARTICLE_RENDER_STYLE_SPHERE );
_raveGloveParticleSystem.setShowingEmitterBaseParticle(_raveGloveEmitter[f], true );
_raveGloveParticleSystem.setEmitterParticleLifespan (_raveGloveEmitter[f], 0.03 );
_raveGloveParticleSystem.setEmitterThrust (_raveGloveEmitter[f], 0.0f );
_raveGloveParticleSystem.setEmitterRate (_raveGloveEmitter[f], 30.0 );
_raveGloveParticleSystem.setEmitterParticleResolution (_raveGloveEmitter[f], 20 );
_raveGloveParticleSystem.setParticleAttributesToDefault(&attributes);
attributes.radius = 0.01f;
attributes.color = glm::vec4( 0.1f, 0.2f, 0.4f, 0.5f);
attributes.modulationAmplitude = 0.5;
attributes.modulationRate = 3.0;
attributes.modulationStyle = COLOR_MODULATION_STYLE_LIGHTNESS_WAVE;
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_0, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_1, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_2, attributes);
_raveGloveParticleSystem.setParticleAttributes(_raveGloveEmitter[f], PARTICLE_LIFESTAGE_3, attributes);
}
}
}

View file

@ -25,12 +25,6 @@
#include "world.h"
#include "devices/SerialInterface.h"
enum RaveLightsSetting {
RAVE_LIGHTS_AVATAR = 0,
RAVE_LIGHTS_PARTICLES
};
class Avatar;
class ProgramObject;
@ -45,20 +39,15 @@ public:
glm::vec3 velocity; // the velocity of the ball
float radius; // the radius of the ball
bool isCollidable; // whether or not the ball responds to collisions
bool isColliding; // ball is currently colliding
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
};
void init();
void reset();
void simulate(float deltaTime, bool isMine);
void render();
void renderRaveGloveStage();
void setRaveLights(RaveLightsSetting setting);
void render(bool isMine);
void setBallColor (glm::vec3 ballColor ) { _ballColor = ballColor; }
void updateRaveGloveParticles(float deltaTime);
void updateRaveGloveEmitters();
void setRaveGloveEffectsMode(QKeyEvent* event);
// getters
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
@ -68,12 +57,7 @@ private:
// disallow copies of the Hand, copy of owning Avatar is disallowed too
Hand(const Hand&);
Hand& operator= (const Hand&);
ParticleSystem _raveGloveParticleSystem;
float _raveGloveClock;
bool _raveGloveInitialized;
int _raveGloveEmitter[NUM_FINGERS];
int _controllerButtons; /// Button states read from hand-held controllers
Avatar* _owningAvatar;
@ -83,16 +67,22 @@ private:
std::vector<HandBall> _leapFingerRootBalls;
glm::vec3 _lastFingerAddVoxel, _lastFingerDeleteVoxel;
bool _isCollidingWithVoxel;
VoxelDetail _collidingVoxel;
glm::vec3 _collisionCenter;
float _collisionAge;
float _collisionDuration;
// private methods
void setLeapHands(const std::vector<glm::vec3>& handPositions,
const std::vector<glm::vec3>& handNormals);
void activateNewRaveGloveMode();
void renderLeapHands();
void renderLeapFingerTrails();
void calculateGeometry();
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
};
#endif

View file

@ -1,92 +0,0 @@
//
// HandControl.cpp
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "HandControl.h"
// this class takes mouse movements normalized within the screen
// dimensions and uses those to determine avatar hand movements, as well
// as states for ramping up and ramping down the amplitude of such movements.
//
// This class might expand to accommodate 3D input devices
//
HandControl::HandControl() {
_enabled = false;
_width = 0;
_height = 0;
_startX = 0;
_startY = 0;
_x = 0;
_y = 0;
_lastX = 0;
_lastY = 0;
_velocityX = 0;
_velocityY = 0;
_rampUpRate = 0.05;
_rampDownRate = 0.02;
_envelope = 0.0f;
}
void HandControl::setScreenDimensions(int width, int height) {
_width = width;
_height = height;
_startX = _width / 2;
_startY = _height / 2;
}
void HandControl::update(int x, int y) {
_lastX = _x;
_lastY = _y;
_x = x;
_y = y;
_velocityX = _x - _lastX;
_velocityY = _y - _lastY;
// if the mouse is moving, ramp up the envelope to increase amplitude of hand movement...
if ((_velocityX != 0)
|| (_velocityY != 0)) {
_enabled = true;
if (_envelope < 1.0) {
_envelope += _rampUpRate;
if (_envelope >= 1.0) {
_envelope = 1.0;
}
}
}
// if not enabled ramp down the envelope to decrease amplitude of hand movement...
if (! _enabled) {
if (_envelope > 0.0) {
_envelope -= _rampDownRate;
if (_envelope <= 0.0) {
_startX = _width / 2;
_startY = _height / 2;
_envelope = 0.0;
}
}
}
_leftRight = 0.0;
_downUp = 0.0;
_backFront = 0.0;
// if envelope is greater than zero, apply mouse movement to values to be output
if (_envelope > 0.0) {
_leftRight += ((_x - _startX) / (float)_width ) * _envelope;
_downUp += ((_y - _startY) / (float)_height) * _envelope;
}
}
glm::vec3 HandControl::getValues() {
return glm::vec3(_leftRight, _downUp, _backFront);
}
void HandControl::stop() {
_enabled = false;
}

View file

@ -1,41 +0,0 @@
//
// HandControl.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__HandControl__
#define __interface__HandControl__
#include <glm/glm.hpp>
class HandControl {
public:
HandControl();
void setScreenDimensions(int width, int height);
void update( int x, int y );
glm::vec3 getValues();
void stop();
private:
bool _enabled;
int _width;
int _height;
int _startX;
int _startY;
int _x;
int _y;
int _lastX;
int _lastY;
int _velocityX;
int _velocityY;
float _rampUpRate;
float _rampDownRate;
float _envelope;
float _leftRight;
float _downUp;
float _backFront;
};
#endif

View file

@ -499,10 +499,6 @@ static TextRenderer* textRenderer() {
void MyAvatar::render(bool forceRenderHead) {
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
}
// render a simple round on the ground projected down from the avatar's position
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
@ -562,21 +558,6 @@ void MyAvatar::render(bool forceRenderHead) {
}
}
void MyAvatar::renderScreenTint(ScreenTintLayer layer) {
if (layer == SCREEN_TINT_BEFORE_AVATARS) {
if (_hand.isRaveGloveActive()) {
_hand.renderRaveGloveStage();
}
}
else if (layer == SCREEN_TINT_BEFORE_AVATARS) {
if (_hand.isRaveGloveActive()) {
// Restore the world lighting
Application::getInstance()->setupWorldLight();
}
}
}
void MyAvatar::saveData(QSettings* settings) {
settings->beginGroup("Avatar");
@ -728,7 +709,7 @@ void MyAvatar::renderBody(bool forceRenderHead) {
_head.render(alpha, false);
}
}
_hand.render();
_hand.render(true);
}
void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) {

View file

@ -21,7 +21,6 @@ public:
void simulate(float deltaTime, Transmitter* transmitter);
void updateFromGyrosAndOrWebcam(bool turnWithHead);
void render(bool forceRenderHead);
void renderScreenTint(ScreenTintLayer layer);
// setters
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }

View file

@ -16,6 +16,7 @@
SixenseManager::SixenseManager() {
#ifdef HAVE_SIXENSE
sixenseInit();
sixenseSetFilterEnabled(0);
#endif
}
@ -24,7 +25,7 @@ SixenseManager::~SixenseManager() {
sixenseExit();
#endif
}
void SixenseManager::update(float deltaTime) {
#ifdef HAVE_SIXENSE
if (sixenseGetNumActiveControllers() == 0) {
@ -32,7 +33,6 @@ void SixenseManager::update(float deltaTime) {
}
MyAvatar* avatar = Application::getInstance()->getAvatar();
Hand& hand = avatar->getHand();
hand.getPalms().clear();
int maxControllers = sixenseGetMaxControllers();
for (int i = 0; i < maxControllers; i++) {
@ -42,45 +42,69 @@ void SixenseManager::update(float deltaTime) {
sixenseControllerData data;
sixenseGetNewestData(i, &data);
// Set palm position and normal based on Hydra position/orientation
PalmData palm(&hand);
palm.setActive(true);
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
//printf("si: %i\n", data.controller_index);
// Compute current velocity from position change
palm.setVelocity((position - palm.getPosition()) / deltaTime);
// Set palm position and normal based on Hydra position/orientation
// Either find a palm matching the sixense controller, or make a new one
PalmData* palm;
bool foundHand = false;
for (int j = 0; j < hand.getNumPalms(); j++) {
if (hand.getPalms()[j].getSixenseID() == data.controller_index) {
palm = &hand.getPalms()[j];
foundHand = true;
}
}
if (!foundHand) {
PalmData newPalm(&hand);
hand.getPalms().push_back(newPalm);
palm = &hand.getPalms()[hand.getNumPalms() - 1];
palm->setSixenseID(data.controller_index);
printf("Found new Sixense controller, ID %i\n", data.controller_index);
}
palm->setActive(true);
// Read controller buttons and joystick into the hand
palm.setControllerButtons(data.buttons);
palm.setTrigger(data.trigger);
palm.setJoystick(data.joystick_x, data.joystick_y);
palm->setControllerButtons(data.buttons);
palm->setTrigger(data.trigger);
palm->setJoystick(data.joystick_x, data.joystick_y);
// Vibrate if needed
if (palm->getIsCollidingWithVoxel()) {
//printf("vibrate!\n");
//vibrate(data.controller_index, 100, 1);
}
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
// Adjust for distance between acquisition 'orb' and the user's torso
// (distance to the right of body center, distance below torso, distance behind torso)
const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f);
position = SPHERE_TO_TORSO + position;
palm.setRawPosition(position);
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
// Rotate about controller
// Compute current velocity from position change
palm->setVelocity((position - palm->getRawPosition()) / deltaTime / 1000.f); // meters/sec
palm->setRawPosition(position);
// Rotation of Palm
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
rotation = glm::angleAxis(180.0f, 0.f, 1.f, 0.f) * rotation;
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
palm.setRawNormal(rotation * PALM_VECTOR);
palm->setRawNormal(rotation * PALM_VECTOR);
// initialize the "finger" based on the direction
FingerData finger(&palm, &hand);
FingerData finger(palm, &hand);
finger.setActive(true);
finger.setRawRootPosition(position);
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, 100.0f);
const float FINGER_LENGTH = 300.0f; // Millimeters
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
finger.setRawTipPosition(position + rotation * FINGER_VECTOR);
// three fingers indicates to the skeleton that we have enough data to determine direction
palm.getFingers().clear();
palm.getFingers().push_back(finger);
palm.getFingers().push_back(finger);
palm.getFingers().push_back(finger);
hand.getPalms().push_back(palm);
palm->getFingers().clear();
palm->getFingers().push_back(finger);
palm->getFingers().push_back(finger);
palm->getFingers().push_back(finger);
}
#endif
}

View file

@ -1397,8 +1397,9 @@ FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) {
return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer));
}
bool addMeshVoxelsOperation(VoxelNode* node, void* extraData) {
if (!node->isLeaf()) {
bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
if (!voxel->isLeaf()) {
return true;
}
FBXMesh& mesh = *static_cast<FBXMesh*>(extraData);
@ -1408,13 +1409,13 @@ bool addMeshVoxelsOperation(VoxelNode* node, void* extraData) {
const int VERTICES_PER_FACE = 4;
const int VERTEX_COUNT = FACE_COUNT * VERTICES_PER_FACE;
const float EIGHT_BIT_MAXIMUM = 255.0f;
glm::vec3 color = glm::vec3(node->getColor()[0], node->getColor()[1], node->getColor()[2]) / EIGHT_BIT_MAXIMUM;
glm::vec3 color = glm::vec3(voxel->getColor()[0], voxel->getColor()[1], voxel->getColor()[2]) / EIGHT_BIT_MAXIMUM;
for (int i = 0; i < VERTEX_COUNT; i++) {
part.quadIndices.append(part.quadIndices.size());
mesh.colors.append(color);
}
glm::vec3 corner = node->getCorner();
float scale = node->getScale();
glm::vec3 corner = voxel->getCorner();
float scale = voxel->getScale();
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z));
mesh.vertices.append(glm::vec3(corner.x, corner.y, corner.z + scale));

View file

@ -24,22 +24,24 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
vertices->clear();
vertices->reserve(limit);
const unsigned MILKY_WAY_WIDTH = 16.0; // width in degrees of one half of the Milky Way
const float MILKY_WAY_INCLINATION = 30.0f; // angle of Milky Way from horizontal in degrees
const float MILKY_WAY_RATIO = 0.6;
const unsigned MILKY_WAY_WIDTH = 12.0; // width in degrees of one half of the Milky Way
const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees
const float MILKY_WAY_RATIO = 0.4;
const unsigned NUM_DEGREES = 360;
for(int star = 0; star < floor(limit * (1 - MILKY_WAY_RATIO)); ++star) {
float azimuth, altitude;
azimuth = (((float)rand() / (float) RAND_MAX) * NUM_DEGREES) - (NUM_DEGREES / 2);
altitude = (acos((2.0f * ((float)rand() / (float)RAND_MAX)) - 1.0f) / PI_OVER_180) + 90;
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
}
for(int star = 0; star < ceil(limit * MILKY_WAY_RATIO); ++star) {
float azimuth = ((float)rand() / (float) RAND_MAX) * NUM_DEGREES;
float altitude = asin((float)rand() / (float) RAND_MAX * 2 - 1) * MILKY_WAY_WIDTH;
float altitude = powf(randFloat()*0.5f, 2.f)/0.25f * MILKY_WAY_WIDTH;
if (randFloat() > 0.5f) {
altitude *= -1.f;
}
// we need to rotate the Milky Way band to the correct orientation in the sky
// convert from spherical coordinates to cartesian, rotate the point and then convert back.
@ -71,8 +73,15 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
// 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;
unsigned char red, green, blue;
if (randFloat() < 0.3f) {
// A few stars are colorful
red = 2 + (rand() % 254);
green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
} else {
// Most stars are dimmer and white
red = green = blue = 2 + (rand() % 128);
}
return red | (green << 8) | (blue << 16);
}

View file

@ -103,7 +103,7 @@ QString LodToolsDialog::getFeedbackText() {
// distance feedback
float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();
float relativeToDefault = voxelSizeScale / DEFAULT_VOXEL_SIZE_SCALE;
float relativeToDefault = voxelSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
QString result;
if (relativeToDefault > 1.01) {
result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback);
@ -134,7 +134,7 @@ void LodToolsDialog::boundaryLevelValueChanged(int value) {
}
void LodToolsDialog::resetClicked(bool checked) {
int sliderValue = DEFAULT_VOXEL_SIZE_SCALE / TREE_SCALE;
int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE;
//sizeScaleValueChanged(sliderValue);
_lodSize->setValue(sliderValue);
_boundaryLevelAdjust->setValue(0);

View file

@ -136,7 +136,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
label = _labels[_localVoxelsMemory];
statsValue.str("");
statsValue <<
"Nodes RAM: " << VoxelNode::getTotalMemoryUsage() / 1000000.f << "MB "
"Nodes RAM: " << OctreeElement::getTotalMemoryUsage() / 1000000.f << "MB "
"Geometry RAM: " << voxels->getVoxelMemoryUsageRAM() / 1000000.f << "MB " <<
"VBO: " << voxels->getVoxelMemoryUsageVBO() / 1000000.f << "MB ";
if (voxels->hasVoxelMemoryUsageGPU()) {
@ -146,9 +146,9 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
// Local Voxels
label = _labels[_localVoxels];
unsigned long localTotal = VoxelNode::getNodeCount();
unsigned long localInternal = VoxelNode::getInternalNodeCount();
unsigned long localLeaves = VoxelNode::getLeafNodeCount();
unsigned long localTotal = OctreeElement::getNodeCount();
unsigned long localInternal = OctreeElement::getInternalNodeCount();
unsigned long localLeaves = OctreeElement::getLeafNodeCount();
QString localTotalString = locale.toString((uint)localTotal); // consider adding: .rightJustified(10, ' ');
QString localInternalString = locale.toString((uint)localInternal);
QString localLeavesString = locale.toString((uint)localLeaves);
@ -177,7 +177,7 @@ void VoxelStatsDialog::paintEvent(QPaintEvent* event) {
serverCount++;
// calculate server node totals
totalNodes += stats.getTotalVoxels();
totalNodes += stats.getTotalElements();
totalInternal += stats.getTotalInternal();
totalLeaves += stats.getTotalLeaves();
@ -313,7 +313,7 @@ void VoxelStatsDialog::showAllVoxelServers() {
}
} // fall through... since MOST has all of MORE
case MORE: {
QString totalString = locale.toString((uint)stats.getTotalVoxels());
QString totalString = locale.toString((uint)stats.getTotalElements());
QString internalString = locale.toString((uint)stats.getTotalInternal());
QString leavesString = locale.toString((uint)stats.getTotalLeaves());

View file

@ -1,77 +0,0 @@
//
// AudioInjectionManager.cpp
// hifi
//
// Created by Stephen Birarda on 5/16/13.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#include <sys/time.h>
#include "SharedUtil.h"
#include "NodeList.h"
#include "NodeTypes.h"
#include "Node.h"
#include "PacketHeaders.h"
#include "AudioInjectionManager.h"
UDPSocket* AudioInjectionManager::_injectorSocket = NULL;
sockaddr AudioInjectionManager::_destinationSocket;
bool AudioInjectionManager::_isDestinationSocketExplicit = false;
AudioInjector* AudioInjectionManager::_injectors[50] = {};
AudioInjector* AudioInjectionManager::injectorWithCapacity(int capacity) {
for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) {
if (!_injectors[i]) {
_injectors[i] = new AudioInjector(capacity);
return _injectors[i];
}
}
return NULL;
}
void AudioInjectionManager::setDestinationSocket(sockaddr& destinationSocket) {
_destinationSocket = destinationSocket;
_isDestinationSocketExplicit = true;
}
void* AudioInjectionManager::injectAudioViaThread(void* args) {
AudioInjector* injector = (AudioInjector*) args;
// if we don't have an injectorSocket then grab the one from the node list
if (!_injectorSocket) {
_injectorSocket = NodeList::getInstance()->getNodeSocket();
}
// if we don't have an explicit destination socket then pull active socket for current audio mixer from node list
if (!_isDestinationSocketExplicit) {
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
if (audioMixer && audioMixer->getActiveSocket()) {
_destinationSocket = *audioMixer->getActiveSocket();
} else {
pthread_exit(0);
}
}
injector->injectAudio(_injectorSocket, &_destinationSocket);
// if this an injector inside the injection manager's array we're responsible for deletion
for (int i = 0; i < MAX_CONCURRENT_INJECTORS; i++) {
if (_injectors[i] == injector) {
// pointer matched - delete this injector
delete injector;
// set the pointer to NULL so we can reuse this spot
_injectors[i] = NULL;
}
}
pthread_exit(0);
}
void AudioInjectionManager::threadInjector(AudioInjector* injector) {
pthread_t audioInjectThread;
pthread_create(&audioInjectThread, NULL, injectAudioViaThread, (void*) injector);
}

View file

@ -1,36 +0,0 @@
//
// AudioInjectionManager.h
// hifi
//
// Created by Stephen Birarda on 5/16/13.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__AudioInjectionManager__
#define __hifi__AudioInjectionManager__
#include <iostream>
#include "UDPSocket.h"
#include "AudioInjector.h"
const int MAX_CONCURRENT_INJECTORS = 50;
class AudioInjectionManager {
public:
static AudioInjector* injectorWithCapacity(int capacity);
static void threadInjector(AudioInjector* injector);
static void setInjectorSocket(UDPSocket* injectorSocket) { _injectorSocket = injectorSocket;}
static void setDestinationSocket(sockaddr& destinationSocket);
private:
static void* injectAudioViaThread(void* args);
static UDPSocket* _injectorSocket;
static sockaddr _destinationSocket;
static bool _isDestinationSocketExplicit;
static AudioInjector* _injectors[MAX_CONCURRENT_INJECTORS];
};
#endif /* defined(__hifi__AudioInjectionManager__) */

View file

@ -1,137 +0,0 @@
//
// AudioInjector.cpp
// hifi
//
// Created by Stephen Birarda on 4/23/13.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#include <cstring>
#include <fstream>
#include <limits>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
#include "AudioInjector.h"
AudioInjector::AudioInjector(int maxNumSamples) :
_streamIdentifier(QUuid::createUuid()),
_numTotalSamples(maxNumSamples),
_position(0.0f, 0.0f, 0.0f),
_orientation(),
_radius(0.0f),
_volume(MAX_INJECTOR_VOLUME),
_indexOfNextSlot(0),
_isInjectingAudio(false)
{
_audioSampleArray = new int16_t[maxNumSamples];
memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t));
}
AudioInjector::~AudioInjector() {
delete[] _audioSampleArray;
}
void AudioInjector::injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket) {
if (_audioSampleArray && _indexOfNextSlot > 0) {
_isInjectingAudio = true;
timeval startTime;
// calculate the number of bytes required for additional data
int leadingBytes = numBytesForPacketHeader((unsigned char*) &PACKET_TYPE_INJECT_AUDIO)
+ NUM_BYTES_RFC4122_UUID
+ NUM_BYTES_RFC4122_UUID
+ sizeof(_position)
+ sizeof(_orientation)
+ sizeof(_radius)
+ sizeof(_volume);
unsigned char dataPacket[(BUFFER_LENGTH_SAMPLES_PER_CHANNEL * sizeof(int16_t)) + leadingBytes];
unsigned char* currentPacketPtr = dataPacket + populateTypeAndVersion(dataPacket, PACKET_TYPE_INJECT_AUDIO);
// copy the UUID for the owning node
QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122();
memcpy(currentPacketPtr, rfcUUID.constData(), rfcUUID.size());
currentPacketPtr += rfcUUID.size();
// copy the stream identifier
QByteArray rfcStreamIdentifier = _streamIdentifier.toRfc4122();
memcpy(currentPacketPtr, rfcStreamIdentifier.constData(), rfcStreamIdentifier.size());
currentPacketPtr += rfcStreamIdentifier.size();
memcpy(currentPacketPtr, &_position, sizeof(_position));
currentPacketPtr += sizeof(_position);
memcpy(currentPacketPtr, &_orientation, sizeof(_orientation));
currentPacketPtr += sizeof(_orientation);
memcpy(currentPacketPtr, &_radius, sizeof(_radius));
currentPacketPtr += sizeof(_radius);
*currentPacketPtr = _volume;
currentPacketPtr++;
gettimeofday(&startTime, NULL);
int nextFrame = 0;
for (int i = 0; i < _numTotalSamples; i += BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
int usecToSleep = usecTimestamp(&startTime) + (nextFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
int numSamplesToCopy = BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
if (_numTotalSamples - i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL) {
numSamplesToCopy = _numTotalSamples - i;
memset(currentPacketPtr + numSamplesToCopy,
0,
BUFFER_LENGTH_BYTES_PER_CHANNEL - (numSamplesToCopy * sizeof(int16_t)));
}
memcpy(currentPacketPtr, _audioSampleArray + i, numSamplesToCopy * sizeof(int16_t));
injectorSocket->send(destinationSocket, dataPacket, sizeof(dataPacket));
}
_isInjectingAudio = false;
}
}
void AudioInjector::addSample(const int16_t sample) {
if (_indexOfNextSlot != _numTotalSamples) {
// only add this sample if we actually have space for it
_audioSampleArray[_indexOfNextSlot++] = sample;
}
}
void AudioInjector::addSamples(int16_t* sampleBuffer, int numSamples) {
if (_audioSampleArray + _indexOfNextSlot + numSamples <= _audioSampleArray + _numTotalSamples) {
// only copy the audio from the sample buffer if there's space
memcpy(_audioSampleArray + _indexOfNextSlot, sampleBuffer, numSamples * sizeof(int16_t));
_indexOfNextSlot += numSamples;
}
}
void AudioInjector::clear() {
_indexOfNextSlot = 0;
memset(_audioSampleArray, 0, _numTotalSamples * sizeof(int16_t));
}
int16_t& AudioInjector::sampleAt(const int index) {
assert(index >= 0 && index < _numTotalSamples);
return _audioSampleArray[index];
}
void AudioInjector::insertSample(const int index, int sample) {
assert (index >= 0 && index < _numTotalSamples);
_audioSampleArray[index] = (int16_t) sample;
_indexOfNextSlot = index + 1;
}

View file

@ -1,73 +0,0 @@
//
// AudioInjector.h
// hifi
//
// Created by Stephen Birarda on 4/23/13.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__AudioInjector__
#define __hifi__AudioInjector__
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/component_wise.hpp>
#include <QtCore/QObject>
#include <QtCore/QUuid>
#include <RegisteredMetaTypes.h>
#include <UDPSocket.h>
#include "AudioRingBuffer.h"
const int MAX_INJECTOR_VOLUME = 0xFF;
const int INJECT_INTERVAL_USECS = floorf((BUFFER_LENGTH_SAMPLES_PER_CHANNEL / SAMPLE_RATE) * 1000000);
class AudioInjector : public QObject {
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
Q_PROPERTY(uchar volume READ getVolume WRITE setVolume);
public:
AudioInjector(int maxNumSamples);
~AudioInjector();
void injectAudio(UDPSocket* injectorSocket, sockaddr* destinationSocket);
bool isInjectingAudio() const { return _isInjectingAudio; }
unsigned char getVolume() const { return _volume; }
void setVolume(unsigned char volume) { _volume = volume; }
const glm::vec3& getPosition() const { return _position; }
void setPosition(const glm::vec3& position) { _position = position; }
const glm::quat& getOrientation() const { return _orientation; }
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
float getRadius() const { return _radius; }
void setRadius(float radius) { _radius = radius; }
bool hasSamplesToInject() const { return _indexOfNextSlot > 0; }
void addSample(const int16_t sample);
void addSamples(int16_t* sampleBuffer, int numSamples);
void clear();
public slots:
int16_t& sampleAt(const int index);
void insertSample(const int index, int sample);
private:
QUuid _streamIdentifier;
int16_t* _audioSampleArray;
int _numTotalSamples;
glm::vec3 _position;
glm::quat _orientation;
float _radius;
unsigned char _volume;
int _indexOfNextSlot;
bool _isInjectingAudio;
};
#endif /* defined(__hifi__AudioInjector__) */

View file

@ -16,7 +16,7 @@
#include "NodeData.h"
const float SAMPLE_RATE = 22050.0;
const int SAMPLE_RATE = 22050;
const int BUFFER_LENGTH_BYTES_STEREO = 1024;
const int BUFFER_LENGTH_BYTES_PER_CHANNEL = 512;

View file

@ -24,6 +24,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier)
}
const uchar MAX_INJECTOR_VOLUME = 255;
int InjectedAudioRingBuffer::parseData(unsigned char* sourceBuffer, int numBytes) {
unsigned char* currentBuffer = sourceBuffer + numBytesForPacketHeader(sourceBuffer);

View file

@ -11,8 +11,6 @@
#include <QtCore/QUuid>
#include "AudioInjector.h"
#include "PositionalAudioRingBuffer.h"
class InjectedAudioRingBuffer : public PositionalAudioRingBuffer {

View file

@ -22,4 +22,5 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi voxels library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})

View file

@ -10,8 +10,6 @@
#include "AvatarData.h"
#include <SharedUtil.h>
// Glove flags
#define GLOVE_FLAG_RAVE 0x01
// When converting between fixed and float, use this as the radix.
const int fingerVectorRadix = 4;
@ -19,10 +17,7 @@ const int fingerVectorRadix = 4;
HandData::HandData(AvatarData* owningAvatar) :
_basePosition(0.0f, 0.0f, 0.0f),
_baseOrientation(0.0f, 0.0f, 0.0f, 1.0f),
_owningAvatarData(owningAvatar),
_isRaveGloveActive(false),
_raveGloveEffectsMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR),
_raveGloveEffectsModeChanged(false)
_owningAvatarData(owningAvatar)
{
// Start with two palms
addNewPalm();
@ -41,8 +36,10 @@ _velocity(0, 0, 0),
_controllerButtons(0),
_isActive(false),
_leapID(LEAPID_INVALID),
_sixenseID(SIXENSEID_INVALID),
_numFramesWithoutData(0),
_owningHandData(owningHandData)
_owningHandData(owningHandData),
_isCollidingWithVoxel(false)
{
for (int i = 0; i < NUM_FINGERS_PER_HAND; ++i) {
_fingers.push_back(FingerData(this, owningHandData));
@ -65,13 +62,6 @@ _owningHandData(owningHandData)
int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
const unsigned char* startPosition = destinationBuffer;
unsigned char gloveFlags = 0;
if (isRaveGloveActive())
gloveFlags |= GLOVE_FLAG_RAVE;
*destinationBuffer++ = gloveFlags;
*destinationBuffer++ = getRaveGloveMode();
unsigned int numHands = 0;
for (unsigned int handIndex = 0; handIndex < getNumPalms(); ++handIndex) {
PalmData& palm = getPalms()[handIndex];
@ -118,8 +108,6 @@ int HandData::encodeRemoteData(unsigned char* destinationBuffer) {
int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
const unsigned char* startPosition = sourceBuffer;
unsigned char gloveFlags = *sourceBuffer++;
char effectsMode = *sourceBuffer++;
unsigned int numHands = *sourceBuffer++;
for (unsigned int handIndex = 0; handIndex < numHands; ++handIndex) {
@ -163,11 +151,6 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
palm.setActive(false);
}
setRaveGloveActive((gloveFlags & GLOVE_FLAG_RAVE) != 0);
if (numHands > 0) {
setRaveGloveMode(effectsMode);
}
// One byte for error checking safety.
unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition);
unsigned char checkLength = *sourceBuffer++;
@ -176,13 +159,6 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) {
return sourceBuffer - startPosition;
}
void HandData::setRaveGloveMode(int effectsMode) {
if (effectsMode != _raveGloveEffectsMode) {
_raveGloveEffectsModeChanged = true;
}
_raveGloveEffectsMode = effectsMode;
}
void HandData::setFingerTrailLength(unsigned int length) {
for (size_t i = 0; i < getNumPalms(); ++i) {
PalmData& palm = getPalms()[i];

View file

@ -24,22 +24,7 @@ const int NUM_FINGERS_PER_HAND = 5;
const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
const int LEAPID_INVALID = -1;
enum RaveGloveEffectsMode
{
RAVE_GLOVE_EFFECTS_MODE_NULL = -1,
RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR,
RAVE_GLOVE_EFFECTS_MODE_TRAILS,
RAVE_GLOVE_EFFECTS_MODE_FIRE,
RAVE_GLOVE_EFFECTS_MODE_WATER,
RAVE_GLOVE_EFFECTS_MODE_FLASHY,
RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER,
RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER,
RAVE_GLOVE_EFFECTS_MODE_SNAKE,
RAVE_GLOVE_EFFECTS_MODE_PULSE,
RAVE_GLOVE_EFFECTS_MODE_THROB,
NUM_RAVE_GLOVE_EFFECTS_MODES
};
const int SIXENSEID_INVALID = -1;
const int BUTTON_1 = 32;
const int BUTTON_2 = 64;
@ -75,20 +60,12 @@ public:
int encodeRemoteData(unsigned char* destinationBuffer);
int decodeRemoteData(unsigned char* sourceBuffer);
void setRaveGloveActive(bool active) { _isRaveGloveActive = active; }
void setRaveGloveMode(int effectsMode);
bool isRaveGloveActive() const { return _isRaveGloveActive; }
int getRaveGloveMode() { return _raveGloveEffectsMode; }
friend class AvatarData;
protected:
glm::vec3 _basePosition; // Hands are placed relative to this
glm::quat _baseOrientation; // Hands are placed relative to this
AvatarData* _owningAvatarData;
std::vector<PalmData> _palms;
bool _isRaveGloveActive;
int _raveGloveEffectsMode;
bool _raveGloveEffectsModeChanged;
private:
// privatize copy ctor and assignment operator so copies of this object cannot be made
HandData(const HandData&);
@ -119,7 +96,7 @@ public:
void incrementFramesWithoutData() { _numFramesWithoutData++; }
void resetFramesWithoutData() { _numFramesWithoutData = 0; }
int getFramesWithoutData() const { return _numFramesWithoutData; }
private:
glm::vec3 _tipRawPosition;
glm::vec3 _rootRawPosition;
@ -142,12 +119,16 @@ public:
const glm::vec3& getRawNormal() const { return _rawNormal; }
bool isActive() const { return _isActive; }
int getLeapID() const { return _leapID; }
int getSixenseID() const { return _sixenseID; }
std::vector<FingerData>& getFingers() { return _fingers; }
size_t getNumFingers() { return _fingers.size(); }
void setActive(bool active) { _isActive = active; }
void setLeapID(int id) { _leapID = id; }
void setSixenseID(int id) { _sixenseID = id; }
void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
void setRawNormal(const glm::vec3& normal) { _rawNormal = normal; }
void setVelocity(const glm::vec3& velocity) { _velocity = velocity; }
@ -166,6 +147,9 @@ public:
void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
float getJoystickX() { return _joystickX; }
float getJoystickY() { return _joystickY; }
bool getIsCollidingWithVoxel() { return _isCollidingWithVoxel; }
void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; }
private:
std::vector<FingerData> _fingers;
@ -178,8 +162,12 @@ private:
bool _isActive; // This has current valid data
int _leapID; // the Leap's serial id for this tracked object
int _sixenseID; // Sixense controller ID for this palm
int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
HandData* _owningHandData;
bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel
};
#endif /* defined(__hifi__HandData__) */

View file

@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
set(TARGET_NAME voxel-server-library)
set(TARGET_NAME octree-server)
find_package(Qt5Widgets REQUIRED)
@ -24,9 +24,6 @@ qt5_use_modules(${TARGET_NAME} Widgets)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
@ -36,8 +33,10 @@ target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi voxels library
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi octree library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi avatars library
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
# link dl library on UNIX for civetweb
if (UNIX AND NOT APPLE)
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
endif (UNIX AND NOT APPLE)

View file

@ -0,0 +1,168 @@
//
// OctreeInboundPacketProcessor.cpp
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded network packet processor for the voxel-server
//
#include <PacketHeaders.h>
#include <PerfStat.h>
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
#include "OctreeInboundPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) :
_myServer(myServer),
_receivedPacketCount(0),
_totalTransitTime(0),
_totalProcessTime(0),
_totalLockWaitTime(0),
_totalElementsInPacket(0),
_totalPackets(0)
{
}
void OctreeInboundPacketProcessor::resetStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
_singleSenderStats.clear();
}
void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr,
unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsVerboseDebug();
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
}
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
// Ask our tree subclass if it can handle the incoming packet...
PACKET_TYPE packetType = packetData[0];
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket);
_receivedPacketCount++;
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
uint64_t arrivedAt = usecTimestampNow();
uint64_t transitTime = arrivedAt - sentAt;
int editsInPacket = 0;
uint64_t processTime = 0;
uint64_t lockWaitTime = 0;
if (_myServer->wantsDebugReceiving()) {
printf("PROCESSING THREAD: got %c - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
packetType, _receivedPacketCount, packetLength, sequence, transitTime);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
unsigned char* editData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) {
int maxSize = packetLength - atByte;
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() %c "
"packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n",
packetType, packetData, packetLength, editData, atByte, maxSize);
}
uint64_t startLock = usecTimestampNow();
_myServer->getOctree()->lockForWrite();
uint64_t startProcess = usecTimestampNow();
int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType,
packetData, packetLength, editData, maxSize);
_myServer->getOctree()->unlock();
uint64_t endProcess = usecTimestampNow();
editsInPacket++;
uint64_t thisProcessTime = endProcess - startProcess;
uint64_t thisLockWaitTime = startProcess - startLock;
processTime += thisProcessTime;
lockWaitTime += thisLockWaitTime;
// skip to next voxel edit record in the packet
editData += editDataBytesRead;
atByte += editDataBytesRead;
}
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
"packetData=%p packetLength=%ld voxelData=%p atByte=%d\n",
packetType, packetData, packetLength, editData, atByte);
}
// Make sure our Node and NodeList knows we've heard from this node.
Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
QUuid& nodeUUID = DEFAULT_NODE_ID_REF;
if (senderNode) {
senderNode->setLastHeardMicrostamp(usecTimestampNow());
nodeUUID = senderNode->getUUID();
if (debugProcessPacket) {
qDebug() << "sender has uuid=" << nodeUUID << "\n";
}
} else {
if (debugProcessPacket) {
qDebug() << "sender has no known nodeUUID.\n";
}
}
trackInboundPackets(nodeUUID, sequence, transitTime, editsInPacket, processTime, lockWaitTime);
} else {
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
}
}
void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int editsInPacket, uint64_t processTime, uint64_t lockWaitTime) {
_totalTransitTime += transitTime;
_totalProcessTime += processTime;
_totalLockWaitTime += lockWaitTime;
_totalElementsInPacket += editsInPacket;
_totalPackets++;
// find the individual senders stats and track them there too...
// see if this is the first we've heard of this node...
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
SingleSenderStats stats;
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
_singleSenderStats[nodeUUID] = stats;
} else {
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
}
}
SingleSenderStats::SingleSenderStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
}

View file

@ -1,5 +1,5 @@
//
// VoxelServerPacketProcessor.h
// OctreeInboundPacketProcessor.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
@ -8,13 +8,13 @@
// Threaded or non-threaded network packet processor for the voxel-server
//
#ifndef __voxel_server__VoxelServerPacketProcessor__
#define __voxel_server__VoxelServerPacketProcessor__
#ifndef __octree_server__OctreeInboundPacketProcessor__
#define __octree_server__OctreeInboundPacketProcessor__
#include <map>
#include <ReceivedPacketProcessor.h>
class VoxelServer;
class OctreeServer;
class SingleSenderStats {
public:
@ -23,17 +23,17 @@ public:
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }
uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; }
uint64_t getAverageLockWaitTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; }
uint64_t getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
uint64_t getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalVoxelsInPacket;
uint64_t _totalElementsInPacket;
uint64_t _totalPackets;
};
@ -43,41 +43,41 @@ typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterato
/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class VoxelServerPacketProcessor : public ReceivedPacketProcessor {
class OctreeInboundPacketProcessor : public ReceivedPacketProcessor {
public:
VoxelServerPacketProcessor(VoxelServer* myServer);
OctreeInboundPacketProcessor(OctreeServer* myServer);
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }
uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; }
uint64_t getAverageLockWaitTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; }
uint64_t getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
uint64_t getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
void resetStats();
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
virtual void processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength);
private:
void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime);
VoxelServer* _myServer;
OctreeServer* _myServer;
int _receivedPacketCount;
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalVoxelsInPacket;
uint64_t _totalElementsInPacket;
uint64_t _totalPackets;
NodeToSenderStatsMap _singleSenderStats;
};
#endif // __voxel_server__VoxelServerPacketProcessor__
#endif // __octree_server__OctreeInboundPacketProcessor__

View file

@ -1,22 +1,21 @@
//
// VoxelPersistThread.cpp
// voxel-server
// OctreePersistThread.cpp
// Octree-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
// Threaded or non-threaded Octree persistence
//
#include <QDebug>
#include <NodeList.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "VoxelPersistThread.h"
#include "VoxelServer.h"
#include "OctreePersistThread.h"
#include "OctreeServer.h"
VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval) :
OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) :
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
@ -24,17 +23,17 @@ VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, in
_loadTimeUSecs(0) {
}
bool VoxelPersistThread::process() {
bool OctreePersistThread::process() {
if (!_initialLoadComplete) {
uint64_t loadStarted = usecTimestampNow();
qDebug("loading voxels from file: %s...\n", _filename);
qDebug("loading Octrees from file: %s...\n", _filename);
bool persistantFileRead;
_tree->lockForWrite();
{
PerformanceWarning warn(true, "Loading Voxel File", true);
PerformanceWarning warn(true, "Loading Octree File", true);
persistantFileRead = _tree->readFromSVOFile(_filename);
}
_tree->unlock();
@ -44,20 +43,20 @@ bool VoxelPersistThread::process() {
_loadTimeUSecs = loadDone - loadStarted;
_tree->clearDirtyBit(); // the tree is clean since we just loaded it
qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
qDebug("DONE loading Octrees from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = VoxelNode::getNodeCount();
unsigned long internalNodeCount = VoxelNode::getInternalNodeCount();
unsigned long leafNodeCount = VoxelNode::getLeafNodeCount();
unsigned long nodeCount = OctreeElement::getNodeCount();
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
double usecPerGet = (double)VoxelNode::getGetChildAtIndexTime() / (double)VoxelNode::getGetChildAtIndexCalls();
double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls();
qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n",
VoxelNode::getGetChildAtIndexTime(), VoxelNode::getGetChildAtIndexCalls(), usecPerGet);
OctreeElement::getGetChildAtIndexTime(), OctreeElement::getGetChildAtIndexCalls(), usecPerGet);
double usecPerSet = (double)VoxelNode::getSetChildAtIndexTime() / (double)VoxelNode::getSetChildAtIndexCalls();
double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls();
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet);
OctreeElement::getSetChildAtIndexTime(), OctreeElement::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
_lastCheck = usecTimestampNow(); // we just loaded, no need to save again
@ -75,10 +74,10 @@ bool VoxelPersistThread::process() {
// check the dirty bit and persist here...
_lastCheck = usecTimestampNow();
if (_tree->isDirty()) {
qDebug("saving voxels to file %s...\n",_filename);
qDebug("saving Octrees to file %s...\n",_filename);
_tree->writeToSVOFile(_filename);
_tree->clearDirtyBit(); // tree is clean after saving
qDebug("DONE saving voxels to file...\n");
qDebug("DONE saving Octrees to file...\n");
}
}
}

View file

@ -1,26 +1,25 @@
//
// VoxelPersistThread.h
// voxel-server
// OctreePersistThread.h
// Octree-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
// Threaded or non-threaded Octree persistence
//
#ifndef __voxel_server__VoxelPersistThread__
#define __voxel_server__VoxelPersistThread__
#ifndef __Octree_server__OctreePersistThread__
#define __Octree_server__OctreePersistThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <VoxelTree.h>
#include <Octree.h>
/// Generalized threaded processor for handling received inbound packets.
class VoxelPersistThread : public virtual GenericThread {
class OctreePersistThread : public virtual GenericThread {
public:
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
bool isInitialLoadComplete() const { return _initialLoadComplete; }
@ -31,7 +30,7 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
VoxelTree* _tree;
Octree* _tree;
const char* _filename;
int _persistInterval;
bool _initialLoadComplete;
@ -41,4 +40,4 @@ private:
uint64_t _lastCheck;
};
#endif // __voxel_server__VoxelPersistThread__
#endif // __Octree_server__OctreePersistThread__

View file

@ -1,5 +1,5 @@
//
// VoxelNodeData.cpp
// OctreeQueryNode.cpp
// hifi
//
// Created by Stephen Birarda on 3/21/13.
@ -8,15 +8,15 @@
#include "PacketHeaders.h"
#include "SharedUtil.h"
#include "VoxelNodeData.h"
#include "OctreeQueryNode.h"
#include <cstring>
#include <cstdio>
#include "VoxelSendThread.h"
#include "OctreeSendThread.h"
VoxelNodeData::VoxelNodeData(Node* owningNode) :
VoxelQuery(owningNode),
OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
OctreeQuery(owningNode),
_viewSent(false),
_voxelPacketAvailableBytes(MAX_PACKET_SIZE),
_octreePacketAvailableBytes(MAX_PACKET_SIZE),
_maxSearchLevel(1),
_maxLevelReachedInLastSearch(1),
_lastTimeBagEmpty(0),
@ -24,39 +24,38 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_viewFrustumJustStoppedChanging(true),
_currentPacketIsColor(true),
_currentPacketIsCompressed(false),
_voxelSendThread(NULL),
_octreeSendThread(NULL),
_lastClientBoundaryLevelAdjust(0),
_lastClientVoxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE),
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_lodChanged(false),
_lodInitialized(false)
{
_voxelPacket = new unsigned char[MAX_PACKET_SIZE];
_voxelPacketAt = _voxelPacket;
_lastVoxelPacket = new unsigned char[MAX_PACKET_SIZE];
_lastVoxelPacketLength = 0;
_octreePacket = new unsigned char[MAX_PACKET_SIZE];
_octreePacketAt = _octreePacket;
_lastOctreePacket = new unsigned char[MAX_PACKET_SIZE];
_lastOctreePacketLength = 0;
_duplicatePacketCount = 0;
_sequenceNumber = 0;
resetVoxelPacket(true); // don't bump sequence
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
// Create voxel sending thread...
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer) {
// Create octree sending thread...
QUuid nodeUUID = getOwningNode()->getUUID();
_voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer);
_voxelSendThread->initialize(true);
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
_octreeSendThread->initialize(true);
}
bool VoxelNodeData::packetIsDuplicate() const {
bool OctreeQueryNode::packetIsDuplicate() const {
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
// of the entire packet, we need to compare only the packet content...
if (_lastVoxelPacketLength == getPacketLength()) {
return memcmp(_lastVoxelPacket + VOXEL_PACKET_HEADER_SIZE,
_voxelPacket+VOXEL_PACKET_HEADER_SIZE , getPacketLength() - VOXEL_PACKET_HEADER_SIZE) == 0;
if (_lastOctreePacketLength == getPacketLength()) {
return memcmp(_lastOctreePacket + OCTREE_PACKET_HEADER_SIZE,
_octreePacket+OCTREE_PACKET_HEADER_SIZE , getPacketLength() - OCTREE_PACKET_HEADER_SIZE == 0);
}
return false;
}
bool VoxelNodeData::shouldSuppressDuplicatePacket() {
bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
bool shouldSuppress = false; // assume we won't suppress
// only consider duplicate packets
@ -90,19 +89,19 @@ bool VoxelNodeData::shouldSuppressDuplicatePacket() {
return shouldSuppress;
}
void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) {
void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
// Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
// packet send rate.
_lastVoxelPacketLength = getPacketLength();
memcpy(_lastVoxelPacket, _voxelPacket, _lastVoxelPacketLength);
_lastOctreePacketLength = getPacketLength();
memcpy(_lastOctreePacket, _octreePacket, _lastOctreePacketLength);
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
_currentPacketIsColor = getWantColor();
_currentPacketIsCompressed = getWantCompression();
VOXEL_PACKET_FLAGS flags = 0;
OCTREE_PACKET_FLAGS flags = 0;
if (_currentPacketIsColor) {
setAtBit(flags,PACKET_IS_COLOR_BIT);
}
@ -110,63 +109,63 @@ void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) {
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
}
_voxelPacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, PACKET_TYPE_VOXEL_DATA);
_voxelPacketAt = _voxelPacket + numBytesPacketHeader;
_voxelPacketAvailableBytes -= numBytesPacketHeader;
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_octreePacket, getMyPacketType());
_octreePacketAt = _octreePacket + numBytesPacketHeader;
_octreePacketAvailableBytes -= numBytesPacketHeader;
// pack in flags
VOXEL_PACKET_FLAGS* flagsAt = (VOXEL_PACKET_FLAGS*)_voxelPacketAt;
OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)_octreePacketAt;
*flagsAt = flags;
_voxelPacketAt += sizeof(VOXEL_PACKET_FLAGS);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_FLAGS);
_octreePacketAt += sizeof(OCTREE_PACKET_FLAGS);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS);
// pack in sequence number
VOXEL_PACKET_SEQUENCE* sequenceAt = (VOXEL_PACKET_SEQUENCE*)_voxelPacketAt;
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt;
*sequenceAt = _sequenceNumber;
_voxelPacketAt += sizeof(VOXEL_PACKET_SEQUENCE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastVoxelPacketLength == VOXEL_PACKET_HEADER_SIZE)) {
_octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastOctreePacketLength == OCTREE_PACKET_HEADER_SIZE)) {
_sequenceNumber++;
}
// pack in timestamp
VOXEL_PACKET_SENT_TIME now = usecTimestampNow();
VOXEL_PACKET_SENT_TIME* timeAt = (VOXEL_PACKET_SENT_TIME*)_voxelPacketAt;
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)_octreePacketAt;
*timeAt = now;
_voxelPacketAt += sizeof(VOXEL_PACKET_SENT_TIME);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SENT_TIME);
_octreePacketAt += sizeof(OCTREE_PACKET_SENT_TIME);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SENT_TIME);
_voxelPacketWaiting = false;
_octreePacketWaiting = false;
}
void VoxelNodeData::writeToPacket(const unsigned char* buffer, int bytes) {
void OctreeQueryNode::writeToPacket(const unsigned char* buffer, int bytes) {
// compressed packets include lead bytes which contain compressed size, this allows packing of
// multiple compressed portions together
if (_currentPacketIsCompressed) {
*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)_voxelPacketAt = bytes;
_voxelPacketAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)_octreePacketAt = bytes;
_octreePacketAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
if (bytes <= _voxelPacketAvailableBytes) {
memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes;
_voxelPacketWaiting = true;
if (bytes <= _octreePacketAvailableBytes) {
memcpy(_octreePacketAt, buffer, bytes);
_octreePacketAvailableBytes -= bytes;
_octreePacketAt += bytes;
_octreePacketWaiting = true;
}
}
VoxelNodeData::~VoxelNodeData() {
delete[] _voxelPacket;
delete[] _lastVoxelPacket;
OctreeQueryNode::~OctreeQueryNode() {
delete[] _octreePacket;
delete[] _lastOctreePacket;
if (_voxelSendThread) {
_voxelSendThread->terminate();
delete _voxelSendThread;
if (_octreeSendThread) {
_octreeSendThread->terminate();
delete _octreeSendThread;
}
}
bool VoxelNodeData::updateCurrentViewFrustum() {
bool OctreeQueryNode::updateCurrentViewFrustum() {
bool currentViewFrustumChanged = false;
ViewFrustum newestViewFrustum;
// get position and orientation details from the camera
@ -196,13 +195,13 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = true;
}
if (_lastClientVoxelSizeScale != getVoxelSizeScale()) {
_lastClientVoxelSizeScale = getVoxelSizeScale();
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lodChanged = true;
}
} else {
_lodInitialized = true;
_lastClientVoxelSizeScale = getVoxelSizeScale();
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = false;
}
@ -217,7 +216,7 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
return currentViewFrustumChanged;
}
void VoxelNodeData::setViewSent(bool viewSent) {
void OctreeQueryNode::setViewSent(bool viewSent) {
_viewSent = viewSent;
if (viewSent) {
_viewFrustumJustStoppedChanging = false;
@ -225,7 +224,7 @@ void VoxelNodeData::setViewSent(bool viewSent) {
}
}
void VoxelNodeData::updateLastKnownViewFrustum() {
void OctreeQueryNode::updateLastKnownViewFrustum() {
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
@ -239,7 +238,7 @@ void VoxelNodeData::updateLastKnownViewFrustum() {
}
bool VoxelNodeData::moveShouldDump() const {
bool OctreeQueryNode::moveShouldDump() const {
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
glm::vec3 newPosition = _currentViewFrustum.getPosition();
@ -251,12 +250,12 @@ bool VoxelNodeData::moveShouldDump() const {
return false;
}
void VoxelNodeData::dumpOutOfView() {
void OctreeQueryNode::dumpOutOfView() {
int stillInView = 0;
int outOfView = 0;
VoxelNodeBag tempBag;
OctreeElementBag tempBag;
while (!nodeBag.isEmpty()) {
VoxelNode* node = nodeBag.extract();
OctreeElement* node = nodeBag.extract();
if (node->isInView(_currentViewFrustum)) {
tempBag.insert(node);
stillInView++;
@ -266,7 +265,7 @@ void VoxelNodeData::dumpOutOfView() {
}
if (stillInView > 0) {
while (!tempBag.isEmpty()) {
VoxelNode* node = tempBag.extract();
OctreeElement* node = tempBag.extract();
if (node->isInView(_currentViewFrustum)) {
nodeBag.insert(node);
}

View file

@ -1,44 +1,46 @@
//
// VoxelNodeData.h
// OctreeQueryNode.h
// hifi
//
// Created by Stephen Birarda on 3/21/13.
// Created by Brad Hefta-Gaub on 12/4/13.
//
//
#ifndef __hifi__VoxelNodeData__
#define __hifi__VoxelNodeData__
#ifndef __hifi__OctreeQueryNode__
#define __hifi__OctreeQueryNode__
#include <iostream>
#include <NodeData.h>
#include <VoxelPacketData.h>
#include <VoxelQuery.h>
#include <OctreePacketData.h>
#include <OctreeQuery.h>
#include <CoverageMap.h>
#include <VoxelConstants.h>
#include <VoxelNodeBag.h>
#include <VoxelSceneStats.h>
#include <OctreeConstants.h>
#include <OctreeElementBag.h>
#include <OctreeSceneStats.h>
class VoxelSendThread;
class VoxelServer;
class OctreeSendThread;
class OctreeServer;
class VoxelNodeData : public VoxelQuery {
class OctreeQueryNode : public OctreeQuery {
public:
VoxelNodeData(Node* owningNode);
virtual ~VoxelNodeData();
OctreeQueryNode(Node* owningNode);
virtual ~OctreeQueryNode();
virtual PACKET_TYPE getMyPacketType() const = 0;
void resetVoxelPacket(bool lastWasSurpressed = false); // resets voxel packet to after "V" header
void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header
void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _voxelPacket; }
int getPacketLength() const { return (MAX_PACKET_SIZE - _voxelPacketAvailableBytes); }
bool isPacketWaiting() const { return _voxelPacketWaiting; }
const unsigned char* getPacket() const { return _octreePacket; }
int getPacketLength() const { return (MAX_PACKET_SIZE - _octreePacketAvailableBytes); }
bool isPacketWaiting() const { return _octreePacketWaiting; }
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
int getAvailable() const { return _voxelPacketAvailableBytes; }
int getAvailable() const { return _octreePacketAvailableBytes; }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
@ -46,7 +48,7 @@ public:
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
VoxelNodeBag nodeBag;
OctreeElementBag nodeBag;
CoverageMap map;
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
@ -76,25 +78,25 @@ public:
bool hasLodChanged() const { return _lodChanged; };
VoxelSceneStats stats;
OctreeSceneStats stats;
void initializeVoxelSendThread(VoxelServer* voxelServer);
bool isVoxelSendThreadInitalized() { return _voxelSendThread; }
void initializeOctreeSendThread(OctreeServer* octreeServer);
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
void dumpOutOfView();
private:
VoxelNodeData(const VoxelNodeData &);
VoxelNodeData& operator= (const VoxelNodeData&);
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
bool _viewSent;
unsigned char* _voxelPacket;
unsigned char* _voxelPacketAt;
int _voxelPacketAvailableBytes;
bool _voxelPacketWaiting;
unsigned char* _octreePacket;
unsigned char* _octreePacketAt;
int _octreePacketAvailableBytes;
bool _octreePacketWaiting;
unsigned char* _lastVoxelPacket;
int _lastVoxelPacketLength;
unsigned char* _lastOctreePacket;
int _lastOctreePacketLength;
int _duplicatePacketCount;
uint64_t _firstSuppressedPacket;
@ -108,15 +110,15 @@ private:
bool _currentPacketIsColor;
bool _currentPacketIsCompressed;
VoxelSendThread* _voxelSendThread;
OctreeSendThread* _octreeSendThread;
// watch for LOD changes
int _lastClientBoundaryLevelAdjust;
float _lastClientVoxelSizeScale;
float _lastClientOctreeSizeScale;
bool _lodChanged;
bool _lodInitialized;
VOXEL_PACKET_SEQUENCE _sequenceNumber;
OCTREE_PACKET_SEQUENCE _sequenceNumber;
};
#endif /* defined(__hifi__VoxelNodeData__) */
#endif /* defined(__hifi__OctreeQueryNode__) */

View file

@ -1,43 +1,34 @@
//
// VoxelSendThread.cpp
// voxel-server
// OctreeSendThread.cpp
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel packet sender
//
#include <EnvironmentData.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
extern EnvironmentData environmentData[3];
#include "VoxelSendThread.h"
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "OctreeSendThread.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
uint64_t startSceneSleepTime = 0;
uint64_t endSceneSleepTime = 0;
VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) :
OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) :
_nodeUUID(nodeUUID),
_myServer(myServer),
_packetData()
{
}
bool VoxelSendThread::process() {
bool OctreeSendThread::process() {
uint64_t start = usecTimestampNow();
bool gotLock = false;
// don't do any send processing until the initial load of the voxels is complete...
// don't do any send processing until the initial load of the octree is complete...
if (_myServer->isInitialLoadComplete()) {
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
@ -45,41 +36,41 @@ bool VoxelSendThread::process() {
// make sure the node list doesn't kill our node while we're using it
if (node->trylock()) {
gotLock = true;
VoxelNodeData* nodeData = NULL;
OctreeQueryNode* nodeData = NULL;
nodeData = (VoxelNodeData*) node->getLinkedData();
nodeData = (OctreeQueryNode*) node->getLinkedData();
int packetsSent = 0;
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
}
node->unlock(); // we're done with this node for now.
}
}
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n");
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
qDebug("OctreeSendThread::process() waiting for isInitialLoadComplete()\n");
}
}
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
if (isStillRunning() && gotLock) {
// dynamically sleep until we need to fire off the next set of voxels
// dynamically sleep until we need to fire off the next set of octree elements
int elapsed = (usecTimestampNow() - start);
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
PerformanceWarning warn(false,"VoxelSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
@ -88,15 +79,15 @@ bool VoxelSendThread::process() {
return isStillRunning(); // keep running till they terminate us
}
uint64_t VoxelSendThread::_usleepTime = 0;
uint64_t VoxelSendThread::_usleepCalls = 0;
uint64_t OctreeSendThread::_usleepTime = 0;
uint64_t OctreeSendThread::_usleepCalls = 0;
uint64_t VoxelSendThread::_totalBytes = 0;
uint64_t VoxelSendThread::_totalWastedBytes = 0;
uint64_t VoxelSendThread::_totalPackets = 0;
uint64_t OctreeSendThread::_totalBytes = 0;
uint64_t OctreeSendThread::_totalWastedBytes = 0;
uint64_t OctreeSendThread::_totalPackets = 0;
int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugVoxelSending();
int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugSending();
uint64_t now = usecTimestampNow();
bool packetSent = false; // did we send a packet?
@ -105,16 +96,16 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings.
if (nodeData->shouldSuppressDuplicatePacket()) {
nodeData->resetVoxelPacket(true); // we still need to reset it though!
nodeData->resetOctreePacket(true); // we still need to reset it though!
return packetsSent; // without sending...
}
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(messageData);
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
// If we've got a stats message ready to send, then see if we can piggyback them together
@ -145,11 +136,15 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
}
// actually send it
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
packetSent = true;
} else {
// not enough room in the packet, send two packets
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
// there was nothing else to send.
@ -169,8 +164,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
truePacketsSent++;
packetsSent++;
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
packetSent = true;
@ -191,8 +187,9 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
// If there's actually a packet waiting, then send it.
if (nodeData->isPacketWaiting()) {
// just send the voxel packet
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(),
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
packetSent = true;
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
@ -214,14 +211,14 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSent++;
nodeData->resetVoxelPacket();
nodeData->resetOctreePacket();
}
return packetsSent;
}
/// Version of voxel distributor that sends the deepest LOD level at once
int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
int truePacketsSent = 0;
int trueBytesSent = 0;
@ -242,7 +239,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// then let's just send that waiting packet.
if (!nodeData->getCurrentPacketFormatMatches()) {
if (nodeData->isPacketWaiting()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
@ -250,19 +247,19 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
}
nodeData->resetVoxelPacket();
nodeData->resetOctreePacket();
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(wantCompression), targetSize);
}
@ -270,7 +267,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
_packetData.changeSettings(wantCompression, targetSize);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()),
@ -279,8 +276,8 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent())
);
@ -290,7 +287,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow();
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
if (nodeData->getLastTimeBagEmpty() > 0) {
@ -308,7 +305,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
if (_myServer->wantDumpVoxelsOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
nodeData->dumpOutOfView();
}
nodeData->map.erase();
@ -329,15 +326,11 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
unsigned long elapsedTime = nodeData->stats.getElapsedTime();
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugSending()) {
qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes);
}
if (_myServer->wantDisplayVoxelStats()) {
nodeData->stats.printDebugDetails();
}
// start tracking our stats
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta())
&& nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
@ -347,23 +340,22 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->nodeBag.deleteAll();
}
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugSending()) {
qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes);
}
::startSceneSleepTime = _usleepTime;
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
_myServer->getServerTree().rootNode, _myServer->getJurisdiction());
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _myServer->getJurisdiction());
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->nodeBag.isEmpty()) {
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // only in case of empty
nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // only in case of empty
}
} else {
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // original behavior, reset on move or empty
nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // original behavior, reset on move or empty
}
}
@ -371,35 +363,33 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (!nodeData->nodeBag.isEmpty()) {
int bytesWritten = 0;
uint64_t start = usecTimestampNow();
uint64_t startCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = VoxelPacketData::getCompressContentCalls();
uint64_t startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = OctreePacketData::getCompressContentCalls();
bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxVoxelPacketsPerSecond() / INTERVALS_PER_SECOND));
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);
}
int extraPackingAttempts = 0;
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);
}
bool lastNodeDidntFit = false; // assume each node fits
if (!nodeData->nodeBag.isEmpty()) {
VoxelNode* subTree = nodeData->nodeBag.extract();
OctreeElement* subTree = nodeData->nodeBag.extract();
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
float voxelSizeScale = nodeData->getVoxelSizeScale();
float voxelSizeScale = nodeData->getOctreeSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
@ -416,12 +406,12 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
_myServer->getServerTree().lockForRead();
_myServer->getOctree()->lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) {
if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) {
if (_packetData.hasContent() && bytesWritten == 0 &&
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
@ -437,7 +427,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
_myServer->getOctree()->unlock();
} else {
// If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0
bytesWritten = 0;
@ -456,18 +446,18 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// form actually inflated beyond our padding, and in this case we will send the current packet, then
// write to out new packet...
int writtenSize = _packetData.getFinalizedSize()
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0);
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0);
if (writtenSize > nodeData->getAvailable()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n",
writtenSize, nodeData->getAvailable());
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n",
nodeData->getAvailable(), _packetData.getFinalizedSize(),
_packetData.getUncompressedSize(), _packetData.getTargetSize());
@ -486,11 +476,11 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
sendNow = false; // try to pack more
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
if (sendNow) {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
} else {
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
@ -499,9 +489,9 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// in the wire packet. We also include room for our section header, and a little bit of padding
// to account for the fact that whenc compressing small amounts of data, we sometimes end up with
// a larger compressed size then uncompressed size
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(nodeData->getWantCompression()), targetSize);
}
@ -509,8 +499,15 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
}
// Here's where we can/should allow the server to send other data...
// send the environment packet
if (shouldSendEnvironments) {
if (_myServer->hasSpecialPacketToSend()) {
trueBytesSent += _myServer->sendSpecialPacket(node);
truePacketsSent++;
packetsSentThisInterval++;
/**
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
int envPacketLength = numBytesPacketHeader;
int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount();
@ -519,19 +516,23 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
}
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength);
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
trueBytesSent += envPacketLength;
truePacketsSent++;
packetsSentThisInterval++;
**/
}
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
uint64_t endCompressCalls = VoxelPacketData::getCompressContentCalls();
uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls();
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
uint64_t endCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
@ -544,7 +545,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
} else if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
} else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
@ -554,16 +555,16 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
nodeData->map.printStats();
}
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval);
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);
}
} // end if bag wasn't empty, and so we sent stuff...

View file

@ -0,0 +1,45 @@
//
// OctreeSendThread.h
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded object for sending voxels to a client
//
#ifndef __octree_server__OctreeSendThread__
#define __octree_server__OctreeSendThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <OctreeElementBag.h>
#include "OctreeQueryNode.h"
#include "OctreeServer.h"
/// Threaded processor for sending voxel packets to a single client
class OctreeSendThread : public virtual GenericThread {
public:
OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer);
static uint64_t _totalBytes;
static uint64_t _totalWastedBytes;
static uint64_t _totalPackets;
static uint64_t _usleepTime;
static uint64_t _usleepCalls;
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
QUuid _nodeUUID;
OctreeServer* _myServer;
int handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
int packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
OctreePacketData _packetData;
};
#endif // __octree_server__OctreeSendThread__

View file

@ -1,78 +1,65 @@
//
// VoxelServer.cpp
// OctreeServer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 9/16/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <time.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtCore/QUuid>
#include <Logging.h>
#include <OctalCode.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <VoxelTree.h>
#include "VoxelNodeData.h"
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <SceneUtils.h>
#include <PerfStat.h>
#include <JurisdictionSender.h>
#include <UUID.h>
#ifdef _WIN32
#include "Syssocket.h"
#include "Systime.h"
#else
#include <sys/time.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#endif
#include "civetweb.h"
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
void attachVoxelNodeDataToNode(Node* newNode) {
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode);
newNode->setLinkedData(voxelNodeData);
if (GetInstance()) {
OctreeQueryNode* newQueryNodeData = GetInstance()->createOctreeQueryNode(newNode);
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
newNode->setLinkedData(newQueryNodeData);
}
}
}
VoxelServer* VoxelServer::_theInstance = NULL;
void OctreeServer::nodeAdded(Node* node) {
// do nothing
}
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes),
_serverTree(true) {
void OctreeServer::nodeKilled(Node* node) {
// Use this to cleanup our node
if (node->getType() == NODE_TYPE_AGENT) {
OctreeQueryNode* nodeData = (OctreeQueryNode*)node->getLinkedData();
if (nodeData) {
node->setLinkedData(NULL);
delete nodeData;
}
}
};
OctreeServer* OctreeServer::_theInstance = NULL;
OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
{
_argc = 0;
_argv = NULL;
_tree = NULL;
_packetsPerClientPerInterval = 10;
_wantVoxelPersist = true;
_wantLocalDomain = false;
_debugVoxelSending = false;
_shouldShowAnimationDebug = false;
_displayVoxelStats = false;
_debugVoxelReceiving = false;
_sendEnvironments = true;
_sendMinimalEnvironment = false;
_dumpVoxelsOnMove = false;
_wantPersist = true;
_debugSending = false;
_debugReceiving = false;
_verboseDebug = false;
_jurisdiction = NULL;
_jurisdictionSender = NULL;
_voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL;
_octreeInboundPacketProcessor = NULL;
_persistThread = NULL;
_parsedArgV = NULL;
_started = time(0);
@ -81,16 +68,39 @@ VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assign
_theInstance = this;
}
VoxelServer::~VoxelServer() {
OctreeServer::~OctreeServer() {
if (_parsedArgV) {
for (int i = 0; i < _argc; i++) {
delete[] _parsedArgV[i];
}
delete[] _parsedArgV;
}
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->terminate();
delete _octreeInboundPacketProcessor;
}
if (_persistThread) {
_persistThread->terminate();
delete _persistThread;
}
// tell our NodeList we're done with notifications
NodeList::getInstance()->removeHook(this);
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "OctreeServer::run()... DONE\n";
}
void VoxelServer::initMongoose(int port) {
void OctreeServer::initMongoose(int port) {
// setup the mongoose web server
struct mg_callbacks callbacks = {};
@ -110,10 +120,10 @@ void VoxelServer::initMongoose(int port) {
mg_start(&callbacks, NULL, options);
}
int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
int OctreeServer::civetwebRequestHandler(struct mg_connection* connection) {
const struct mg_request_info* ri = mg_get_request_info(connection);
VoxelServer* theServer = GetInstance();
OctreeServer* theServer = GetInstance();
#ifdef FORCE_CRASH
if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) {
@ -134,7 +144,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
}
if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) {
theServer->_voxelServerPacketProcessor->resetStats();
theServer->_octreeInboundPacketProcessor->resetStats();
showStats = true;
}
@ -145,7 +155,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n");
mg_printf(connection, "%s", "<html><doc>\r\n");
mg_printf(connection, "%s", "<pre>\r\n");
mg_printf(connection, "%s", "<b>Your Voxel Server is running... <a href='/'>[RELOAD]</a></b>\r\n");
mg_printf(connection, "<b>Your %s Server is running... <a href='/'>[RELOAD]</a></b>\r\n", theServer->getMyServerName());
tm* localtm = localtime(&theServer->_started);
const int MAX_TIME_LENGTH = 128;
@ -195,7 +205,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
mg_printf(connection, "Voxels Loaded At: %s", buffer);
mg_printf(connection, "%s File Loaded At: %s", theServer->getMyServerName(), buffer);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
@ -204,7 +214,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, " [%s UTM] ", buffer);
}
} else {
mg_printf(connection, "%s", "Voxel Persist Disabled...\r\n");
mg_printf(connection, "%s File Persist Disabled...\r\n", theServer->getMyServerName());
}
mg_printf(connection, "%s", "\r\n");
@ -214,7 +224,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
mg_printf(connection, "%s", "Voxels Load Took: ");
mg_printf(connection, "%s File Load Took: ", theServer->getMyServerName());
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
@ -244,9 +254,9 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "\r\n");
// display scene stats
unsigned long nodeCount = VoxelNode::getNodeCount();
unsigned long internalNodeCount = VoxelNode::getInternalNodeCount();
unsigned long leafNodeCount = VoxelNode::getLeafNodeCount();
unsigned long nodeCount = OctreeElement::getNodeCount();
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
QLocale locale(QLocale::English);
const float AS_PERCENT = 100.0;
@ -264,13 +274,13 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display outbound packet stats
mg_printf(connection, "%s", "<b>Voxel Packet Statistics...</b>\r\n");
uint64_t totalOutboundPackets = VoxelSendThread::_totalPackets;
uint64_t totalOutboundBytes = VoxelSendThread::_totalBytes;
uint64_t totalWastedBytes = VoxelSendThread::_totalWastedBytes;
uint64_t totalBytesOfOctalCodes = VoxelPacketData::getTotalBytesOfOctalCodes();
uint64_t totalBytesOfBitMasks = VoxelPacketData::getTotalBytesOfBitMasks();
uint64_t totalBytesOfColor = VoxelPacketData::getTotalBytesOfColor();
mg_printf(connection, "<b>%s Outbound Packet Statistics...</b>\r\n", theServer->getMyServerName());
uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
uint64_t totalOutboundBytes = OctreeSendThread::_totalBytes;
uint64_t totalWastedBytes = OctreeSendThread::_totalWastedBytes;
uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
const int COLUMN_WIDTH = 10;
mg_printf(connection, " Total Outbound Packets: %s packets\r\n",
@ -293,36 +303,37 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "\r\n");
// display inbound packet stats
mg_printf(connection, "%s", "<b>Voxel Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n");
uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerVoxel();
uint64_t averageLockWaitTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerVoxel();
uint64_t totalVoxelsProcessed = theServer->_voxelServerPacketProcessor->getTotalVoxelsProcessed();
uint64_t totalPacketsProcessed = theServer->_voxelServerPacketProcessor->getTotalPacketsProcessed();
mg_printf(connection, "<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
theServer->getMyServerName());
uint64_t averageTransitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
uint64_t averageLockWaitTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
uint64_t totalElementsProcessed = theServer->_octreeInboundPacketProcessor->getTotalElementsProcessed();
uint64_t totalPacketsProcessed = theServer->_octreeInboundPacketProcessor->getTotalPacketsProcessed();
float averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Inbound Voxels: %s voxels\r\n",
locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
mg_printf(connection, " Total Inbound Elements: %s elements\r\n",
locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Element: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Element: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = theServer->_voxelServerPacketProcessor->getSingleSenderStats();
NodeToSenderStatsMap& allSenderStats = theServer->_octreeInboundPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++;
QUuid senderID = i->first;
@ -334,28 +345,28 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
averageProcessTimePerVoxel = senderStats.getAverageProcessTimePerVoxel();
averageLockWaitTimePerVoxel = senderStats.getAverageLockWaitTimePerVoxel();
totalVoxelsProcessed = senderStats.getTotalVoxelsProcessed();
averageProcessTimePerElement = senderStats.getAverageProcessTimePerElement();
averageLockWaitTimePerElement = senderStats.getAverageLockWaitTimePerElement();
totalElementsProcessed = senderStats.getTotalElementsProcessed();
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Inbound Voxels: %s voxels\r\n",
locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
mg_printf(connection, " Total Inbound Elements: %s elements\r\n",
locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Element: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Element: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
}
@ -365,14 +376,14 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display memory usage stats
mg_printf(connection, "%s", "<b>Current Memory Usage Statistics</b>\r\n");
mg_printf(connection, "\r\nVoxelNode size... %ld bytes\r\n", sizeof(VoxelNode));
mg_printf(connection, "\r\nOctreeElement size... %ld bytes\r\n", sizeof(OctreeElement));
mg_printf(connection, "%s", "\r\n");
const char* memoryScaleLabel;
const float MEGABYTES = 1000000.f;
const float GIGABYTES = 1000000000.f;
float memoryScale;
if (VoxelNode::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
if (OctreeElement::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
memoryScaleLabel = "MB";
memoryScale = MEGABYTES;
} else {
@ -380,24 +391,24 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
memoryScale = GIGABYTES;
}
mg_printf(connection, "Voxel Node Memory Usage: %8.2f %s\r\n",
VoxelNode::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "Element Node Memory Usage: %8.2f %s\r\n",
OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "Octcode Memory Usage: %8.2f %s\r\n",
VoxelNode::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "External Children Memory Usage: %8.2f %s\r\n",
VoxelNode::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
OctreeElement::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "%s", " -----------\r\n");
mg_printf(connection, " Total: %8.2f %s\r\n",
VoxelNode::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "VoxelNode Children Population Statistics...\r\n");
mg_printf(connection, "%s", "OctreeElement Children Population Statistics...\r\n");
checkSum = 0;
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
checkSum += VoxelNode::getChildrenCount(i);
checkSum += OctreeElement::getChildrenCount(i);
mg_printf(connection, " Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
locale.toString((uint)VoxelNode::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
((float)VoxelNode::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
}
mg_printf(connection, "%s", " ----------------------\r\n");
mg_printf(connection, " Total: %s nodes\r\n",
@ -405,30 +416,30 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
#ifdef BLENDED_UNION_CHILDREN
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "VoxelNode Children Encoding Statistics...\r\n");
mg_printf(connection, "%s", "OctreeElement Children Encoding Statistics...\r\n");
mg_printf(connection, " Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getSingleChildrenCount(), ((float)VoxelNode::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getSingleChildrenCount(), ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getTwoChildrenOffsetCount(),
((float)VoxelNode::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getTwoChildrenOffsetCount(),
((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getTwoChildrenExternalCount(),
((float)VoxelNode::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getTwoChildrenExternalCount(),
((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getThreeChildrenOffsetCount(),
((float)VoxelNode::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getThreeChildrenOffsetCount(),
((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getThreeChildrenExternalCount(),
((float)VoxelNode::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getThreeChildrenExternalCount(),
((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
VoxelNode::getExternalChildrenCount(),
((float)VoxelNode::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
OctreeElement::getExternalChildrenCount(),
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
checkSum = VoxelNode::getSingleChildrenCount() +
VoxelNode::getTwoChildrenOffsetCount() + VoxelNode::getTwoChildrenExternalCount() +
VoxelNode::getThreeChildrenOffsetCount() + VoxelNode::getThreeChildrenExternalCount() +
VoxelNode::getExternalChildrenCount();
checkSum = OctreeElement::getSingleChildrenCount() +
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() +
OctreeElement::getExternalChildrenCount();
mg_printf(connection, "%s", " ----------------\r\n");
mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum);
@ -437,9 +448,9 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "In other news....\r\n");
mg_printf(connection, "could store 4 children internally: %10.llu nodes\r\n",
VoxelNode::getCouldStoreFourChildrenInternally());
OctreeElement::getCouldStoreFourChildrenInternally());
mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n",
VoxelNode::getCouldNotStoreFourChildrenInternally());
OctreeElement::getCouldNotStoreFourChildrenInternally());
#endif
mg_printf(connection, "%s", "\r\n");
@ -456,18 +467,18 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
}
void VoxelServer::setArguments(int argc, char** argv) {
void OctreeServer::setArguments(int argc, char** argv) {
_argc = argc;
_argv = const_cast<const char**>(argv);
qDebug("VoxelServer::setArguments()\n");
qDebug("OctreeServer::setArguments()\n");
for (int i = 0; i < _argc; i++) {
qDebug("_argv[%d]=%s\n", i, _argv[i]);
}
}
void VoxelServer::parsePayload() {
void OctreeServer::parsePayload() {
if (getNumPayloadBytes() > 0) {
QString config((const char*) _payload);
@ -477,7 +488,7 @@ void VoxelServer::parsePayload() {
int argCount = configList.size() + 1;
qDebug("VoxelServer::parsePayload()... argCount=%d\n",argCount);
qDebug("OctreeServer::parsePayload()... argCount=%d\n",argCount);
_parsedArgV = new char*[argCount];
const char* dummy = "config-from-payload";
@ -488,26 +499,73 @@ void VoxelServer::parsePayload() {
QString configItem = configList.at(i-1);
_parsedArgV[i] = new char[configItem.length() + sizeof(char)];
strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData());
qDebug("VoxelServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
qDebug("OctreeServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
}
setArguments(argCount, _parsedArgV);
}
}
//int main(int argc, const char * argv[]) {
void VoxelServer::run() {
void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
NodeList* nodeList = NodeList::getInstance();
const char VOXEL_SERVER_LOGGING_TARGET_NAME[] = "voxel-server";
PACKET_TYPE packetType = dataByteArray[0];
if (packetType == getMyQueryMessageType()) {
bool debug = false;
if (debug) {
qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow());
}
int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data());
// If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
// need to make sure we have it in our nodeList.
QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesPacketHeader,
NUM_BYTES_RFC4122_UUID));
Node* node = nodeList->nodeWithUUID(nodeUUID);
if (node) {
nodeList->updateNodeWithData(node, senderSockAddr, (unsigned char *) dataByteArray.data(),
dataByteArray.size());
if (!node->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
// this means they've heard from us and can reply, let's assume public is active
node->activatePublicSocket();
}
OctreeQueryNode* nodeData = (OctreeQueryNode*) node->getLinkedData();
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
nodeData->initializeOctreeSendThread(this);
}
}
} else if (_jurisdictionSender && packetType == PACKET_TYPE_JURISDICTION_REQUEST) {
_jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
_octreeInboundPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
}
}
void OctreeServer::run() {
// Before we do anything else, create our tree...
_tree = createTree();
// change the logging target name while this is running
Logging::setTargetName(VOXEL_SERVER_LOGGING_TARGET_NAME);
Logging::setTargetName(getMyLoggingServerTargetName());
// Now would be a good time to parse our arguments, if we got them as assignment
if (getNumPayloadBytes() > 0) {
parsePayload();
}
beforeRun(); // after payload has been processed
qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort";
@ -544,27 +602,8 @@ void VoxelServer::run() {
}
}
// should we send environments? Default is yes, but this command line suppresses sending
const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove";
_dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE);
qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove));
// should we send environments? Default is yes, but this command line suppresses sending
const char* SEND_ENVIRONMENTS = "--sendEnvironments";
bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS);
if (dontSendEnvironments) {
qDebug("Sending environments suppressed...\n");
_sendEnvironments = false;
} else {
// should we send environments? Default is yes, but this command line suppresses sending
const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment";
_sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT);
qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment));
}
qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments));
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_VOXEL_SERVER);
nodeList->setOwnerType(getMyNodeType());
// we need to ask the DS about agents so we can ping/reply with them
const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER};
@ -573,69 +612,52 @@ void VoxelServer::run() {
setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications
nodeList->addHook(&_nodeWatcher);
nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode;
nodeList->addHook(this);
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
nodeList->startSilentNodeRemovalThread();
srand((unsigned)time(0));
const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats";
_displayVoxelStats = cmdOptionExists(_argc, _argv, DISPLAY_VOXEL_STATS);
qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats));
const char* VERBOSE_DEBUG = "--verboseDebug";
_verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG);
qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug));
const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
_debugVoxelSending = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_SENDING);
qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending));
const char* DEBUG_SENDING = "--debugSending";
_debugSending = cmdOptionExists(_argc, _argv, DEBUG_SENDING);
qDebug("debugSending=%s\n", debug::valueOf(_debugSending));
const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving";
_debugVoxelReceiving = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_RECEIVING);
qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving));
const char* DEBUG_RECEIVING = "--debugReceiving";
_debugReceiving = cmdOptionExists(_argc, _argv, DEBUG_RECEIVING);
qDebug("debugReceiving=%s\n", debug::valueOf(_debugReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
_shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG);
qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
// By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if (cmdOptionExists(_argc, _argv, NO_VOXEL_PERSIST)) {
_wantVoxelPersist = false;
// By default we will persist, if you want to disable this, then pass in this parameter
const char* NO_PERSIST = "--NoPersist";
if (cmdOptionExists(_argc, _argv, NO_PERSIST)) {
_wantPersist = false;
}
qDebug("wantVoxelPersist=%s\n", debug::valueOf(_wantVoxelPersist));
qDebug("wantPersist=%s\n", debug::valueOf(_wantPersist));
// if we want Voxel Persistence, set up the local file and persist thread
if (_wantVoxelPersist) {
// if we want Persistence, set up the local file and persist thread
if (_wantPersist) {
// Check to see if the user passed in a command line option for setting packet send rate
const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename";
const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME);
if (voxelsPersistFilenameParameter) {
strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter);
const char* PERSIST_FILENAME = "--persistFilename";
const char* persistFilenameParameter = getCmdOption(_argc, _argv, PERSIST_FILENAME);
if (persistFilenameParameter) {
strcpy(_persistFilename, persistFilenameParameter);
} else {
//strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
strcpy(_voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE);
strcpy(_persistFilename, getMyDefaultPersistFilename());
}
qDebug("voxelPersistFilename=%s\n", _voxelPersistFilename);
qDebug("persistFilename=%s\n", _persistFilename);
// now set up VoxelPersistThread
_voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename);
if (_voxelPersistThread) {
_voxelPersistThread->initialize(true);
// now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistFilename);
if (_persistThread) {
_persistThread->initialize(true);
}
}
// Check to see if the user passed in a command line option for loading an old style local
// Voxel File. If so, load it now. This is not the same as a voxel persist file
const char* INPUT_FILE = "-i";
const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE);
if (voxelsFilename) {
_serverTree.readFromSVOFile(voxelsFilename);
}
// Check to see if the user passed in a command line option for setting packet send rate
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
@ -647,12 +669,7 @@ void VoxelServer::run() {
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
}
sockaddr senderAddress;
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t packetLength;
timeval lastDomainServerCheckIn = {};
HifiSockAddr senderSockAddr;
// set up our jurisdiction broadcaster...
_jurisdictionSender = new JurisdictionSender(_jurisdiction);
@ -660,10 +677,10 @@ void VoxelServer::run() {
_jurisdictionSender->initialize(true);
}
// set up our VoxelServerPacketProcessor
_voxelServerPacketProcessor = new VoxelServerPacketProcessor(this);
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->initialize(true);
// set up our OctreeServerPacketProcessor
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->initialize(true);
}
// Convert now to tm struct for local timezone
@ -679,110 +696,15 @@ void VoxelServer::run() {
}
qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n";
QTimer* domainServerTimer = new QTimer(this);
connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit()));
domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000);
// loop to send to nodes requesting data
while (true) {
// check for >= in case one gets past the goalie
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
break;
}
// send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed
if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) {
gettimeofday(&lastDomainServerCheckIn, NULL);
NodeList::getInstance()->sendDomainServerCheckIn();
}
// ping our inactive nodes to punch holes with them
nodeList->possiblyPingInactiveNodes();
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
packetVersionMatch(packetData)) {
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_VOXEL_QUERY) {
bool debug = false;
if (debug) {
qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow());
}
// If we got a PACKET_TYPE_VOXEL_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
// need to make sure we have it in our nodeList.
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
NUM_BYTES_RFC4122_UUID));
Node* node = nodeList->nodeWithUUID(nodeUUID);
if (node) {
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
if (!node->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
// this means they've heard from us and can reply, let's assume public is active
node->activatePublicSocket();
}
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
nodeData->initializeVoxelSendThread(this);
}
}
} else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
if (_jurisdictionSender) {
_jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
}
} else if (_voxelServerPacketProcessor &&
(packetData[0] == PACKET_TYPE_SET_VOXEL
|| packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE
|| packetData[0] == PACKET_TYPE_ERASE_VOXEL)) {
const char* messageName;
switch (packetData[0]) {
case PACKET_TYPE_SET_VOXEL:
messageName = "PACKET_TYPE_SET_VOXEL";
break;
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE";
break;
case PACKET_TYPE_ERASE_VOXEL:
messageName = "PACKET_TYPE_ERASE_VOXEL";
break;
}
_voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
}
}
}
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
// properly shutdown and cleaned up.
NodeList::getInstance()->clear();
QTimer* silentNodeTimer = new QTimer(this);
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor;
}
if (_voxelPersistThread) {
_voxelPersistThread->terminate();
delete _voxelPersistThread;
}
// tell our NodeList we're done with notifications
nodeList->removeHook(&_nodeWatcher);
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "VoxelServer::run()... DONE\n";
QTimer* pingNodesTimer = new QTimer(this);
connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes()));
pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000);
}

View file

@ -0,0 +1,98 @@
//
// OctreeServer.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __octree_server__OctreeServer__
#define __octree_server__OctreeServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <ThreadedAssignment.h>
#include <EnvironmentData.h>
#include "OctreePersistThread.h"
#include "OctreeSendThread.h"
#include "OctreeServerConsts.h"
#include "OctreeInboundPacketProcessor.h"
/// Handles assignments of type OctreeServer - sending octrees to various clients.
class OctreeServer : public ThreadedAssignment, public NodeListHook {
public:
OctreeServer(const unsigned char* dataBuffer, int numBytes);
~OctreeServer();
/// allows setting of run arguments
void setArguments(int argc, char** argv);
bool wantsDebugSending() const { return _debugSending; }
bool wantsDebugReceiving() const { return _debugReceiving; }
bool wantsVerboseDebug() const { return _verboseDebug; }
Octree* getOctree() { return _tree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
static OctreeServer* GetInstance() { return _theInstance; }
bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; }
time_t* getLoadCompleted() { return (_persistThread) ? _persistThread->getLoadCompleted() : NULL; }
uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode) = 0;
virtual Octree* createTree() = 0;
virtual unsigned char getMyNodeType() const = 0;
virtual PACKET_TYPE getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
// subclass may implement these method
virtual void beforeRun() { };
virtual bool hasSpecialPacketToSend() { return false; }
virtual int sendSpecialPacket(Node* node) { return 0; }
static void attachQueryNodeToNode(Node* newNode);
// NodeListHook
virtual void nodeAdded(Node* node);
virtual void nodeKilled(Node* node);
public slots:
/// runs the voxel server assignment
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
protected:
int _argc;
const char** _argv;
char** _parsedArgV;
char _persistFilename[MAX_FILENAME_LENGTH];
int _packetsPerClientPerInterval;
Octree* _tree; // this IS a reaveraging tree
bool _wantPersist;
bool _debugSending;
bool _debugReceiving;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
OctreePersistThread* _persistThread;
void parsePayload();
void initMongoose(int port);
static int civetwebRequestHandler(struct mg_connection *connection);
static OctreeServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
};
#endif // __octree_server__OctreeServer__

View file

@ -0,0 +1,21 @@
// OctreeServerConsts.h
// octree-server
//
// Created by Brad Hefta-Gaub on 12/4/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __octree_server__OctreeServerConsts__
#define __octree_server__OctreeServerConsts__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
const int MAX_FILENAME_LENGTH = 1024;
const int INTERVALS_PER_SECOND = 60;
const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
#endif // __octree_server__OctreeServerConsts__

View file

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ../..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
set(TARGET_NAME octree)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Widgets)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})

View file

@ -151,7 +151,7 @@ int CoverageMap::getPolygonCount() const {
_remainder.getPolygonCount());
}
VoxelProjectedPolygon* CoverageMap::getPolygon(int index) const {
OctreeProjectedPolygon* CoverageMap::getPolygon(int index) const {
int base = 0;
if ((index - base) < _topHalf.getPolygonCount()) {
return _topHalf.getPolygon((index - base));
@ -182,7 +182,7 @@ VoxelProjectedPolygon* CoverageMap::getPolygon(int index) const {
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
CoverageMapStorageResult CoverageMap::checkMap(VoxelProjectedPolygon* polygon, bool storeIt) {
CoverageMapStorageResult CoverageMap::checkMap(OctreeProjectedPolygon* polygon, bool storeIt) {
if (_isRoot) {
_checkMapRootCalls++;
@ -371,13 +371,13 @@ void CoverageRegion::erase() {
}
void CoverageRegion::growPolygonArray() {
VoxelProjectedPolygon** newPolygons = new VoxelProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
OctreeProjectedPolygon** newPolygons = new OctreeProjectedPolygon*[_polygonArraySize + DEFAULT_GROW_SIZE];
float* newDistances = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
float* newSizes = new float[_polygonArraySize + DEFAULT_GROW_SIZE];
if (_polygons) {
memcpy(newPolygons, _polygons, sizeof(VoxelProjectedPolygon*) * _polygonCount);
memcpy(newPolygons, _polygons, sizeof(OctreeProjectedPolygon*) * _polygonCount);
delete[] _polygons;
memcpy(newDistances, _polygonDistances, sizeof(float) * _polygonCount);
delete[] _polygonDistances;
@ -418,9 +418,9 @@ int CoverageRegion::_outOfOrderPolygon = 0;
int CoverageRegion::_clippedPolygons = 0;
bool CoverageRegion::mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray) {
bool CoverageRegion::mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray) {
for (int i = 0; i < _polygonCount; i++) {
VoxelProjectedPolygon* otherPolygon = _polygons[i];
OctreeProjectedPolygon* otherPolygon = _polygons[i];
if (otherPolygon->canMerge(*seed)) {
otherPolygon->merge(*seed);
@ -451,7 +451,7 @@ bool CoverageRegion::mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInA
// just handles storage in the array, doesn't test for occlusion or
// determining if this is the correct map to store in!
void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) {
void CoverageRegion::storeInArray(OctreeProjectedPolygon* polygon) {
_currentCoveredBounds.explandToInclude(polygon->getBoundingBox());
@ -504,7 +504,7 @@ void CoverageRegion::storeInArray(VoxelProjectedPolygon* polygon) {
CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt) {
CoverageMapStorageResult CoverageRegion::checkRegion(OctreeProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt) {
CoverageMapStorageResult result = DOESNT_FIT;
@ -517,7 +517,7 @@ CoverageMapStorageResult CoverageRegion::checkRegion(VoxelProjectedPolygon* poly
} else {
// check to make sure this polygon isn't occluded by something at this level
for (int i = 0; i < _polygonCount; i++) {
VoxelProjectedPolygon* polygonAtThisLevel = _polygons[i];
OctreeProjectedPolygon* polygonAtThisLevel = _polygons[i];
// Check to make sure that the polygon in question is "behind" the polygon in the list
// otherwise, we don't need to test it's occlusion (although, it means we've potentially

View file

@ -1,5 +1,5 @@
//
// CoverageMap.h - 2D CoverageMap Quad tree for storage of VoxelProjectedPolygons
// CoverageMap.h - 2D CoverageMap Quad tree for storage of OctreeProjectedPolygons
// hifi
//
// Added by Brad Hefta-Gaub on 06/11/13.
@ -10,7 +10,7 @@
#define _COVERAGE_MAP_
#include <glm/glm.hpp>
#include "VoxelProjectedPolygon.h"
#include "OctreeProjectedPolygon.h"
typedef enum {STORED, OCCLUDED, DOESNT_FIT, NOT_STORED} CoverageMapStorageResult;
typedef enum {TOP_HALF, BOTTOM_HALF, LEFT_HALF, RIGHT_HALF, REMAINDER} RegionName;
@ -22,8 +22,8 @@ public:
CoverageRegion(BoundingBox boundingBox, bool isRoot, bool managePolygons = true, RegionName regionName = REMAINDER);
~CoverageRegion();
CoverageMapStorageResult checkRegion(VoxelProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt);
void storeInArray(VoxelProjectedPolygon* polygon);
CoverageMapStorageResult checkRegion(OctreeProjectedPolygon* polygon, const BoundingBox& polygonBox, bool storeIt);
void storeInArray(OctreeProjectedPolygon* polygon);
bool contains(const BoundingBox& box) const { return _myBoundingBox.contains(box); };
void erase(); // erase the coverage region
@ -41,7 +41,7 @@ public:
const char* getRegionName() const;
int getPolygonCount() const { return _polygonCount; };
VoxelProjectedPolygon* getPolygon(int index) const { return _polygons[index]; };
OctreeProjectedPolygon* getPolygon(int index) const { return _polygons[index]; };
private:
void init();
@ -53,7 +53,7 @@ private:
RegionName _regionName;
int _polygonCount; // how many polygons at this level
int _polygonArraySize; // how much room is there to store polygons at this level
VoxelProjectedPolygon** _polygons;
OctreeProjectedPolygon** _polygons;
// we will use one or the other of these depending on settings in the code.
float* _polygonDistances;
@ -61,7 +61,7 @@ private:
void growPolygonArray();
static const int DEFAULT_GROW_SIZE = 100;
bool mergeItemsInArray(VoxelProjectedPolygon* seed, bool seedInArray);
bool mergeItemsInArray(OctreeProjectedPolygon* seed, bool seedInArray);
};
@ -77,7 +77,7 @@ public:
CoverageMap(BoundingBox boundingBox = ROOT_BOUNDING_BOX, bool isRoot = IS_ROOT, bool managePolygons = true);
~CoverageMap();
CoverageMapStorageResult checkMap(VoxelProjectedPolygon* polygon, bool storeIt = true);
CoverageMapStorageResult checkMap(OctreeProjectedPolygon* polygon, bool storeIt = true);
BoundingBox getChildBoundingBox(int childIndex);
@ -87,7 +87,7 @@ public:
static bool wantDebugging;
int getPolygonCount() const;
VoxelProjectedPolygon* getPolygon(int index) const;
OctreeProjectedPolygon* getPolygon(int index) const;
CoverageMap* getChild(int childIndex) const { return _childMaps[childIndex]; };
private:

View file

@ -114,7 +114,7 @@ BoundingBox CoverageMapV2::getChildBoundingBox(int childIndex) {
}
// possible results = STORED/NOT_STORED, OCCLUDED, DOESNT_FIT
CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon* polygon, bool storeIt) {
CoverageMapV2StorageResult CoverageMapV2::checkMap(const OctreeProjectedPolygon* polygon, bool storeIt) {
assert(_isRoot); // you can only call this on the root map!!!
_checkMapRootCalls++;
@ -152,7 +152,7 @@ CoverageMapV2StorageResult CoverageMapV2::checkMap(const VoxelProjectedPolygon*
return V2_NOT_STORED; // unless we weren't asked to store it, then we didn't
}
void CoverageMapV2::recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
void CoverageMapV2::recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt,
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered) {
// if we are really small, then we act like we don't intersect, this allows us to stop

View file

@ -1,5 +1,5 @@
//
// CoverageMapV2.h - 2D CoverageMapV2 Quad tree for storage of VoxelProjectedPolygons
// CoverageMapV2.h - 2D CoverageMapV2 Quad tree for storage of OctreeProjectedPolygons
// hifi
//
// Added by Brad Hefta-Gaub on 06/11/13.
@ -11,7 +11,7 @@
#include <glm/glm.hpp>
#include "VoxelProjectedPolygon.h"
#include "OctreeProjectedPolygon.h"
typedef enum {
V2_DOESNT_FIT, V2_STORED, V2_NOT_STORED,
@ -35,7 +35,7 @@ public:
bool isCovered = false, float coverageDistance = NOT_COVERED);
~CoverageMapV2();
CoverageMapV2StorageResult checkMap(const VoxelProjectedPolygon* polygon, bool storeIt = true);
CoverageMapV2StorageResult checkMap(const OctreeProjectedPolygon* polygon, bool storeIt = true);
BoundingBox getChildBoundingBox(int childIndex);
const BoundingBox& getBoundingBox() const { return _myBoundingBox; };
@ -48,7 +48,7 @@ public:
private:
void recurseMap(const VoxelProjectedPolygon* polygon, bool storeIt,
void recurseMap(const OctreeProjectedPolygon* polygon, bool storeIt,
bool& seenOccludedMapNodes, bool& allOccludedMapNodesCovered);
void init();

View file

@ -42,13 +42,13 @@ void JurisdictionListener::nodeKilled(Node* node) {
bool JurisdictionListener::queueJurisdictionRequest() {
static unsigned char buffer[MAX_PACKET_SIZE];
unsigned char* bufferOut = &buffer[0];
ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_VOXEL_JURISDICTION_REQUEST);
ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_JURISDICTION_REQUEST);
int nodeCount = 0;
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) {
sockaddr* nodeAddress = node->getActiveSocket();
const HifiSockAddr* nodeAddress = node->getActiveSocket();
PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++;
}
@ -64,9 +64,9 @@ bool JurisdictionListener::queueJurisdictionRequest() {
return isStillRunning();
}
void JurisdictionListener::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
if (node) {
QUuid nodeUUID = node->getUUID();
JurisdictionMap map;

View file

@ -17,8 +17,8 @@
#include "JurisdictionMap.h"
/// Sends out PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
/// the PACKET_TYPE_VOXEL_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
/// Sends out PACKET_TYPE_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
/// the PACKET_TYPE_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
/// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
/// and adding them to the processing queue by calling queueReceivedPacket()
class JurisdictionListener : public NodeListHook, public PacketSender, public ReceivedPacketProcessor {
@ -39,13 +39,13 @@ public:
void nodeKilled(Node* node);
protected:
/// Callback for processing of received packets. Will process any queued PACKET_TYPE_VOXEL_JURISDICTION and update the
/// Callback for processing of received packets. Will process any queued PACKET_TYPE_JURISDICTION and update the
/// jurisdiction map member variable
/// \param sockaddr& senderAddress the address of the sender
/// \param packetData pointer to received data
/// \param ssize_t packetLength size of received data
/// \thread "this" individual processing thread
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
private:
NodeToJurisdictionMap _jurisdictions;

View file

@ -12,9 +12,9 @@
#include <QDebug>
#include <PacketHeaders.h>
#include <OctalCode.h>
#include "JurisdictionMap.h"
#include "VoxelNode.h"
// standard assignment
@ -263,7 +263,7 @@ bool JurisdictionMap::writeToFile(const char* filename) {
int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
unsigned char* bufferStart = destinationBuffer;
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION);
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
destinationBuffer += headerLength;
// No root or end node details to pack!
@ -277,7 +277,7 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destination
int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
unsigned char* bufferStart = destinationBuffer;
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION);
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
destinationBuffer += headerLength;
// add the root jurisdiction

View file

@ -29,9 +29,9 @@ JurisdictionSender::~JurisdictionSender() {
}
void JurisdictionSender::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_JURISDICTION_REQUEST) {
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
if (node) {
QUuid nodeUUID = node->getUUID();
lockRequestingNodes();
@ -66,7 +66,7 @@ bool JurisdictionSender::process() {
Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
if (node->getActiveSocket() != NULL) {
sockaddr* nodeAddress = node->getActiveSocket();
const HifiSockAddr* nodeAddress = node->getActiveSocket();
queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++;
}

View file

@ -5,7 +5,7 @@
// Created by Brad Hefta-Gaub on 8/12/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Voxel Packet Sender
// Jurisdiction Sender
//
#ifndef __shared__JurisdictionSender__
@ -17,7 +17,7 @@
#include <ReceivedPacketProcessor.h>
#include "JurisdictionMap.h"
/// Will process PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets and send out PACKET_TYPE_VOXEL_JURISDICTION packets
/// Will process PACKET_TYPE_JURISDICTION_REQUEST packets and send out PACKET_TYPE_JURISDICTION packets
/// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
/// and adding them to the processing queue by calling queueReceivedPacket()
class JurisdictionSender : public PacketSender, public ReceivedPacketProcessor {
@ -32,7 +32,7 @@ public:
virtual bool process();
protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
virtual void processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
/// Locks all the resources of the thread.
void lockRequestingNodes() { pthread_mutex_lock(&_requestingNodeMutex); }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,314 @@
//
// Octree.h
// hifi
//
// Created by Stephen Birarda on 3/13/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__Octree__
#define __hifi__Octree__
#include <set>
#include <SimpleMovingAverage.h>
//#include "CoverageMap.h"
class CoverageMap;
class ReadBitstreamToTreeParams;
class Octree;
class OctreeElement;
class OctreeElementBag;
class OctreePacketData;
#include "JurisdictionMap.h"
#include "ViewFrustum.h"
#include "OctreeElement.h"
#include "OctreeElementBag.h"
#include "OctreePacketData.h"
#include "OctreeSceneStats.h"
#include <QObject>
#include <QReadWriteLock>
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* node, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
const bool NO_COLOR = false;
const bool WANT_COLOR = true;
const bool COLLAPSE_EMPTY_TREE = true;
const bool DONT_COLLAPSE = false;
const bool NO_OCCLUSION_CULLING = false;
const bool WANT_OCCLUSION_CULLING = true;
const int DONT_CHOP = 0;
const int NO_BOUNDARY_ADJUST = 0;
const int LOW_RES_MOVING_ADJUST = 1;
const uint64_t IGNORE_LAST_SENT = 0;
#define IGNORE_SCENE_STATS NULL
#define IGNORE_VIEW_FRUSTUM NULL
#define IGNORE_COVERAGE_MAP NULL
#define IGNORE_JURISDICTION_MAP NULL
class EncodeBitstreamParams {
public:
int maxEncodeLevel;
int maxLevelReached;
const ViewFrustum* viewFrustum;
bool includeColor;
bool includeExistsBits;
int chopLevels;
bool deltaViewFrustum;
const ViewFrustum* lastViewFrustum;
bool wantOcclusionCulling;
int boundaryLevelAdjust;
float octreeElementSizeScale;
uint64_t lastViewFrustumSent;
bool forceSendScene;
OctreeSceneStats* stats;
CoverageMap* map;
JurisdictionMap* jurisdictionMap;
// output hints from the encode process
typedef enum {
UNKNOWN,
DIDNT_FIT,
NULL_NODE,
TOO_DEEP,
OUT_OF_JURISDICTION,
LOD_SKIP,
OUT_OF_VIEW,
WAS_IN_VIEW,
NO_CHANGE,
OCCLUDED
} reason;
reason stopReason;
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
int chopLevels = 0,
bool deltaViewFrustum = false,
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
bool wantOcclusionCulling = NO_OCCLUSION_CULLING,
CoverageMap* map = IGNORE_COVERAGE_MAP,
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
uint64_t lastViewFrustumSent = IGNORE_LAST_SENT,
bool forceSendScene = true,
OctreeSceneStats* stats = IGNORE_SCENE_STATS,
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) :
maxEncodeLevel(maxEncodeLevel),
maxLevelReached(0),
viewFrustum(viewFrustum),
includeColor(includeColor),
includeExistsBits(includeExistsBits),
chopLevels(chopLevels),
deltaViewFrustum(deltaViewFrustum),
lastViewFrustum(lastViewFrustum),
wantOcclusionCulling(wantOcclusionCulling),
boundaryLevelAdjust(boundaryLevelAdjust),
octreeElementSizeScale(octreeElementSizeScale),
lastViewFrustumSent(lastViewFrustumSent),
forceSendScene(forceSendScene),
stats(stats),
map(map),
jurisdictionMap(jurisdictionMap),
stopReason(UNKNOWN)
{}
void displayStopReason() {
printf("StopReason: ");
switch (stopReason) {
default:
case UNKNOWN: printf("UNKNOWN\n"); break;
case DIDNT_FIT: printf("DIDNT_FIT\n"); break;
case NULL_NODE: printf("NULL_NODE\n"); break;
case TOO_DEEP: printf("TOO_DEEP\n"); break;
case OUT_OF_JURISDICTION: printf("OUT_OF_JURISDICTION\n"); break;
case LOD_SKIP: printf("LOD_SKIP\n"); break;
case OUT_OF_VIEW: printf("OUT_OF_VIEW\n"); break;
case WAS_IN_VIEW: printf("WAS_IN_VIEW\n"); break;
case NO_CHANGE: printf("NO_CHANGE\n"); break;
case OCCLUDED: printf("OCCLUDED\n"); break;
}
}
};
class ReadElementBufferToTreeArgs {
public:
const unsigned char* buffer;
int length;
bool destructive;
bool pathChanged;
};
class ReadBitstreamToTreeParams {
public:
bool includeColor;
bool includeExistsBits;
OctreeElement* destinationNode;
QUuid sourceUUID;
bool wantImportProgress;
ReadBitstreamToTreeParams(
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElement* destinationNode = NULL,
QUuid sourceUUID = QUuid(),
bool wantImportProgress = false) :
includeColor(includeColor),
includeExistsBits(includeExistsBits),
destinationNode(destinationNode),
sourceUUID(sourceUUID),
wantImportProgress(wantImportProgress)
{}
};
class Octree : public QObject {
Q_OBJECT
public:
Octree(bool shouldReaverage = false);
~Octree();
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0;
virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; }
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength) { return 0; }
OctreeElement* getRoot() { return _rootNode; }
void eraseAllOctreeElements();
void processRemoveOctreeElementsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args);
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void reaverageOctreeElements(OctreeElement* startNode = NULL);
void deleteOctreeElementAt(float x, float y, float z, float s);
OctreeElement* getOctreeElementAt(float x, float y, float z, float s) const;
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData=NULL);
void recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData=NULL);
int encodeTreeBitstream(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) ;
bool isDirty() const { return _isDirty; }
void clearDirtyBit() { _isDirty = false; }
void setDirtyBit() { _isDirty = true; }
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElement*& node, float& distance, BoxFace& face);
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration);
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
// Note: this assumes the fileFormat is the HIO individual voxels code files
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* node = NULL);
bool readFromSVOFile(const char* filename);
// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const char *filename);
bool readFromSchematicFile(const char* filename);
// Octree does not currently handle its own locking, caller must use these to lock/unlock
void lockForRead() { lock.lockForRead(); }
void tryLockForRead() { lock.tryLockForRead(); }
void lockForWrite() { lock.lockForWrite(); }
void tryLockForWrite() { lock.tryLockForWrite(); }
void unlock() { lock.unlock(); }
unsigned long getOctreeElementsCount();
void copySubTreeIntoNewTree(OctreeElement* startNode, Octree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationNode);
bool getShouldReaverage() const { return _shouldReaverage; }
void recurseNodeWithOperation(OctreeElement* node, RecurseOctreeOperation operation,
void* extraData, int recursionCount = 0);
void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData, int recursionCount = 0);
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);
public slots:
void cancelImport();
protected:
void deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData);
int encodeTreeBitstreamRecursion(OctreeElement* node,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel) const;
static bool countOctreeElementsOperation(OctreeElement* node, void* extraData);
OctreeElement* nodeForOctalCode(OctreeElement* ancestorNode, const unsigned char* needleCode, OctreeElement** parentOfFoundNode) const;
OctreeElement* createMissingNode(OctreeElement* lastParentNode, const unsigned char* codeToReach);
int readNodeData(OctreeElement *destinationNode, const unsigned char* nodeData,
int bufferSizeBytes, ReadBitstreamToTreeParams& args);
OctreeElement* _rootNode;
bool _isDirty;
bool _shouldReaverage;
bool _stopImport;
/// Octal Codes of any subtrees currently being encoded. While any of these codes is being encoded, ancestors and
/// descendants of them can not be deleted.
std::set<const unsigned char*> _codesBeingEncoded;
/// mutex lock to protect the encoding set
pthread_mutex_t _encodeSetLock;
/// Called to indicate that a OctreeElement is in the process of being encoded.
void startEncoding(OctreeElement* node);
/// Called to indicate that a OctreeElement is done being encoded.
void doneEncoding(OctreeElement* node);
/// Is the Octal Code currently being deleted?
bool isEncoding(const unsigned char* codeBuffer);
/// Octal Codes of any subtrees currently being deleted. While any of these codes is being deleted, ancestors and
/// descendants of them can not be encoded.
std::set<const unsigned char*> _codesBeingDeleted;
/// mutex lock to protect the deleting set
pthread_mutex_t _deleteSetLock;
/// Called to indicate that an octal code is in the process of being deleted.
void startDeleting(const unsigned char* code);
/// Called to indicate that an octal code is done being deleted.
void doneDeleting(const unsigned char* code);
/// Octal Codes that were attempted to be deleted but couldn't be because they were actively being encoded, and were
/// instead queued for later delete
std::set<const unsigned char*> _codesPendingDelete;
/// mutex lock to protect the deleting set
pthread_mutex_t _deletePendingSetLock;
/// Adds an Octal Code to the set of codes that needs to be deleted
void queueForLaterDelete(const unsigned char* codeBuffer);
/// flushes out any Octal Codes that had to be queued
void emptyDeleteQueue();
QReadWriteLock lock;
};
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
#endif /* defined(__hifi__Octree__) */

View file

@ -0,0 +1,47 @@
//
// OctreeConstants.h
// hifi
//
// Created by Brad Hefta-Gaub on 4/29/13.
//
//
// Various important constants used throughout the system related to voxels
//
//
#ifndef __hifi_OctreeConstants_h__
#define __hifi_OctreeConstants_h__
#include <limits.h>
#include <stdint.h>
#include <OctalCode.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <glm/glm.hpp>
// this is where the coordinate system is represented
const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f);
const glm::vec3 IDENTITY_UP = glm::vec3( 0.0f, 1.0f, 0.0f);
const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f);
const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if we want to resend changed voxels
const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe
// This controls the LOD. Larger number will make smaller voxels visible at greater distance.
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f;
const float MAX_LOD_SIZE_MULTIPLIER = 2000.0f;
const int NUMBER_OF_CHILDREN = 8;
const int MAX_TREE_SLICE_BYTES = 26;
const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f;
// These are guards to prevent our voxel tree recursive routines from spinning out of control
const int UNREASONABLY_DEEP_RECURSION = 20; // use this for something that you want to be shallow, but not spin out
const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper
const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client
#endif

View file

@ -1,5 +1,5 @@
//
// VoxelNode.cpp
// OctreeElement.cpp
// hifi
//
// Created by Stephen Birarda on 3/13/13.
@ -14,36 +14,36 @@
#include <NodeList.h>
#include <PerfStat.h>
#include <assert.h>
#include "AABox.h"
#include "OctalCode.h"
#include "SharedUtil.h"
#include "VoxelConstants.h"
#include "VoxelNode.h"
#include "VoxelTree.h"
#include "OctreeConstants.h"
#include "OctreeElement.h"
#include "Octree.h"
uint64_t VoxelNode::_voxelMemoryUsage = 0;
uint64_t VoxelNode::_octcodeMemoryUsage = 0;
uint64_t VoxelNode::_externalChildrenMemoryUsage = 0;
uint64_t VoxelNode::_voxelNodeCount = 0;
uint64_t VoxelNode::_voxelNodeLeafCount = 0;
uint64_t OctreeElement::_voxelMemoryUsage = 0;
uint64_t OctreeElement::_octcodeMemoryUsage = 0;
uint64_t OctreeElement::_externalChildrenMemoryUsage = 0;
uint64_t OctreeElement::_voxelNodeCount = 0;
uint64_t OctreeElement::_voxelNodeLeafCount = 0;
VoxelNode::VoxelNode() {
unsigned char* rootCode = new unsigned char[1];
*rootCode = 0;
init(rootCode);
_voxelNodeCount++;
_voxelNodeLeafCount++; // all nodes start as leaf nodes
OctreeElement::OctreeElement() {
// Note: you must call init() from your subclass, otherwise the OctreeElement will not be properly
// initialized. You will see DEADBEEF in your memory debugger if you have not properly called init()
debug::setDeadBeef(this, sizeof(*this));
}
VoxelNode::VoxelNode(unsigned char * octalCode) {
init(octalCode);
void OctreeElement::init(unsigned char * octalCode) {
if (!octalCode) {
octalCode = new unsigned char[1];
*octalCode = 0;
}
_voxelNodeCount++;
_voxelNodeLeafCount++; // all nodes start as leaf nodes
}
void VoxelNode::init(unsigned char * octalCode) {
int octalCodeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode));
if (octalCodeLength > sizeof(_octalCode)) {
_octalCode.pointer = octalCode;
@ -55,13 +55,6 @@ void VoxelNode::init(unsigned char * octalCode) {
delete[] octalCode;
}
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
_falseColored = false; // assume true color
_currentColor[0] = _currentColor[1] = _currentColor[2] = _currentColor[3] = 0;
#endif
_trueColor[0] = _trueColor[1] = _trueColor[2] = _trueColor[3] = 0;
_density = 0.0f;
// set up the _children union
_childBitmask = 0;
_childrenExternal = false;
@ -89,24 +82,15 @@ void VoxelNode::init(unsigned char * octalCode) {
_children.single = NULL;
#endif
_unknownBufferIndex = true;
setBufferIndex(GLBUFFER_INDEX_UNKNOWN);
setVoxelSystem(NULL);
_isDirty = true;
_shouldRender = false;
_sourceUUIDKey = 0;
calculateAABox();
markWithChangedTime();
_voxelMemoryUsage += sizeof(VoxelNode);
}
VoxelNode::~VoxelNode() {
OctreeElement::~OctreeElement() {
notifyDeleteHooks();
_voxelMemoryUsage -= sizeof(VoxelNode);
_voxelNodeCount--;
if (isLeaf()) {
_voxelNodeLeafCount--;
@ -121,65 +105,32 @@ VoxelNode::~VoxelNode() {
deleteAllChildren();
}
void VoxelNode::markWithChangedTime() {
void OctreeElement::markWithChangedTime() {
_lastChanged = usecTimestampNow();
notifyUpdateHooks(); // if the node has changed, notify our hooks
}
// This method is called by VoxelTree when the subtree below this node
// This method is called by Octree when the subtree below this node
// is known to have changed. It's intended to be used as a place to do
// bookkeeping that a node may need to do when the subtree below it has
// changed. However, you should hopefully make your bookkeeping relatively
// localized, because this method will get called for every node in an
// recursive unwinding case like delete or add voxel
void VoxelNode::handleSubtreeChanged(VoxelTree* myTree) {
void OctreeElement::handleSubtreeChanged(Octree* myTree) {
// here's a good place to do color re-averaging...
if (myTree->getShouldReaverage()) {
setColorFromAverageOfChildren();
calculateAverageFromChildren();
}
markWithChangedTime();
}
const uint8_t INDEX_FOR_NULL = 0;
uint8_t VoxelNode::_nextIndex = INDEX_FOR_NULL + 1; // start at 1, 0 is reserved for NULL
std::map<VoxelSystem*, uint8_t> VoxelNode::_mapVoxelSystemPointersToIndex;
std::map<uint8_t, VoxelSystem*> VoxelNode::_mapIndexToVoxelSystemPointers;
VoxelSystem* VoxelNode::getVoxelSystem() const {
if (_voxelSystemIndex > INDEX_FOR_NULL) {
if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) {
VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex];
return voxelSystem;
}
}
return NULL;
}
void VoxelNode::setVoxelSystem(VoxelSystem* voxelSystem) {
if (voxelSystem == NULL) {
_voxelSystemIndex = INDEX_FOR_NULL;
} else {
uint8_t index;
if (_mapVoxelSystemPointersToIndex.end() != _mapVoxelSystemPointersToIndex.find(voxelSystem)) {
index = _mapVoxelSystemPointersToIndex[voxelSystem];
} else {
index = _nextIndex;
_nextIndex++;
_mapVoxelSystemPointersToIndex[voxelSystem] = index;
_mapIndexToVoxelSystemPointers[index] = voxelSystem;
}
_voxelSystemIndex = index;
}
}
const uint16_t KEY_FOR_NULL = 0;
uint16_t VoxelNode::_nextUUIDKey = KEY_FOR_NULL + 1; // start at 1, 0 is reserved for NULL
std::map<QString, uint16_t> VoxelNode::_mapSourceUUIDsToKeys;
std::map<uint16_t, QString> VoxelNode::_mapKeysToSourceUUIDs;
uint16_t OctreeElement::_nextUUIDKey = KEY_FOR_NULL + 1; // start at 1, 0 is reserved for NULL
std::map<QString, uint16_t> OctreeElement::_mapSourceUUIDsToKeys;
std::map<uint16_t, QString> OctreeElement::_mapKeysToSourceUUIDs;
void VoxelNode::setSourceUUID(const QUuid& sourceUUID) {
void OctreeElement::setSourceUUID(const QUuid& sourceUUID) {
uint16_t key;
QString sourceUUIDString = sourceUUID.toString();
if (_mapSourceUUIDsToKeys.end() != _mapSourceUUIDsToKeys.find(sourceUUIDString)) {
@ -193,7 +144,7 @@ void VoxelNode::setSourceUUID(const QUuid& sourceUUID) {
_sourceUUIDKey = key;
}
QUuid VoxelNode::getSourceUUID() const {
QUuid OctreeElement::getSourceUUID() const {
if (_sourceUUIDKey > KEY_FOR_NULL) {
if (_mapKeysToSourceUUIDs.end() != _mapKeysToSourceUUIDs.find(_sourceUUIDKey)) {
return QUuid(_mapKeysToSourceUUIDs[_sourceUUIDKey]);
@ -202,7 +153,7 @@ QUuid VoxelNode::getSourceUUID() const {
return QUuid();
}
bool VoxelNode::matchesSourceUUID(const QUuid& sourceUUID) const {
bool OctreeElement::matchesSourceUUID(const QUuid& sourceUUID) const {
if (_sourceUUIDKey > KEY_FOR_NULL) {
if (_mapKeysToSourceUUIDs.end() != _mapKeysToSourceUUIDs.find(_sourceUUIDKey)) {
return QUuid(_mapKeysToSourceUUIDs[_sourceUUIDKey]) == sourceUUID;
@ -211,7 +162,7 @@ bool VoxelNode::matchesSourceUUID(const QUuid& sourceUUID) const {
return sourceUUID.isNull();
}
uint16_t VoxelNode::getSourceNodeUUIDKey(const QUuid& sourceUUID) {
uint16_t OctreeElement::getSourceNodeUUIDKey(const QUuid& sourceUUID) {
uint16_t key = KEY_FOR_NULL;
QString sourceUUIDString = sourceUUID.toString();
if (_mapSourceUUIDsToKeys.end() != _mapSourceUUIDsToKeys.find(sourceUUIDString)) {
@ -222,7 +173,7 @@ uint16_t VoxelNode::getSourceNodeUUIDKey(const QUuid& sourceUUID) {
void VoxelNode::setShouldRender(bool shouldRender) {
void OctreeElement::setShouldRender(bool shouldRender) {
// if shouldRender is changing, then consider ourselves dirty
if (shouldRender != _shouldRender) {
_shouldRender = shouldRender;
@ -231,7 +182,7 @@ void VoxelNode::setShouldRender(bool shouldRender) {
}
}
void VoxelNode::calculateAABox() {
void OctreeElement::calculateAABox() {
glm::vec3 corner;
// copy corner into box
@ -242,8 +193,8 @@ void VoxelNode::calculateAABox() {
_box.setBox(corner,voxelScale);
}
void VoxelNode::deleteChildAtIndex(int childIndex) {
VoxelNode* childAt = getChildAtIndex(childIndex);
void OctreeElement::deleteChildAtIndex(int childIndex) {
OctreeElement* childAt = getChildAtIndex(childIndex);
if (childAt) {
delete childAt;
setChildAtIndex(childIndex, NULL);
@ -261,8 +212,8 @@ void VoxelNode::deleteChildAtIndex(int childIndex) {
}
// does not delete the node!
VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
VoxelNode* returnedChild = getChildAtIndex(childIndex);
OctreeElement* OctreeElement::removeChildAtIndex(int childIndex) {
OctreeElement* returnedChild = getChildAtIndex(childIndex);
if (returnedChild) {
setChildAtIndex(childIndex, NULL);
_isDirty = true;
@ -281,11 +232,11 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) {
}
#ifdef HAS_AUDIT_CHILDREN
void VoxelNode::auditChildren(const char* label) const {
void OctreeElement::auditChildren(const char* label) const {
bool auditFailed = false;
for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) {
VoxelNode* testChildNew = getChildAtIndex(childIndex);
VoxelNode* testChildOld = _childrenArray[childIndex];
OctreeElement* testChildNew = getChildAtIndex(childIndex);
OctreeElement* testChildOld = _childrenArray[childIndex];
if (testChildNew != testChildOld) {
auditFailed = true;
@ -302,8 +253,8 @@ void VoxelNode::auditChildren(const char* label) const {
for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) {
VoxelNode* testChildNew = getChildAtIndex(childIndex);
VoxelNode* testChildOld = _childrenArray[childIndex];
OctreeElement* testChildNew = getChildAtIndex(childIndex);
OctreeElement* testChildOld = _childrenArray[childIndex];
qDebug("child at index %d... testChildOld=%p testChildNew=%p %s \n",
childIndex, testChildOld, testChildNew ,
@ -316,25 +267,25 @@ void VoxelNode::auditChildren(const char* label) const {
#endif // def HAS_AUDIT_CHILDREN
uint64_t VoxelNode::_getChildAtIndexTime = 0;
uint64_t VoxelNode::_getChildAtIndexCalls = 0;
uint64_t VoxelNode::_setChildAtIndexTime = 0;
uint64_t VoxelNode::_setChildAtIndexCalls = 0;
uint64_t OctreeElement::_getChildAtIndexTime = 0;
uint64_t OctreeElement::_getChildAtIndexCalls = 0;
uint64_t OctreeElement::_setChildAtIndexTime = 0;
uint64_t OctreeElement::_setChildAtIndexCalls = 0;
#ifdef BLENDED_UNION_CHILDREN
uint64_t VoxelNode::_singleChildrenCount = 0;
uint64_t VoxelNode::_twoChildrenOffsetCount = 0;
uint64_t VoxelNode::_twoChildrenExternalCount = 0;
uint64_t VoxelNode::_threeChildrenOffsetCount = 0;
uint64_t VoxelNode::_threeChildrenExternalCount = 0;
uint64_t VoxelNode::_couldStoreFourChildrenInternally = 0;
uint64_t VoxelNode::_couldNotStoreFourChildrenInternally = 0;
uint64_t OctreeElement::_singleChildrenCount = 0;
uint64_t OctreeElement::_twoChildrenOffsetCount = 0;
uint64_t OctreeElement::_twoChildrenExternalCount = 0;
uint64_t OctreeElement::_threeChildrenOffsetCount = 0;
uint64_t OctreeElement::_threeChildrenExternalCount = 0;
uint64_t OctreeElement::_couldStoreFourChildrenInternally = 0;
uint64_t OctreeElement::_couldNotStoreFourChildrenInternally = 0;
#endif
uint64_t VoxelNode::_externalChildrenCount = 0;
uint64_t VoxelNode::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint64_t OctreeElement::_externalChildrenCount = 0;
uint64_t OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const {
OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const {
#ifdef SIMPLE_CHILD_ARRAY
return _simpleChildArray[childIndex];
#endif // SIMPLE_CHILD_ARRAY
@ -366,7 +317,7 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const {
#ifdef BLENDED_UNION_CHILDREN
PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls);
VoxelNode* result = NULL;
OctreeElement* result = NULL;
int childCount = getChildCount();
#ifdef HAS_AUDIT_CHILDREN
@ -405,10 +356,10 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const {
} else {
if (indexOne == childIndex) {
int32_t offset = _children.offsetsTwoChildren[0];
result = (VoxelNode*)((uint8_t*)this + offset);
result = (OctreeElement*)((uint8_t*)this + offset);
} else if (indexTwo == childIndex) {
int32_t offset = _children.offsetsTwoChildren[1];
result = (VoxelNode*)((uint8_t*)this + offset);
result = (OctreeElement*)((uint8_t*)this + offset);
}
}
} break;
@ -435,11 +386,11 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const {
decodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
if (indexOne == childIndex) {
result = (VoxelNode*)((uint8_t*)this + offsetOne);
result = (OctreeElement*)((uint8_t*)this + offsetOne);
} else if (indexTwo == childIndex) {
result = (VoxelNode*)((uint8_t*)this + offsetTwo);
result = (OctreeElement*)((uint8_t*)this + offsetTwo);
} else if (indexThree == childIndex) {
result = (VoxelNode*)((uint8_t*)this + offsetThree);
result = (OctreeElement*)((uint8_t*)this + offsetThree);
}
}
} break;
@ -477,7 +428,7 @@ VoxelNode* VoxelNode::getChildAtIndex(int childIndex) const {
}
#ifdef BLENDED_UNION_CHILDREN
void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) {
void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
@ -490,7 +441,7 @@ void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) {
if (_childrenExternal) {
//assert(_children.external);
const int previousChildCount = 2;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
delete[] _children.external;
_children.external = NULL; // probably not needed!
_childrenExternal = false;
@ -508,9 +459,9 @@ void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) {
if (!_childrenExternal) {
_childrenExternal = true;
const int newChildCount = 2;
_externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*);
_children.external = new VoxelNode*[newChildCount];
memset(_children.external, 0, sizeof(VoxelNode*) * newChildCount);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
}
_children.external[0] = childOne;
_children.external[1] = childTwo;
@ -518,7 +469,7 @@ void VoxelNode::storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo) {
}
}
void VoxelNode::retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo) {
void OctreeElement::retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo) {
// If we previously had an external array, then get the
if (_childrenExternal) {
childOne = _children.external[0];
@ -528,17 +479,17 @@ void VoxelNode::retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo)
_childrenExternal = false;
_twoChildrenExternalCount--;
const int newChildCount = 2;
_externalChildrenMemoryUsage -= newChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= newChildCount * sizeof(OctreeElement*);
} else {
int64_t offsetOne = _children.offsetsTwoChildren[0];
int64_t offsetTwo = _children.offsetsTwoChildren[1];
childOne = (VoxelNode*)((uint8_t*)this + offsetOne);
childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo);
childOne = (OctreeElement*)((uint8_t*)this + offsetOne);
childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo);
_twoChildrenOffsetCount--;
}
}
void VoxelNode::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const {
void OctreeElement::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const {
const uint64_t ENCODE_BITS = 21;
const uint64_t ENCODE_MASK = 0xFFFFF;
const uint64_t ENCODE_MASK_SIGN = 0x100000;
@ -560,7 +511,7 @@ void VoxelNode::decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64
offsetThree = threeNegative ? -offsetEncodedThree : offsetEncodedThree;
}
void VoxelNode::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) {
void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree) {
const uint64_t ENCODE_BITS = 21;
const uint64_t ENCODE_MASK = 0xFFFFF;
const uint64_t ENCODE_MASK_SIGN = 0x100000;
@ -588,7 +539,7 @@ void VoxelNode::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t
_children.offsetsThreeChildrenEncoded = offsetEncodedOne | offsetEncodedTwo | offsetEncodedThree;
}
void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree) {
void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this;
@ -607,7 +558,7 @@ void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, Vox
_children.external = NULL; // probably not needed!
_childrenExternal = false;
const int previousChildCount = 3;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
}
// encode in union
encodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
@ -619,9 +570,9 @@ void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, Vox
if (!_childrenExternal) {
_childrenExternal = true;
const int newChildCount = 3;
_externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*);
_children.external = new VoxelNode*[newChildCount];
memset(_children.external, 0, sizeof(VoxelNode*) * newChildCount);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
}
_children.external[0] = childOne;
_children.external[1] = childTwo;
@ -630,7 +581,7 @@ void VoxelNode::storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, Vox
}
}
void VoxelNode::retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree) {
void OctreeElement::retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree) {
// If we previously had an external array, then get the
if (_childrenExternal) {
childOne = _children.external[0];
@ -640,19 +591,19 @@ void VoxelNode::retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo
_children.external = NULL; // probably not needed!
_childrenExternal = false;
_threeChildrenExternalCount--;
_externalChildrenMemoryUsage -= 3 * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= 3 * sizeof(OctreeElement*);
} else {
int64_t offsetOne, offsetTwo, offsetThree;
decodeThreeOffsets(offsetOne, offsetTwo, offsetThree);
childOne = (VoxelNode*)((uint8_t*)this + offsetOne);
childTwo = (VoxelNode*)((uint8_t*)this + offsetTwo);
childThree = (VoxelNode*)((uint8_t*)this + offsetThree);
childOne = (OctreeElement*)((uint8_t*)this + offsetOne);
childTwo = (OctreeElement*)((uint8_t*)this + offsetTwo);
childThree = (OctreeElement*)((uint8_t*)this + offsetThree);
_threeChildrenOffsetCount--;
}
}
void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour) {
void OctreeElement::checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour) {
int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this;
int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this;
int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this;
@ -675,10 +626,10 @@ void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo,
}
#endif
void VoxelNode::deleteAllChildren() {
// first delete all the VoxelNode objects...
void OctreeElement::deleteAllChildren() {
// first delete all the OctreeElement objects...
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childAt = getChildAtIndex(i);
OctreeElement* childAt = getChildAtIndex(i);
if (childAt) {
delete childAt;
}
@ -731,7 +682,7 @@ void VoxelNode::deleteAllChildren() {
#endif // BLENDED_UNION_CHILDREN
}
void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) {
#ifdef SIMPLE_CHILD_ARRAY
int previousChildCount = getChildCount();
if (child) {
@ -775,20 +726,20 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
} else if (previousChildCount == 0 && newChildCount == 1) {
_children.single = child;
} else if (previousChildCount == 1 && newChildCount == 2) {
VoxelNode* previousChild = _children.single;
_children.external = new VoxelNode*[NUMBER_OF_CHILDREN];
memset(_children.external, 0, sizeof(VoxelNode*) * NUMBER_OF_CHILDREN);
OctreeElement* previousChild = _children.single;
_children.external = new OctreeElement*[NUMBER_OF_CHILDREN];
memset(_children.external, 0, sizeof(OctreeElement*) * NUMBER_OF_CHILDREN);
_children.external[firstIndex] = previousChild;
_children.external[childIndex] = child;
_externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(VoxelNode*);
_externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(OctreeElement*);
} else if (previousChildCount == 2 && newChildCount == 1) {
assert(child == NULL); // we are removing a child, so this must be true!
VoxelNode* previousFirstChild = _children.external[firstIndex];
VoxelNode* previousSecondChild = _children.external[secondIndex];
OctreeElement* previousFirstChild = _children.external[firstIndex];
OctreeElement* previousSecondChild = _children.external[secondIndex];
delete[] _children.external;
_externalChildrenMemoryUsage -= NUMBER_OF_CHILDREN * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= NUMBER_OF_CHILDREN * sizeof(OctreeElement*);
if (childIndex == firstIndex) {
_children.single = previousSecondChild;
} else {
@ -839,8 +790,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// If we had 1 child, and we're adding a second child, then we need to determine
// if we can use offsets to store them
VoxelNode* childOne;
VoxelNode* childTwo;
OctreeElement* childOne;
OctreeElement* childTwo;
if (getNthBit(previousChildMask, 1) < childIndex) {
childOne = _children.single;
@ -859,8 +810,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
int indexTwo = getNthBit(previousChildMask, 2);
bool keepChildOne = indexTwo == childIndex;
VoxelNode* childOne;
VoxelNode* childTwo;
OctreeElement* childOne;
OctreeElement* childTwo;
retrieveTwoChildren(childOne, childTwo);
@ -878,8 +829,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
bool replaceChildOne = indexOne == childIndex;
// Get the existing two children out of their encoding...
VoxelNode* childOne;
VoxelNode* childTwo;
OctreeElement* childOne;
OctreeElement* childTwo;
retrieveTwoChildren(childOne, childTwo);
if (replaceChildOne) {
@ -894,9 +845,9 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// If we had 2 children, and now have 3, then we know we are going to an external case...
// First, decode the children...
VoxelNode* childOne;
VoxelNode* childTwo;
VoxelNode* childThree;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveTwoChildren(childOne, childTwo);
@ -926,9 +877,9 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
bool removeChildOne = indexOne == childIndex;
bool removeChildTwo = indexTwo == childIndex;
VoxelNode* childOne;
VoxelNode* childTwo;
VoxelNode* childThree;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
@ -953,9 +904,9 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
bool replaceChildOne = indexOne == childIndex;
bool replaceChildTwo = indexTwo == childIndex;
VoxelNode* childOne;
VoxelNode* childTwo;
VoxelNode* childThree;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
@ -973,10 +924,10 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// If we had 3 children, and now have 4, then we know we are going to an external case...
// First, decode the children...
VoxelNode* childOne;
VoxelNode* childTwo;
VoxelNode* childThree;
VoxelNode* childFour;
OctreeElement* childOne;
OctreeElement* childTwo;
OctreeElement* childThree;
OctreeElement* childFour;
// Get the existing two children out of their encoding...
retrieveThreeChildren(childOne, childTwo, childThree);
@ -1005,10 +956,10 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// now, allocate the external...
_childrenExternal = true;
const int newChildCount = 4;
_children.external = new VoxelNode*[newChildCount];
memset(_children.external, 0, sizeof(VoxelNode*) * newChildCount);
_children.external = new OctreeElement*[newChildCount];
memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount);
_externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
_children.external[0] = childOne;
_children.external[1] = childTwo;
@ -1028,10 +979,10 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
bool removeChildTwo = indexTwo == childIndex;
bool removeChildThree = indexThree == childIndex;
VoxelNode* childOne = _children.external[0];
VoxelNode* childTwo = _children.external[1];
VoxelNode* childThree = _children.external[2];
VoxelNode* childFour = _children.external[3];
OctreeElement* childOne = _children.external[0];
OctreeElement* childTwo = _children.external[1];
OctreeElement* childThree = _children.external[2];
OctreeElement* childFour = _children.external[3];
if (removeChildOne) {
childOne = childTwo;
@ -1051,7 +1002,7 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
delete[] _children.external;
_children.external = NULL;
_externalChildrenCount--;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
storeThreeChildren(childOne, childTwo, childThree);
} else if (previousChildCount == newChildCount) {
//assert(_children.external && _childrenExternal && previousChildCount >= 4);
@ -1075,8 +1026,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert
// this child pointer into our external list
VoxelNode** newExternalList = new VoxelNode*[newChildCount];
memset(newExternalList, 0, sizeof(VoxelNode*) * newChildCount);
OctreeElement** newExternalList = new OctreeElement*[newChildCount];
memset(newExternalList, 0, sizeof(OctreeElement*) * newChildCount);
int copiedCount = 0;
for (int ordinal = 1; ordinal <= newChildCount; ordinal++) {
@ -1101,8 +1052,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
}
delete[] _children.external;
_children.external = newExternalList;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
} else if (previousChildCount > newChildCount) {
//assert(_children.external && _childrenExternal && previousChildCount >= 4);
@ -1110,7 +1061,7 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
// 4 or more children, one item being removed, we know we're stored externally, we just figure out which
// item to remove from our external list
VoxelNode** newExternalList = new VoxelNode*[newChildCount];
OctreeElement** newExternalList = new OctreeElement*[newChildCount];
for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) {
int index = getNthBit(previousChildMask, ordinal);
@ -1127,8 +1078,8 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
}
delete[] _children.external;
_children.external = newExternalList;
_externalChildrenMemoryUsage -= previousChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage += newChildCount * sizeof(VoxelNode*);
_externalChildrenMemoryUsage -= previousChildCount * sizeof(OctreeElement*);
_externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*);
} else {
//assert(false);
qDebug("THIS SHOULD NOT HAPPEN previousChildCount == %d && newChildCount == %d\n",previousChildCount, newChildCount);
@ -1149,16 +1100,16 @@ void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) {
}
VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
VoxelNode* childAt = getChildAtIndex(childIndex);
OctreeElement* OctreeElement::addChildAtIndex(int childIndex) {
OctreeElement* childAt = getChildAtIndex(childIndex);
if (!childAt) {
// before adding a child, see if we're currently a leaf
if (isLeaf()) {
_voxelNodeLeafCount--;
}
childAt = new VoxelNode(childOctalCode(getOctalCode(), childIndex));
childAt->setVoxelSystem(getVoxelSystem()); // our child is always part of our voxel system NULL ok
unsigned char* newChildCode = childOctalCode(getOctalCode(), childIndex);
childAt = createNewElement(newChildCode);
setChildAtIndex(childIndex, childAt);
_isDirty = true;
@ -1168,12 +1119,12 @@ VoxelNode* VoxelNode::addChildAtIndex(int childIndex) {
}
// handles staging or deletion of all deep children
void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) {
void OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) {
if (recursionCount > DANGEROUSLY_DEEP_RECURSION) {
qDebug() << "VoxelNode::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n";
qDebug() << "OctreeElement::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!\n";
return;
}
VoxelNode* childToDelete = getChildAtIndex(childIndex);
OctreeElement* childToDelete = getChildAtIndex(childIndex);
if (childToDelete) {
// If the child is not a leaf, then call ourselves recursively on all the children
if (!childToDelete->isLeaf()) {
@ -1188,180 +1139,37 @@ void VoxelNode::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) {
}
}
// will average the child colors...
void VoxelNode::setColorFromAverageOfChildren() {
int colorArray[4] = {0,0,0,0};
float density = 0.0f;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childAt = getChildAtIndex(i);
if (childAt && childAt->isColored()) {
for (int j = 0; j < 3; j++) {
colorArray[j] += childAt->getTrueColor()[j]; // color averaging should always be based on true colors
}
colorArray[3]++;
}
if (childAt) {
density += childAt->getDensity();
}
}
density /= (float) NUMBER_OF_CHILDREN;
//
// The VISIBLE_ABOVE_DENSITY sets the density of matter above which an averaged color voxel will
// be set. It is an important physical constant in our universe. A number below 0.5 will cause
// things to get 'fatter' at a distance, because upward averaging will make larger voxels out of
// less data, which is (probably) going to be preferable because it gives a sense that there is
// something out there to go investigate. A number above 0.5 would cause the world to become
// more 'empty' at a distance. Exactly 0.5 would match the physical world, at least for materials
// that are not shiny and have equivalent ambient reflectance.
//
const float VISIBLE_ABOVE_DENSITY = 0.10f;
nodeColor newColor = { 0, 0, 0, 0};
if (density > VISIBLE_ABOVE_DENSITY) {
// The density of material in the space of the voxel sets whether it is actually colored
for (int c = 0; c < 3; c++) {
// set the average color value
newColor[c] = colorArray[c] / colorArray[3];
}
// set the alpha to 1 to indicate that this isn't transparent
newColor[3] = 1;
}
// Set the color from the average of the child colors, and update the density
setColor(newColor);
setDensity(density);
}
// Note: !NO_FALSE_COLOR implementations of setFalseColor(), setFalseColored(), and setColor() here.
// the actual NO_FALSE_COLOR version are inline in the VoxelNode.h
#ifndef NO_FALSE_COLOR // !NO_FALSE_COLOR means, does have false color
void VoxelNode::setFalseColor(colorPart red, colorPart green, colorPart blue) {
if (_falseColored != true || _currentColor[0] != red || _currentColor[1] != green || _currentColor[2] != blue) {
_falseColored=true;
_currentColor[0] = red;
_currentColor[1] = green;
_currentColor[2] = blue;
_currentColor[3] = 1; // XXXBHG - False colors are always considered set
_isDirty = true;
markWithChangedTime();
}
}
void VoxelNode::setFalseColored(bool isFalseColored) {
if (_falseColored != isFalseColored) {
// if we were false colored, and are no longer false colored, then swap back
if (_falseColored && !isFalseColored) {
memcpy(&_currentColor,&_trueColor,sizeof(nodeColor));
}
_falseColored = isFalseColored;
_isDirty = true;
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
markWithChangedTime();
}
};
void VoxelNode::setColor(const nodeColor& color) {
if (_trueColor[0] != color[0] || _trueColor[1] != color[1] || _trueColor[2] != color[2]) {
memcpy(&_trueColor,&color,sizeof(nodeColor));
if (!_falseColored) {
memcpy(&_currentColor,&color,sizeof(nodeColor));
}
_isDirty = true;
_density = 1.0f; // If color set, assume leaf, re-averaging will update density if needed.
markWithChangedTime();
}
}
#endif
// will detect if children are leaves AND the same color
// and in that case will delete the children and make this node
// a leaf, returns TRUE if all the leaves are collapsed into a
// single node
bool VoxelNode::collapseIdenticalLeaves() {
// scan children, verify that they are ALL present and accounted for
bool allChildrenMatch = true; // assume the best (ottimista)
int red,green,blue;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childAt = getChildAtIndex(i);
// if no child, child isn't a leaf, or child doesn't have a color
if (!childAt || !childAt->isLeaf() || !childAt->isColored()) {
allChildrenMatch=false;
//qDebug("SADNESS child missing or not colored! i=%d\n",i);
break;
} else {
if (i==0) {
red = childAt->getColor()[0];
green = childAt->getColor()[1];
blue = childAt->getColor()[2];
} else if (red != childAt->getColor()[0] ||
green != childAt->getColor()[1] || blue != childAt->getColor()[2]) {
allChildrenMatch=false;
break;
}
}
}
if (allChildrenMatch) {
//qDebug("allChildrenMatch: pruning tree\n");
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childAt = getChildAtIndex(i);
delete childAt; // delete all the child nodes
setChildAtIndex(i, NULL); // set it to NULL
}
nodeColor collapsedColor;
collapsedColor[0]=red;
collapsedColor[1]=green;
collapsedColor[2]=blue;
collapsedColor[3]=1; // color is set
setColor(collapsedColor);
}
return allChildrenMatch;
}
void VoxelNode::setRandomColor(int minimumBrightness) {
nodeColor newColor;
for (int c = 0; c < 3; c++) {
newColor[c] = randomColorValue(minimumBrightness);
}
newColor[3] = 1;
setColor(newColor);
}
void VoxelNode::printDebugDetails(const char* label) const {
void OctreeElement::printDebugDetails(const char* label) const {
unsigned char childBits = 0;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
VoxelNode* childAt = getChildAtIndex(i);
OctreeElement* childAt = getChildAtIndex(i);
if (childAt) {
setAtBit(childBits,i);
}
}
qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isColored=%s (%d,%d,%d,%d) isDirty=%s shouldRender=%s\n children=", label,
qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isDirty=%s shouldRender=%s\n children=", label,
_box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getScale(),
debug::valueOf(isLeaf()), debug::valueOf(isColored()), getColor()[0], getColor()[1], getColor()[2], getColor()[3],
debug::valueOf(isDirty()), debug::valueOf(getShouldRender()));
debug::valueOf(isLeaf()), debug::valueOf(isDirty()), debug::valueOf(getShouldRender()));
outputBits(childBits, false);
qDebug("\n octalCode=");
printOctalCode(getOctalCode());
}
float VoxelNode::getEnclosingRadius() const {
float OctreeElement::getEnclosingRadius() const {
return getScale() * sqrtf(3.0f) / 2.0f;
}
bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const {
bool OctreeElement::isInView(const ViewFrustum& viewFrustum) const {
AABox box = _box; // use temporary box so we can scale it
box.scale(TREE_SCALE);
bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box));
return inView;
}
ViewFrustum::location VoxelNode::inFrustum(const ViewFrustum& viewFrustum) const {
ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) const {
AABox box = _box; // use temporary box so we can scale it
box.scale(TREE_SCALE);
return viewFrustum.boxInFrustum(box);
@ -1376,9 +1184,9 @@ ViewFrustum::location VoxelNode::inFrustum(const ViewFrustum& viewFrustum) const
// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest"
// corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of.
// By doing this, we don't need to test each child voxel's position vs the LOD boundary
bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const {
bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const {
bool shouldRender = false;
if (isColored()) {
if (hasContent()) {
float furthestDistance = furthestDistanceToCamera(*viewFrustum);
float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust, voxelScaleSize);
float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize);
@ -1390,7 +1198,7 @@ bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, float voxe
}
// Calculates the distance to the furthest point of the voxel to the camera
float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {
float OctreeElement::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {
AABox box = getAABox();
box.scale(TREE_SCALE);
glm::vec3 furthestPoint = viewFrustum.getFurthestPointFromCamera(box);
@ -1399,35 +1207,35 @@ float VoxelNode::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const
return distanceToVoxelCenter;
}
float VoxelNode::distanceToCamera(const ViewFrustum& viewFrustum) const {
float OctreeElement::distanceToCamera(const ViewFrustum& viewFrustum) const {
glm::vec3 center = _box.calcCenter() * (float)TREE_SCALE;
glm::vec3 temp = viewFrustum.getPosition() - center;
float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp));
return distanceToVoxelCenter;
}
float VoxelNode::distanceSquareToPoint(const glm::vec3& point) const {
float OctreeElement::distanceSquareToPoint(const glm::vec3& point) const {
glm::vec3 temp = point - _box.calcCenter();
float distanceSquare = glm::dot(temp, temp);
return distanceSquare;
}
float VoxelNode::distanceToPoint(const glm::vec3& point) const {
float OctreeElement::distanceToPoint(const glm::vec3& point) const {
glm::vec3 temp = point - _box.calcCenter();
float distance = sqrtf(glm::dot(temp, temp));
return distance;
}
QReadWriteLock VoxelNode::_deleteHooksLock;
std::vector<VoxelNodeDeleteHook*> VoxelNode::_deleteHooks;
QReadWriteLock OctreeElement::_deleteHooksLock;
std::vector<OctreeElementDeleteHook*> OctreeElement::_deleteHooks;
void VoxelNode::addDeleteHook(VoxelNodeDeleteHook* hook) {
void OctreeElement::addDeleteHook(OctreeElementDeleteHook* hook) {
_deleteHooksLock.lockForWrite();
_deleteHooks.push_back(hook);
_deleteHooksLock.unlock();
}
void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
void OctreeElement::removeDeleteHook(OctreeElementDeleteHook* hook) {
_deleteHooksLock.lockForWrite();
for (int i = 0; i < _deleteHooks.size(); i++) {
if (_deleteHooks[i] == hook) {
@ -1438,21 +1246,21 @@ void VoxelNode::removeDeleteHook(VoxelNodeDeleteHook* hook) {
_deleteHooksLock.unlock();
}
void VoxelNode::notifyDeleteHooks() {
void OctreeElement::notifyDeleteHooks() {
_deleteHooksLock.lockForRead();
for (int i = 0; i < _deleteHooks.size(); i++) {
_deleteHooks[i]->voxelDeleted(this);
_deleteHooks[i]->elementDeleted(this);
}
_deleteHooksLock.unlock();
}
std::vector<VoxelNodeUpdateHook*> VoxelNode::_updateHooks;
std::vector<OctreeElementUpdateHook*> OctreeElement::_updateHooks;
void VoxelNode::addUpdateHook(VoxelNodeUpdateHook* hook) {
void OctreeElement::addUpdateHook(OctreeElementUpdateHook* hook) {
_updateHooks.push_back(hook);
}
void VoxelNode::removeUpdateHook(VoxelNodeUpdateHook* hook) {
void OctreeElement::removeUpdateHook(OctreeElementUpdateHook* hook) {
for (int i = 0; i < _updateHooks.size(); i++) {
if (_updateHooks[i] == hook) {
_updateHooks.erase(_updateHooks.begin() + i);
@ -1461,8 +1269,8 @@ void VoxelNode::removeUpdateHook(VoxelNodeUpdateHook* hook) {
}
}
void VoxelNode::notifyUpdateHooks() {
void OctreeElement::notifyUpdateHooks() {
for (int i = 0; i < _updateHooks.size(); i++) {
_updateHooks[i]->voxelUpdated(this);
_updateHooks[i]->elementUpdated(this);
}
}

View file

@ -1,13 +1,13 @@
//
// VoxelNode.h
// OctreeElement.h
// hifi
//
// Created by Stephen Birarda on 3/13/13.
//
//
#ifndef __hifi__VoxelNode__
#define __hifi__VoxelNode__
#ifndef __hifi__OctreeElement__
#define __hifi__OctreeElement__
//#define HAS_AUDIT_CHILDREN
//#define SIMPLE_CHILD_ARRAY
@ -18,45 +18,50 @@
#include <SharedUtil.h>
#include "AABox.h"
#include "ViewFrustum.h"
#include "VoxelConstants.h"
#include "OctreeConstants.h"
//#include "Octree.h"
class VoxelTree; // forward declaration
class VoxelNode; // forward declaration
class VoxelSystem; // forward declaration
typedef unsigned char colorPart;
typedef unsigned char nodeColor[4];
typedef unsigned char rgbColor[3];
class Octree;
class OctreeElement;
class OctreeElementDeleteHook;
class OctreePacketData;
class VoxelSystem;
class ReadBitstreamToTreeParams;
// Callers who want delete hook callbacks should implement this class
class VoxelNodeDeleteHook {
class OctreeElementDeleteHook {
public:
virtual void voxelDeleted(VoxelNode* node) = 0;
virtual void elementDeleted(OctreeElement* element) = 0;
};
// Callers who want update hook callbacks should implement this class
class VoxelNodeUpdateHook {
class OctreeElementUpdateHook {
public:
virtual void voxelUpdated(VoxelNode* node) = 0;
virtual void elementUpdated(OctreeElement* element) = 0;
};
class VoxelNode {
class OctreeElement {
protected:
// can only be constructed by derived implementation
OctreeElement();
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0;
public:
VoxelNode(); // root node constructor
VoxelNode(unsigned char * octalCode); // regular constructor
~VoxelNode();
virtual void init(unsigned char * octalCode); /// Your subclass must call init on construction.
virtual ~OctreeElement();
const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; }
VoxelNode* getChildAtIndex(int childIndex) const;
OctreeElement* getChildAtIndex(int childIndex) const;
void deleteChildAtIndex(int childIndex);
VoxelNode* removeChildAtIndex(int childIndex);
VoxelNode* addChildAtIndex(int childIndex);
OctreeElement* removeChildAtIndex(int childIndex);
virtual OctreeElement* addChildAtIndex(int childIndex);
void safeDeepDeleteChildAtIndex(int childIndex, int recursionCount = 0); // handles deletion of all descendents
void setColorFromAverageOfChildren();
void setRandomColor(int minimumBrightness);
bool collapseIdenticalLeaves();
virtual void calculateAverageFromChildren() { };
virtual bool collapseChildren() { return false; };
const AABox& getAABox() const { return _box; }
const glm::vec3& getCorner() const { return _box.getCorner(); }
@ -64,15 +69,21 @@ public:
int getLevel() const { return numberOfThreeBitSectionsInCode(getOctalCode()) + 1; }
float getEnclosingRadius() const;
bool isColored() const { return _trueColor[3] == 1; }
virtual bool hasContent() const { return isLeaf(); }
virtual void splitChildren() { }
virtual bool requiresSplit() const { return false; }
virtual bool appendElementData(OctreePacketData* packetData) const { return true; }
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args)
{ return 0; }
bool isInView(const ViewFrustum& viewFrustum) const;
ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const;
float distanceToCamera(const ViewFrustum& viewFrustum) const;
float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const;
bool calculateShouldRender(const ViewFrustum* viewFrustum,
float voxelSizeScale = DEFAULT_VOXEL_SIZE_SCALE, int boundaryLevelAdjust = 0) const;
float voxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const;
// points are assumed to be in Voxel Coordinates (not TREE_SCALE'd)
float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this.
@ -87,27 +98,15 @@ public:
bool hasChangedSince(uint64_t time) const { return (_lastChanged > time); }
void markWithChangedTime();
uint64_t getLastChanged() const { return _lastChanged; }
void handleSubtreeChanged(VoxelTree* myTree);
void handleSubtreeChanged(Octree* myTree);
glBufferIndex getBufferIndex() const { return _glBufferIndex; }
bool isKnownBufferIndex() const { return !_unknownBufferIndex; }
void setBufferIndex(glBufferIndex index) { _glBufferIndex = index; _unknownBufferIndex =(index == GLBUFFER_INDEX_UNKNOWN);}
VoxelSystem* getVoxelSystem() const;
void setVoxelSystem(VoxelSystem* voxelSystem);
// Used by VoxelSystem for rendering in/out of view and LOD
void setShouldRender(bool shouldRender);
bool getShouldRender() const { return _shouldRender; }
void setFalseColor(colorPart red, colorPart green, colorPart blue);
void setFalseColored(bool isFalseColored);
bool getFalseColored() { return _falseColored; }
void setColor(const nodeColor& color);
const nodeColor& getTrueColor() const { return _trueColor; }
const nodeColor& getColor() const { return _currentColor; }
void setDensity(float density) { _density = density; }
float getDensity() const { return _density; }
/// we assume that if you should be rendered, then your subclass is rendering, but this allows subclasses to
/// implement alternate rendering strategies
virtual bool isRendered() const { return getShouldRender(); }
void setSourceUUID(const QUuid& sourceID);
QUuid getSourceUUID() const;
@ -115,11 +114,11 @@ public:
bool matchesSourceUUID(const QUuid& sourceUUID) const;
static uint16_t getSourceNodeUUIDKey(const QUuid& sourceUUID);
static void addDeleteHook(VoxelNodeDeleteHook* hook);
static void removeDeleteHook(VoxelNodeDeleteHook* hook);
static void addDeleteHook(OctreeElementDeleteHook* hook);
static void removeDeleteHook(OctreeElementDeleteHook* hook);
static void addUpdateHook(VoxelNodeUpdateHook* hook);
static void removeUpdateHook(VoxelNodeUpdateHook* hook);
static void addUpdateHook(OctreeElementUpdateHook* hook);
static void removeUpdateHook(OctreeElementUpdateHook* hook);
static unsigned long getNodeCount() { return _voxelNodeCount; }
static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; }
@ -154,21 +153,20 @@ public:
#endif // def HAS_AUDIT_CHILDREN
#endif // def BLENDED_UNION_CHILDREN
private:
protected:
void deleteAllChildren();
void setChildAtIndex(int childIndex, VoxelNode* child);
void setChildAtIndex(int childIndex, OctreeElement* child);
#ifdef BLENDED_UNION_CHILDREN
void storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo);
void retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo);
void storeThreeChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree);
void retrieveThreeChildren(VoxelNode*& childOne, VoxelNode*& childTwo, VoxelNode*& childThree);
void storeTwoChildren(OctreeElement* childOne, OctreeElement* childTwo);
void retrieveTwoChildren(OctreeElement*& childOne, OctreeElement*& childTwo);
void storeThreeChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree);
void retrieveThreeChildren(OctreeElement*& childOne, OctreeElement*& childTwo, OctreeElement*& childThree);
void decodeThreeOffsets(int64_t& offsetOne, int64_t& offsetTwo, int64_t& offsetThree) const;
void encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int64_t offsetThree);
void checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, VoxelNode* childThree, VoxelNode* childFour);
void checkStoreFourChildren(OctreeElement* childOne, OctreeElement* childTwo, OctreeElement* childThree, OctreeElement* childFour);
#endif
void calculateAABox();
void init(unsigned char * octalCode);
void notifyDeleteHooks();
void notifyUpdateHooks();
@ -184,45 +182,29 @@ private:
/// Client and server, pointers to child nodes, various encodings
#ifdef SIMPLE_CHILD_ARRAY
VoxelNode* _simpleChildArray[8]; /// Only used when SIMPLE_CHILD_ARRAY is enabled
OctreeElement* _simpleChildArray[8]; /// Only used when SIMPLE_CHILD_ARRAY is enabled
#endif
#ifdef SIMPLE_EXTERNAL_CHILDREN
union children_t {
VoxelNode* single;
VoxelNode** external;
OctreeElement* single;
OctreeElement** external;
} _children;
#endif
#ifdef BLENDED_UNION_CHILDREN
union children_t {
VoxelNode* single;
OctreeElement* single;
int32_t offsetsTwoChildren[2];
uint64_t offsetsThreeChildrenEncoded;
VoxelNode** external;
OctreeElement** external;
} _children;
#ifdef HAS_AUDIT_CHILDREN
VoxelNode* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding
OctreeElement* _childrenArray[8]; /// Only used when HAS_AUDIT_CHILDREN is enabled to help debug children encoding
#endif // def HAS_AUDIT_CHILDREN
#endif //def BLENDED_UNION_CHILDREN
uint32_t _glBufferIndex : 24, /// Client only, vbo index for this voxel if being rendered, 3 bytes
_voxelSystemIndex : 8; /// Client only, index to the VoxelSystem rendering this voxel, 1 bytes
// Support for _voxelSystemIndex, we use these static member variables to track the VoxelSystems that are
// in use by various voxel nodes. We map the VoxelSystem pointers into an 1 byte key, this limits us to at
// most 255 voxel systems in use at a time within the client. Which is far more than we need.
static uint8_t _nextIndex;
static std::map<VoxelSystem*, uint8_t> _mapVoxelSystemPointersToIndex;
static std::map<uint8_t, VoxelSystem*> _mapIndexToVoxelSystemPointers;
float _density; /// Client and server, If leaf: density = 1, if internal node: 0-1 density of voxels inside, 4 bytes
nodeColor _trueColor; /// Client and server, true color of this voxel, 4 bytes
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
uint16_t _sourceUUIDKey; /// Client only, stores node id of voxel server that sent his voxel, 2 bytes
// Support for _sourceUUID, we use these static member variables to track the UUIDs that are
@ -242,10 +224,10 @@ private:
_childrenExternal : 1; /// Client only, is this voxel's VBO buffer the unknown buffer index, 1 bit
static QReadWriteLock _deleteHooksLock;
static std::vector<VoxelNodeDeleteHook*> _deleteHooks;
static std::vector<OctreeElementDeleteHook*> _deleteHooks;
//static QReadWriteLock _updateHooksLock;
static std::vector<VoxelNodeUpdateHook*> _updateHooks;
static std::vector<OctreeElementUpdateHook*> _updateHooks;
static uint64_t _voxelNodeCount;
static uint64_t _voxelNodeLeafCount;
@ -272,4 +254,4 @@ private:
static uint64_t _childrenCount[NUMBER_OF_CHILDREN + 1];
};
#endif /* defined(__hifi__VoxelNode__) */
#endif /* defined(__hifi__OctreeElement__) */

Some files were not shown because too many files have changed in this diff Show more