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:
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
CMakeLists.txt
external/PortAudio
src
Application.cppApplication.hAudio.cppAudio.hDataServerClient.cppDataServerClient.hEnvironment.cppEnvironment.hMenu.cppMenu.hOscilloscope.cppOscilloscope.hPairingHandler.cppVoxelHideShowThread.cppVoxelHideShowThread.hVoxelImporter.cppVoxelPacketProcessor.cppVoxelPacketProcessor.hVoxelSystem.cppVoxelSystem.h
avatar
devices
renderer
starfield
ui
libraries
|
@ -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)
|
|
@ -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})
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
140
assignment-client/src/AssignmentClient.cpp
Normal file
140
assignment-client/src/AssignmentClient.cpp
Normal 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();
|
||||
}
|
32
assignment-client/src/AssignmentClient.h
Normal file
32
assignment-client/src/AssignmentClient.h
Normal 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__) */
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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__) */
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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__) */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
1174
interface/external/PortAudio/include/portaudio.h
vendored
1174
interface/external/PortAudio/include/portaudio.h
vendored
File diff suppressed because it is too large
Load diff
Binary file not shown.
BIN
interface/external/PortAudio/lib/UNIX/libportaudio.a
vendored
BIN
interface/external/PortAudio/lib/UNIX/libportaudio.a
vendored
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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__) */
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
46
interface/src/VoxelHideShowThread.cpp
Normal file
46
interface/src/VoxelHideShowThread.cpp
Normal 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
|
||||
}
|
31
interface/src/VoxelHideShowThread.h
Normal file
31
interface/src/VoxelHideShowThread.h
Normal 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__
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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__) */
|
|
@ -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;
|
||||
}
|
|
@ -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__) */
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "AudioInjector.h"
|
||||
|
||||
#include "PositionalAudioRingBuffer.h"
|
||||
|
||||
class InjectedAudioRingBuffer : public PositionalAudioRingBuffer {
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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__) */
|
||||
|
|
|
@ -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)
|
168
libraries/octree-server/src/OctreeInboundPacketProcessor.cpp
Normal file
168
libraries/octree-server/src/OctreeInboundPacketProcessor.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -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__
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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__
|
|
@ -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);
|
||||
}
|
|
@ -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__) */
|
|
@ -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...
|
45
libraries/octree-server/src/OctreeSendThread.h
Normal file
45
libraries/octree-server/src/OctreeSendThread.h
Normal 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__
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
98
libraries/octree-server/src/OctreeServer.h
Normal file
98
libraries/octree-server/src/OctreeServer.h
Normal 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__
|
21
libraries/octree-server/src/OctreeServerConsts.h
Normal file
21
libraries/octree-server/src/OctreeServerConsts.h
Normal 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__
|
27
libraries/octree/CMakeLists.txt
Normal file
27
libraries/octree/CMakeLists.txt
Normal 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})
|
|
@ -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
|
|
@ -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:
|
|
@ -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
|
|
@ -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();
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
|
@ -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++;
|
||||
}
|
|
@ -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); }
|
1499
libraries/octree/src/Octree.cpp
Normal file
1499
libraries/octree/src/Octree.cpp
Normal file
File diff suppressed because it is too large
Load diff
314
libraries/octree/src/Octree.h
Normal file
314
libraries/octree/src/Octree.h
Normal 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__) */
|
47
libraries/octree/src/OctreeConstants.h
Normal file
47
libraries/octree/src/OctreeConstants.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue