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

Conflicts:
	interface/src/Application.cpp
This commit is contained in:
ZappoMan 2013-11-07 16:44:34 -08:00
commit 19515c80fd
43 changed files with 631 additions and 374 deletions

View file

@ -71,6 +71,9 @@ We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux
distributions. A Windows build is planned for the future, but not currently in
development.
On a fresh Ubuntu 13.10 install, these are all the packages you need to grab and build the hifi project:
<pre>sudo apt-get install build-essential cmake git libcurl4-openssl-dev libqt5scripttools5 libqt5svg5-dev libqt5webkit5-dev libqt5location5 qtlocation5-dev qtdeclarative5-dev qtscript5-dev qtsensors5-dev qtmultimedia5-dev qtquick1-5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-dev</pre>
Running Interface
-----
@ -147,4 +150,4 @@ To access your local domain in Interface, open your Preferences -- on OS X this
If everything worked you should see "Servers: 3" in the upper right. Nice work!
In the voxel-server/src directory you will find a README that explains in
further detail how to setup and administer a voxel-server.
further detail how to setup and administer a voxel-server.

View file

@ -29,7 +29,11 @@ link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR})
include_directories(${ROOT_DIR}/externals/civetweb/include)
if (UNIX)
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
endif (UNIX)
# link curl for synchronous script downloads
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${CURL_LIBRARY})
target_link_libraries(${TARGET_NAME} ${CURL_LIBRARY})

View file

@ -57,7 +57,7 @@ void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
const char AGENT_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER };
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));
@ -114,9 +114,11 @@ void Agent::run() {
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(INJECT_INTERVAL_USECS);
voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
// hook in a constructor for audio injectorss
AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
@ -158,33 +160,14 @@ void Agent::run() {
NodeList::getInstance()->sendDomainServerCheckIn();
}
// find the audio-mixer in the NodeList so we can inject audio at it
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
if (audioMixer && audioMixer->getActiveSocket()) {
emit willSendAudioDataCallback();
}
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow();
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
if (audioMixer && audioMixer->getActiveSocket() && scriptedAudioInjector.hasSamplesToInject()) {
// we have an audio mixer and samples to inject, send those off
scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket());
// clear out the audio injector so that it doesn't re-send what we just sent
scriptedAudioInjector.clear();
}
if (audioMixer && !audioMixer->getActiveSocket()) {
// don't have an active socket for the audio-mixer, ping it now
NodeList::getInstance()->pingPublicAndLocalSocketsForInactiveNode(audioMixer);
}
if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) {
timeval thisSend = {};
gettimeofday(&thisSend, NULL);
// allow the scripter's call back to setup visual data
emit willSendVisualDataCallback();
@ -193,14 +176,13 @@ void Agent::run() {
// 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) {

View file

@ -12,9 +12,6 @@
#include <sys/time.h>
#include <sys/wait.h>
#include <QtCore/QCoreApplication>
#include <Logging.h>
#include <NodeList.h>
#include <PacketHeaders.h>

View file

@ -78,6 +78,11 @@ function updateCells() {
nextCells[i][j] = -1;
}
}
if (Math.random() < 0.001) {
// Random mutation to keep things interesting in there.
nextCells[i][j] = 1;
}
}
}

View file

@ -124,6 +124,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_isHoverVoxel(false),
_isHoverVoxelSounding(false),
_mouseVoxelScale(1.0f / 1024.0f),
_mouseVoxelScaleInitialized(false),
_justEditedVoxel(false),
_nudgeStarted(false),
_lookingAlongX(false),
@ -1782,7 +1783,7 @@ void Application::shrinkMirrorView() {
}
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 20.0f;
const float MAX_VOXEL_EDIT_DISTANCE = 50.0f;
const float HEAD_SPHERE_RADIUS = 0.07;
static QUuid DEFAULT_NODE_ID_REF;
@ -1987,8 +1988,8 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
// deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
const float PITCH_SCALE = 0.5f;
const float YAW_SCALE = 0.5f;
const float PITCH_SCALE = 0.25f;
const float YAW_SCALE = 0.25f;
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
_faceshift.getEstimatedEyePitch() * pitchSign * PITCH_SCALE, _faceshift.getEstimatedEyeYaw() * YAW_SCALE, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
@ -2051,6 +2052,8 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()");
_mouseVoxel.s = 0.0f;
bool wasInitialized = _mouseVoxelScaleInitialized;
_mouseVoxelScaleInitialized = false;
if (Menu::getInstance()->isVoxelModeActionChecked() &&
(fabs(_myAvatar.getVelocity().x) +
fabs(_myAvatar.getVelocity().y) +
@ -2058,6 +2061,12 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) {
if (distance < MAX_VOXEL_EDIT_DISTANCE) {
// set the voxel scale to that of the first moused-over voxel
if (!wasInitialized) {
_mouseVoxelScale = _mouseVoxel.s;
}
_mouseVoxelScaleInitialized = true;
// find the nearest voxel with the desired scale
if (_mouseVoxelScale > _mouseVoxel.s) {
// choose the larger voxel that encompasses the one selected
@ -2990,6 +2999,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glDisable(GL_LIGHTING);
glPushMatrix();
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
const float CUBE_EXPANSION = 1.01f;
if (_nudgeStarted) {
renderNudgeGuide(_nudgeGuidePosition.x, _nudgeGuidePosition.y, _nudgeGuidePosition.z, _nudgeVoxel.s);
renderNudgeGrid(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s, _mouseVoxel.s);
@ -2999,7 +3009,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
_nudgeVoxel.z + _nudgeVoxel.s * 0.5f);
glColor3ub(255, 255, 255);
glLineWidth(4.0f);
glutWireCube(_nudgeVoxel.s);
glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION);
glPopMatrix();
} else {
renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
@ -3018,13 +3028,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
_nudgeGuidePosition.y + _nudgeVoxel.s*0.5f,
_nudgeGuidePosition.z + _nudgeVoxel.s*0.5f);
glLineWidth(4.0f);
glutWireCube(_nudgeVoxel.s);
glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION);
} else {
glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f,
_mouseVoxel.y + _mouseVoxel.s*0.5f,
_mouseVoxel.z + _mouseVoxel.s*0.5f);
glLineWidth(4.0f);
glutWireCube(_mouseVoxel.s);
glutWireCube(_mouseVoxel.s * CUBE_EXPANSION);
}
glLineWidth(1.0f);
glPopMatrix();
@ -4105,11 +4115,13 @@ void Application::nodeKilled(Node* node) {
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
// we should remove it...
_voxelServerJurisdictions.erase(nodeUUID);
@ -4165,11 +4177,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
}
// store jurisdiction details for later use
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it

View file

@ -378,6 +378,7 @@ private:
VoxelDetail _mouseVoxel; // details of the voxel to be edited
float _mouseVoxelScale; // the scale for adding/removing voxels
bool _mouseVoxelScaleInitialized;
glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit
bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel

View file

@ -8,18 +8,21 @@
#include <cstdlib>
#include <QMenuBar>
#include <QBoxLayout>
#include <QColorDialog>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFileDialog>
#include <QFormLayout>
#include <QInputDialog>
#include <QLineEdit>
#include <QMainWindow>
#include <QMenuBar>
#include <QSlider>
#include <QStandardPaths>
#include <QUuid>
#include <QWindow>
#include <UUID.h>
@ -280,6 +283,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options");
@ -324,6 +328,7 @@ Menu::Menu() :
false,
appInstance->getFaceshift(),
SLOT(setTCPEnabled(bool)));
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, true);
QMenu* webcamOptionsMenu = developerMenu->addMenu("Webcam Options");
@ -715,73 +720,43 @@ void Menu::aboutApp() {
InfoView::forcedShow();
}
void updateDSHostname(const QString& domainServerHostname) {
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
void sendFakeEnterEvent() {
QPoint lastCursorPosition = QCursor::pos();
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
if (domainServerHostname.size() > 0) {
// the user input a new hostname, use that
newHostname = domainServerHostname;
}
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(newHostname);
QPoint windowPosition = glWidget->mapFromGlobal(lastCursorPosition);
QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition);
QCoreApplication::sendEvent(glWidget, &enterEvent);
}
const int QLINE_MINIMUM_WIDTH = 400;
QLineEdit* lineEditForDomainHostname() {
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
if (NodeList::getInstance()->getDomainPort() != DEFAULT_DOMAIN_SERVER_PORT) {
// add the port to the currentDomainHostname string if it is custom
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainPort()));
}
QLineEdit* domainServerLineEdit = new QLineEdit(currentDomainHostname);
domainServerLineEdit->setPlaceholderText(DEFAULT_DOMAIN_HOSTNAME);
domainServerLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
return domainServerLineEdit;
}
const float DIALOG_RATIO_OF_WINDOW = 0.30;
void Menu::login() {
Application* applicationInstance = Application::getInstance();
QDialog dialog;
dialog.setWindowTitle("Login");
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
dialog.setLayout(layout);
QInputDialog loginDialog(Application::getInstance()->getWindow());
loginDialog.setWindowTitle("Login");
loginDialog.setLabelText("Username:");
QString username = Application::getInstance()->getProfile()->getUsername();
loginDialog.setTextValue(username);
loginDialog.setWindowFlags(Qt::Sheet);
loginDialog.resize(loginDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, loginDialog.size().height());
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
int dialogReturn = loginDialog.exec();
QString username = applicationInstance->getProfile()->getUsername();
QLineEdit* usernameLineEdit = new QLineEdit(username);
usernameLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Username:", usernameLineEdit);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
layout->addWidget(buttons);
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
if (usernameLineEdit->text() != username) {
if (dialogReturn == QDialog::Accepted && !loginDialog.textValue().isEmpty() && loginDialog.textValue() != username) {
// there has been a username change
// ask for a profile reset with the new username
applicationInstance->resetProfile(usernameLineEdit->text());
Application::getInstance()->resetProfile(loginDialog.textValue());
}
sendFakeEnterEvent();
}
void Menu::editPreferences() {
Application* applicationInstance = Application::getInstance();
QDialog dialog;
QDialog dialog(applicationInstance->getWindow());
dialog.setWindowTitle("Interface Preferences");
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
dialog.setLayout(layout);
@ -839,119 +814,107 @@ void Menu::editPreferences() {
layout->addWidget(buttons);
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
QUrl faceModelURL(faceURLEdit->text());
if (faceModelURL.toString() != faceURLString) {
// change the faceModelURL in the profile, it will also update this user's BlendFace
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
if (ret == QDialog::Accepted) {
QUrl faceModelURL(faceURLEdit->text());
// send the new face mesh URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::FaceMeshURL,
faceModelURL.toString().toLocal8Bit().constData());
}
QUrl skeletonModelURL(skeletonURLEdit->text());
if (skeletonModelURL.toString() != skeletonURLString) {
// change the skeletonModelURL in the profile, it will also update this user's Body
applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL);
if (faceModelURL.toString() != faceURLString) {
// change the faceModelURL in the profile, it will also update this user's BlendFace
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
// send the new face mesh URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::FaceMeshURL,
faceModelURL.toString().toLocal8Bit().constData());
}
// send the new skeleton model URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
skeletonModelURL.toString().toLocal8Bit().constData());
QUrl skeletonModelURL(skeletonURLEdit->text());
if (skeletonModelURL.toString() != skeletonURLString) {
// change the skeletonModelURL in the profile, it will also update this user's Body
applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL);
// send the new skeleton model URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
skeletonModelURL.toString().toLocal8Bit().constData());
}
QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
applicationInstance->getAvatar()->setLeanScale(leanScale->value());
_audioJitterBufferSamples = audioJitterBufferSamples->value();
if (_audioJitterBufferSamples != 0) {
applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples);
}
_fieldOfView = fieldOfView->value();
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
}
QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
applicationInstance->getAvatar()->setLeanScale(leanScale->value());
_audioJitterBufferSamples = audioJitterBufferSamples->value();
if (_audioJitterBufferSamples != 0) {
applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples);
}
_fieldOfView = fieldOfView->value();
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
sendFakeEnterEvent();
}
void Menu::goToDomain() {
QDialog dialog;
dialog.setWindowTitle("Go To Domain");
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
dialog.setLayout(layout);
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
QLineEdit* domainServerLineEdit = lineEditForDomainHostname();
form->addRow("Domain server:", domainServerLineEdit);
if (NodeList::getInstance()->getDomainPort() != DEFAULT_DOMAIN_SERVER_PORT) {
// add the port to the currentDomainHostname string if it is custom
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainPort()));
}
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
layout->addWidget(buttons);
QInputDialog domainDialog(Application::getInstance()->getWindow());
domainDialog.setWindowTitle("Go to Domain");
domainDialog.setLabelText("Domain server:");
domainDialog.setTextValue(currentDomainHostname);
domainDialog.setWindowFlags(Qt::Sheet);
domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height());
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
int dialogReturn = domainDialog.exec();
if (dialogReturn == QDialog::Accepted) {
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
if (domainDialog.textValue().size() > 0) {
// the user input a new hostname, use that
newHostname = domainDialog.textValue();
}
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
}
updateDSHostname(domainServerLineEdit->text());
sendFakeEnterEvent();
}
void Menu::goToLocation() {
QDialog dialog;
dialog.setWindowTitle("Go To Location");
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
dialog.setLayout(layout);
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
const int QLINE_MINIMUM_WIDTH = 300;
Application* applicationInstance = Application::getInstance();
MyAvatar* myAvatar = applicationInstance->getAvatar();
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();
QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x),
QString::number(avatarPos.y), QString::number(avatarPos.z));
QLineEdit* coordinates = new QLineEdit(currentLocation);
coordinates->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("Coordinates as x,y,z:", coordinates);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
layout->addWidget(buttons);
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
QByteArray newCoordinates;
if (coordinates->text().size() > 0) {
// the user input a new hostname, use that
QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x),
QString::number(avatarPos.y), QString::number(avatarPos.z));
QInputDialog coordinateDialog(Application::getInstance()->getWindow());
coordinateDialog.setWindowTitle("Go to Location");
coordinateDialog.setLabelText("Coordinate as x,y,z:");
coordinateDialog.setTextValue(currentLocation);
coordinateDialog.setWindowFlags(Qt::Sheet);
coordinateDialog.resize(coordinateDialog.parentWidget()->size().width() * 0.30, coordinateDialog.size().height());
int dialogReturn = coordinateDialog.exec();
if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) {
QByteArray newCoordinates;
QString delimiterPattern(",");
QStringList coordinateItems = coordinates->text().split(delimiterPattern);
QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern);
const int NUMBER_OF_COORDINATE_ITEMS = 3;
const int X_ITEM = 0;
const int Y_ITEM = 1;
@ -968,36 +931,27 @@ void Menu::goToLocation() {
}
}
}
sendFakeEnterEvent();
}
void Menu::goToUser() {
QDialog dialog;
dialog.setWindowTitle("Go To User");
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
dialog.setLayout(layout);
QInputDialog userDialog(Application::getInstance()->getWindow());
userDialog.setWindowTitle("Go to User");
userDialog.setLabelText("Destination user:");
QString username = Application::getInstance()->getProfile()->getUsername();
userDialog.setTextValue(username);
userDialog.setWindowFlags(Qt::Sheet);
userDialog.resize(userDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, userDialog.size().height());
QFormLayout* form = new QFormLayout();
layout->addLayout(form, 1);
QLineEdit* usernameLineEdit = new QLineEdit();
usernameLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
form->addRow("", usernameLineEdit);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
layout->addWidget(buttons);
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
if (!usernameLineEdit->text().isEmpty()) {
int dialogReturn = userDialog.exec();
if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) {
// there's a username entered by the user, make a request to the data-server
DataServerClient::getValuesForKeysAndUserString((QStringList() << DataServerKey::Domain << DataServerKey::Position),
usernameLineEdit->text());
userDialog.textValue());
}
sendFakeEnterEvent();
}
void Menu::bandwidthDetails() {

View file

@ -146,6 +146,7 @@ namespace MenuOption {
const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats";
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString ChatCircling = "Chat Circling";
const QString Collisions = "Collisions";
const QString CopyVoxels = "Copy";
const QString CoverageMap = "Render Coverage Map";
@ -164,6 +165,7 @@ namespace MenuOption {
const QString EchoAudio = "Echo Audio";
const QString ExportVoxels = "Export Voxels";
const QString ExtraDebugging = "Extra Debugging";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString HeadMouse = "Head Mouse";
const QString FaceMode = "Cycle Face Mode";
const QString FaceshiftTCP = "Faceshift (TCP)";

View file

@ -2645,23 +2645,8 @@ bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) {
void VoxelSystem::nodeKilled(Node* node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
_voxelServerCount--;
QUuid nodeUUID = node->getUUID();
qDebug("VoxelSystem... voxel server %s removed...\n", nodeUUID.toString().toLocal8Bit().constData());
if (_voxelServerCount > 0) {
// Kill any voxels from the local tree that match this nodeID
// commenting out for removal of 16 bit node IDs
lockTree();
_tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeUUID);
unlockTree();
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
} else {
// Last server, take the easy way and kill all the local voxels!
killLocalVoxels();
}
}
}

View file

@ -96,8 +96,6 @@ Avatar::Avatar(Node* owningNode) :
_leadingAvatar(NULL),
_voxels(this),
_moving(false),
_hoverOnDuration(0.0f),
_hoverOffDuration(0.0f),
_initialized(false),
_handHoldingPosition(0.0f, 0.0f, 0.0f),
_maxArmLength(0.0f),
@ -257,6 +255,11 @@ Avatar::~Avatar() {
delete _balls;
}
void Avatar::deleteOrDeleteLater() {
this->deleteLater();
}
void Avatar::init() {
_head.init();
_hand.init();
@ -387,30 +390,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
}
}
// head scale grows when avatar is looked at
const float BASE_MAX_SCALE = 3.0f;
float maxScale = BASE_MAX_SCALE * glm::distance(_position, Application::getInstance()->getCamera()->getPosition());
if (Application::getInstance()->getLookatTargetAvatar() == this) {
_hoverOnDuration += deltaTime;
_hoverOffDuration = 0.0f;
const float GROW_DELAY = 1.0f;
const float GROW_RATE = 0.25f;
if (_hoverOnDuration > GROW_DELAY) {
_head.setScale(glm::mix(_head.getScale(), maxScale, GROW_RATE));
}
} else {
_hoverOnDuration = 0.0f;
_hoverOffDuration += deltaTime;
const float SHRINK_DELAY = 1.0f;
const float SHRINK_RATE = 0.25f;
if (_hoverOffDuration > SHRINK_DELAY) {
_head.setScale(glm::mix(_head.getScale(), 1.0f, SHRINK_RATE));
}
}
_skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition;

View file

@ -134,6 +134,7 @@ public:
Avatar(Node* owningNode = NULL);
~Avatar();
void deleteOrDeleteLater();
void init();
void simulate(float deltaTime, Transmitter* transmitter);
@ -227,8 +228,6 @@ protected:
AvatarVoxelSystem _voxels;
bool _moving; ///< set when position is changing
float _hoverOnDuration;
float _hoverOffDuration;
// protected methods...
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }

View file

@ -17,6 +17,7 @@
#include "Application.h"
#include "DataServerClient.h"
#include "Menu.h"
#include "MyAvatar.h"
#include "Physics.h"
#include "devices/OculusManager.h"
@ -830,6 +831,7 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
// reset hand and arm positions according to hand movement
glm::vec3 up = orientation * IDENTITY_UP;
bool pointing = false;
if (enableHandMovement && glm::length(_mouseRayDirection) > EPSILON && !Application::getInstance()->isMouseHidden()) {
// confine to the approximate shoulder plane
glm::vec3 pointDirection = _mouseRayDirection;
@ -841,6 +843,7 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
}
const float FAR_AWAY_POINT = TREE_SCALE;
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = _mouseRayOrigin + pointDirection * FAR_AWAY_POINT;
pointing = true;
}
_avatarTouch.setMyBodyPosition(_position);
@ -932,6 +935,8 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
if (_mousePressed) {
_handState = HAND_STATE_GRASPING;
} else if (pointing) {
_handState = HAND_STATE_POINTING;
} else {
_handState = HAND_STATE_NULL;
}
@ -1059,6 +1064,11 @@ void MyAvatar::updateAvatarCollisions(float deltaTime) {
// detect collisions with other avatars and respond
void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) {
// for now, don't collide if we have a new skeleton
if (_skeletonModel.isActive()) {
return;
}
glm::vec3 bodyPushForce = glm::vec3(0.0f, 0.0f, 0.0f);
// loop through the body balls of each avatar to check for every possible collision
@ -1110,6 +1120,10 @@ bool operator<(const SortedAvatar& s1, const SortedAvatar& s2) {
}
void MyAvatar::updateChatCircle(float deltaTime) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::ChatCircling)) {
return;
}
// find all members and sort by distance
QVector<SortedAvatar> sortedAvatars;
NodeList* nodeList = NodeList::getInstance();

View file

@ -28,7 +28,12 @@ void SkeletonModel::simulate(float deltaTime) {
Model::simulate(deltaTime);
setRightHandPosition(_owningAvatar->getHandPosition());
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
const float HAND_RESTORATION_RATE = 0.25f;
restoreRightHandPosition(HAND_RESTORATION_RATE);
} else {
setRightHandPosition(_owningAvatar->getHandPosition());
}
}
bool SkeletonModel::render(float alpha) {

View file

@ -68,7 +68,7 @@ void Faceshift::update() {
(_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))));
// compute and subtract the long term average
const float LONG_TERM_AVERAGE_SMOOTHING = 0.9999f;
const float LONG_TERM_AVERAGE_SMOOTHING = 0.999f;
if (!_longTermAverageInitialized) {
_longTermAverageEyePitch = eulers.x;
_longTermAverageEyeYaw = eulers.y;

View file

@ -477,6 +477,10 @@ bool Model::setLeftHandPosition(const glm::vec3& position) {
return isActive() && setJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, position);
}
bool Model::restoreLeftHandPosition(float percent) {
return isActive() && restoreJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, percent);
}
bool Model::setLeftHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().leftHandJointIndex, rotation);
}
@ -485,6 +489,10 @@ bool Model::setRightHandPosition(const glm::vec3& position) {
return isActive() && setJointPosition(_geometry->getFBXGeometry().rightHandJointIndex, position);
}
bool Model::restoreRightHandPosition(float percent) {
return isActive() && restoreJointPosition(_geometry->getFBXGeometry().rightHandJointIndex, percent);
}
bool Model::setRightHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().rightHandJointIndex, rotation);
}
@ -617,6 +625,19 @@ bool Model::setJointRotation(int jointIndex, const glm::quat& rotation) {
return true;
}
bool Model::restoreJointPosition(int jointIndex, float percent) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
foreach (int index, freeLineage) {
_jointStates[index].rotation = safeMix(_jointStates[index].rotation, geometry.joints.at(index).rotation, percent);
}
return true;
}
void Model::deleteGeometry() {
foreach (Model* attachment, _attachments) {
delete attachment;

View file

@ -74,6 +74,11 @@ public:
/// \return whether or not the left hand joint was found
bool setLeftHandPosition(const glm::vec3& position);
/// Restores some percentage of the default position of the left hand.
/// \param percent the percentage of the default position to restore
/// \return whether or not the left hand joint was found
bool restoreLeftHandPosition(float percent = 1.0f);
/// Sets the rotation of the left hand.
/// \return whether or not the left hand joint was found
bool setLeftHandRotation(const glm::quat& rotation);
@ -82,6 +87,11 @@ public:
/// \return whether or not the right hand joint was found
bool setRightHandPosition(const glm::vec3& position);
/// Restores some percentage of the default position of the right hand.
/// \param percent the percentage of the default position to restore
/// \return whether or not the right hand joint was found
bool restoreRightHandPosition(float percent = 1.0f);
/// Sets the rotation of the right hand.
/// \return whether or not the right hand joint was found
bool setRightHandRotation(const glm::quat& rotation);
@ -130,6 +140,12 @@ protected:
bool setJointPosition(int jointIndex, const glm::vec3& position);
bool setJointRotation(int jointIndex, const glm::quat& rotation);
/// Restores the indexed joint to its default position.
/// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
/// the original position
/// \return true if the joint was found
bool restoreJointPosition(int jointIndex, float percent = 1.0f);
private:
void deleteGeometry();

View file

@ -44,7 +44,7 @@ Node::~Node() {
delete _localSocket;
if (_linkedData) {
_linkedData->deleteLater();
_linkedData->deleteOrDeleteLater();
}
delete _bytesReceivedMovingAverage;

View file

@ -16,4 +16,8 @@ NodeData::NodeData(Node* owningNode) :
NodeData::~NodeData() {
}
void NodeData::deleteOrDeleteLater() {
delete this;
}

View file

@ -21,6 +21,8 @@ public:
virtual ~NodeData() = 0;
virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
virtual void deleteOrDeleteLater();
Node* getOwningNode() { return _owningNode; }
protected:
Node* _owningNode;

View file

@ -687,13 +687,10 @@ unsigned NodeList::broadcastToNodes(unsigned char* broadcastData, size_t dataByt
for(NodeList::iterator node = begin(); node != end(); node++) {
// only send to the NodeTypes we are asked to send to.
if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
if (node->getActiveSocket()) {
if (getNodeActiveSocketOrPing(&(*node))) {
// we know which socket is good for this node, send there
_nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes);
++n;
} else {
// we don't have an active link to this node, ping it to set that up
pingPublicAndLocalSocketsForInactiveNode(&(*node));
}
}
}
@ -718,6 +715,15 @@ void NodeList::possiblyPingInactiveNodes() {
}
}
sockaddr* NodeList::getNodeActiveSocketOrPing(Node* node) {
if (node->getActiveSocket()) {
return node->getActiveSocket();
} else {
pingPublicAndLocalSocketsForInactiveNode(node);
return NULL;
}
}
void NodeList::activateSocketFromNodeCommunication(sockaddr *nodeAddress) {
for(NodeList::iterator node = begin(); node != end(); node++) {
if (!node->getActiveSocket()) {

View file

@ -141,6 +141,7 @@ public:
void removeDomainListener(DomainChangeListener* listener);
void possiblyPingInactiveNodes();
sockaddr* getNodeActiveSocketOrPing(Node* node);
private:
static NodeList* _sharedInstance;

View file

@ -16,10 +16,15 @@
#include "SharedUtil.h"
#include "OctalCode.h"
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode) {
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes) {
if (maxBytes == OVERFLOWED_OCTCODE_BUFFER) {
return OVERFLOWED_OCTCODE_BUFFER;
}
assert(octalCode);
if (*octalCode == 255) {
return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1);
int newMaxBytes = (maxBytes == UNKNOWN_OCTCODE_LENGTH) ? UNKNOWN_OCTCODE_LENGTH : maxBytes - 1;
return *octalCode + numberOfThreeBitSectionsInCode(octalCode + 1, newMaxBytes);
} else {
return *octalCode;
}

View file

@ -24,7 +24,15 @@ void printOctalCode(const unsigned char* octalCode);
int bytesRequiredForCodeLength(unsigned char threeBitCodes);
int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode);
unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber);
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode);
const int OVERFLOWED_OCTCODE_BUFFER = -1;
const int UNKNOWN_OCTCODE_LENGTH = -2;
/// will return -1 if there is an error in the octcode encoding, or it would overflow maxBytes
/// \param const unsigned char* octalCode the octalcode to decode
/// \param int maxBytes number of bytes that octalCode is expected to be, -1 if unknown
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes = UNKNOWN_OCTCODE_LENGTH);
unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels);
unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode,
bool includeColorSpace = false);

View file

@ -91,23 +91,23 @@ bool PacketSender::process() {
while (keepGoing) {
uint64_t SEND_INTERVAL_USECS = (_packetsPerSecond == 0) ? USECS_PER_SECOND : (USECS_PER_SECOND / _packetsPerSecond);
lock();
NetworkPacket& packet = _packets.front();
NetworkPacket temporary = packet; // make a copy
_packets.erase(_packets.begin());
packetsLeft = _packets.size();
unlock();
// send the packet through the NodeList...
UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket();
nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength());
nodeSocket->send(&temporary.getAddress(), temporary.getData(), temporary.getLength());
packetsThisCall++;
if (_notify) {
_notify->packetSentNotification(packet.getLength());
_notify->packetSentNotification(temporary.getLength());
}
lock();
_packets.erase(_packets.begin());
unlock();
packetsLeft = _packets.size();
// in threaded mode, we go till we're empty
if (isThreaded()) {

View file

@ -31,12 +31,13 @@ bool ReceivedPacketProcessor::process() {
usleep(RECEIVED_THREAD_SLEEP_INTERVAL);
}
while (_packets.size() > 0) {
NetworkPacket& packet = _packets.front();
processPacket(packet.getAddress(), packet.getData(), packet.getLength());
lock();
_packets.erase(_packets.begin());
unlock();
lock(); // lock to make sure nothing changes on us
NetworkPacket& packet = _packets.front(); // get the oldest packet
NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us
_packets.erase(_packets.begin()); // remove the oldest packet
unlock(); // let others add to the packets
processPacket(temporary.getAddress(), temporary.getData(), temporary.getLength()); // process our temporary copy
}
return isStillRunning(); // keep running till they terminate us
}

View file

@ -35,6 +35,8 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_lastVoxelPacketLength = 0;
_duplicatePacketCount = 0;
resetVoxelPacket();
qDebug("VoxelNodeData::VoxelNodeData() this=%p owningNode=%p\n", this, owningNode);
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
@ -42,6 +44,10 @@ void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
QUuid nodeUUID = getOwningNode()->getUUID();
_voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer);
_voxelSendThread->initialize(true);
qDebug("VoxelNodeData::initializeVoxelSendThread() this=%p owningNode=%p _voxelSendThread=%p\n",
this, getOwningNode(), _voxelSendThread);
qDebug() << "VoxelNodeData::initializeVoxelSendThread() nodeUUID=" << nodeUUID << "\n";
}
bool VoxelNodeData::packetIsDuplicate() const {
@ -112,12 +118,19 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
}
VoxelNodeData::~VoxelNodeData() {
qDebug("VoxelNodeData::~VoxelNodeData() this=%p owningNode=%p _voxelSendThread=%p\n",
this, getOwningNode(), _voxelSendThread);
QUuid nodeUUID = getOwningNode()->getUUID();
qDebug() << "VoxelNodeData::~VoxelNodeData() nodeUUID=" << nodeUUID << "\n";
delete[] _voxelPacket;
delete[] _lastVoxelPacket;
if (_voxelSendThread) {
_voxelSendThread->terminate();
delete _voxelSendThread;
qDebug("VoxelNodeData::~VoxelNodeData() DELETED _voxelSendThread=%p\n", _voxelSendThread);
}
}

View file

@ -24,7 +24,7 @@ class VoxelServer;
class VoxelNodeData : public VoxelQuery {
public:
VoxelNodeData(Node* owningNode);
~VoxelNodeData();
virtual ~VoxelNodeData();
void resetVoxelPacket(); // resets voxel packet to after "V" header

View file

@ -20,30 +20,28 @@ VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, in
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
_initialLoad(false) {
_initialLoadComplete(false),
_loadTimeUSecs(0) {
}
bool VoxelPersistThread::process() {
if (!_initialLoad) {
_initialLoad = true;
if (!_initialLoadComplete) {
uint64_t loadStarted = usecTimestampNow();
qDebug("loading voxels from file: %s...\n", _filename);
bool persistantFileRead;
_tree->lockForWrite();
{
PerformanceWarning warn(true, "Loading Voxel File", true);
persistantFileRead = _tree->readFromSVOFile(_filename);
}
_tree->unlock();
if (persistantFileRead) {
PerformanceWarning warn(true, "Voxels Re-Averaging", true);
// after done inserting all these voxels, then reaverage colors
qDebug("BEGIN Voxels Re-Averaging\n");
_tree->reaverageVoxelColors(_tree->rootNode);
qDebug("DONE WITH Voxels Re-Averaging\n");
}
_loadCompleted = time(0);
uint64_t loadDone = usecTimestampNow();
_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));
@ -61,6 +59,7 @@ bool VoxelPersistThread::process() {
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
}
uint64_t MSECS_TO_USECS = 1000;

View file

@ -21,6 +21,12 @@ public:
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
bool isInitialLoadComplete() const { return _initialLoadComplete; }
time_t* getLoadCompleted() { return &_loadCompleted; }
uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; }
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
@ -28,7 +34,10 @@ private:
VoxelTree* _tree;
const char* _filename;
int _persistInterval;
bool _initialLoad;
bool _initialLoadComplete;
time_t _loadCompleted;
uint64_t _loadTimeUSecs;
};
#endif // __voxel_server__VoxelPersistThread__

View file

@ -26,28 +26,39 @@ VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) :
bool VoxelSendThread::process() {
uint64_t start = usecTimestampNow();
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
VoxelNodeData* nodeData = NULL;
// don't do any send processing until the initial load of the voxels is complete...
if (_myServer->isInitialLoadComplete()) {
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
if (node) {
nodeData = (VoxelNodeData*) node->getLinkedData();
}
if (node) {
node->lock(); // make sure the node list doesn't kill our node while we're using it
VoxelNodeData* nodeData = NULL;
int packetsSent = 0;
nodeData = (VoxelNodeData*) 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()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
}
// 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()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
}
node->unlock(); // we're done with this node for now.
}
} else {
if (_myServer->wantsDebugVoxelSending()) {
qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n");
}
}
// dynamically sleep until we need to fire off the next set of voxels
int elapsed = (usecTimestampNow() - start);
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
@ -55,6 +66,7 @@ bool VoxelSendThread::process() {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
return isStillRunning(); // keep running till they terminate us
}
@ -113,8 +125,6 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
/// Version of voxel distributor that sends the deepest LOD level at once
int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
_myServer->lockTree();
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
@ -285,10 +295,13 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
_myServer->getServerTree().lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params);
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
if (nodeData->getAvailable() >= bytesWritten) {
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
@ -356,8 +369,6 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
} // end if bag wasn't empty, and so we sent stuff...
_myServer->unlockTree();
return truePacketsSent;
}

View file

@ -10,6 +10,7 @@
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <time.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
@ -45,7 +46,11 @@ const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxe
void attachVoxelNodeDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new VoxelNodeData(newNode));
VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode);
QUuid nodeUUID = newNode->getUUID();
qDebug("attachVoxelNodeDataToNode() newNode=%p voxelNodeData=%p\n", newNode, voxelNodeData);
qDebug() << "attachVoxelNodeDataToNode() node UUID:" << nodeUUID << "\n";
newNode->setLinkedData(voxelNodeData);
}
}
@ -71,7 +76,10 @@ VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assign
_voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL;
_parsedArgV = NULL;
_started = time(0);
_startedUSecs = usecTimestampNow();
_theInstance = this;
}
@ -106,6 +114,19 @@ void VoxelServer::initMongoose(int port) {
int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
const struct mg_request_info* ri = mg_get_request_info(connection);
#ifdef FORCE_CRASH
if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) {
qDebug() << "About to force a crash!\n";
int foo;
int* forceCrash = &foo;
mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n");
mg_printf(connection, "%s", "forcing a crash....\r\n");
delete[] forceCrash;
mg_printf(connection, "%s", "did it crash....\r\n");
return 1;
}
#endif
if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) {
uint64_t checkSum;
@ -114,6 +135,86 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "Your Voxel Server is running.\r\n");
mg_printf(connection, "%s", "\r\n");
tm* localtm = localtime(&GetInstance()->_started);
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
mg_printf(connection, "Running since: %s", buffer);
// Convert now to tm struct for UTC
tm* gmtm = gmtime(&GetInstance()->_started);
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
mg_printf(connection, " [%s UTM] ", buffer);
}
mg_printf(connection, "%s", "\r\n");
uint64_t now = usecTimestampNow();
const int USECS_PER_MSEC = 1000;
uint64_t msecsElapsed = (now - GetInstance()->_startedUSecs) / USECS_PER_MSEC;
const int MSECS_PER_SEC = 1000;
const int SECS_PER_MIN = 60;
const int MIN_PER_HOUR = 60;
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
mg_printf(connection, "%s", "Uptime: ");
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
if (minutes > 0) {
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
}
if (seconds > 0) {
mg_printf(connection, "%.3f seconds ", seconds);
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display voxel file load time
if (GetInstance()->isInitialLoadComplete()) {
tm* voxelsLoadedAtLocal = localtime(GetInstance()->getLoadCompleted());
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);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(GetInstance()->getLoadCompleted());
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
mg_printf(connection, " [%s UTM] ", buffer);
}
mg_printf(connection, "%s", "\r\n");
uint64_t msecsElapsed = GetInstance()->getLoadElapsedTime() / USECS_PER_MSEC;;
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
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: ");
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
if (minutes > 0) {
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
}
if (seconds > 0) {
mg_printf(connection, "%.3f seconds", seconds);
}
mg_printf(connection, "%s", "\r\n");
} else {
mg_printf(connection, "%s", "Voxels not yet loaded...\r\n");
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "Configuration: \r\n ");
@ -283,8 +384,6 @@ void VoxelServer::run() {
parsePayload();
}
pthread_mutex_init(&_treeLock, NULL);
qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort";
@ -438,12 +537,24 @@ void VoxelServer::run() {
_voxelServerPacketProcessor->initialize(true);
}
qDebug("Now running...\n");
// Convert now to tm struct for local timezone
tm* localtm = localtime(&_started);
const int MAX_TIME_LENGTH = 128;
char localBuffer[MAX_TIME_LENGTH] = { 0 };
char utcBuffer[MAX_TIME_LENGTH] = { 0 };
strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
// Convert now to tm struct for UTC
tm* gmtm = gmtime(&_started);
if (gmtm != NULL) {
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
}
qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n";
// loop to send to nodes requesting data
while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
break;
}
@ -499,28 +610,39 @@ void VoxelServer::run() {
}
}
}
qDebug() << "VoxelServer::run()... AFTER loop...\n";
delete _jurisdiction;
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
// properly shutdown and cleaned up.
NodeList::getInstance()->clear();
qDebug() << "VoxelServer::run()... terminating _jurisdictionSender\n";
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
qDebug() << "VoxelServer::run()... terminating _voxelServerPacketProcessor\n";
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor;
}
qDebug() << "VoxelServer::run()... terminating _voxelPersistThread\n";
if (_voxelPersistThread) {
_voxelPersistThread->terminate();
delete _voxelPersistThread;
}
// tell our NodeList we're done with notifications
qDebug() << "VoxelServer::run()... nodeList->removeHook(&_nodeWatcher)\n";
nodeList->removeHook(&_nodeWatcher);
pthread_mutex_destroy(&_treeLock);
qDebug() << "VoxelServer::run()... deleting _jurisdiction\n";
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "VoxelServer::run()... DONE\n";
}

View file

@ -11,6 +11,7 @@
#define __voxel_server__VoxelServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <Assignment.h>
@ -47,10 +48,6 @@ public:
VoxelTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
void lockTree() { pthread_mutex_lock(&_treeLock); }
void unlockTree() { pthread_mutex_unlock(&_treeLock); }
VoxelTree* getTree() { return &_serverTree; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
@ -58,6 +55,10 @@ public:
static VoxelServer* GetInstance() { return _theInstance; }
bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; }
time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; }
uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; }
private:
int _argc;
const char** _argv;
@ -79,7 +80,6 @@ private:
JurisdictionSender* _jurisdictionSender;
VoxelServerPacketProcessor* _voxelServerPacketProcessor;
VoxelPersistThread* _voxelPersistThread;
pthread_mutex_t _treeLock;
EnvironmentData _environmentData[3];
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed
@ -91,6 +91,8 @@ private:
static int civetwebRequestHandler(struct mg_connection *connection);
static VoxelServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
};
#endif // __voxel_server__VoxelServer__

View file

@ -24,6 +24,12 @@ VoxelServerPacketProcessor::VoxelServerPacketProcessor(VoxelServer* myServer) :
void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsDebugVoxelReceiving();
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket(() packetData=%p packetLength=%ld\n", packetData, packetLength);
}
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
@ -49,25 +55,54 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
int atByte = numBytesPacketHeader + sizeof(itemNumber);
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) {
unsigned char octets = (unsigned char)*voxelData;
int maxSize = packetLength - atByte;
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket(() %s packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
packetData, packetLength, voxelData, atByte, maxSize);
}
int octets = numberOfThreeBitSectionsInCode(voxelData, maxSize);
if (octets == OVERFLOWED_OCTCODE_BUFFER) {
printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
printf("bailing processing of packet!\n");
break;
}
const int COLOR_SIZE_IN_BYTES = 3;
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int voxelCodeSize = bytesRequiredForCodeLength(octets);
if (_myServer->wantShowAnimationDebug()) {
int red = voxelData[voxelCodeSize + 0];
int green = voxelData[voxelCodeSize + 1];
int blue = voxelData[voxelCodeSize + 2];
if (atByte + voxelDataSize <= packetLength) {
if (_myServer->wantShowAnimationDebug()) {
int red = voxelData[voxelCodeSize + RED_INDEX];
int green = voxelData[voxelCodeSize + GREEN_INDEX];
int blue = voxelData[voxelCodeSize + BLUE_INDEX];
float* vertices = firstVertexForCode(voxelData);
printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
delete[] vertices;
float* vertices = firstVertexForCode(voxelData);
printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
delete[] vertices;
}
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive);
_myServer->getServerTree().unlock();
// skip to next voxel edit record in the packet
voxelData += voxelDataSize;
atByte += voxelDataSize;
} else {
printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n");
break;
}
_myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive);
// skip to next
voxelData += voxelDataSize;
atByte += voxelDataSize;
}
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket(() DONE LOOPING FOR %s packetData=%p packetLength=%ld voxelData=%p atByte=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
packetData, packetLength, voxelData, atByte);
}
// Make sure our Node and NodeList knows we've heard from this node.
@ -79,9 +114,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them
_myServer->lockTree();
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength);
_myServer->unlockTree();
_myServer->getServerTree().unlock();
// Make sure our Node and NodeList knows we've heard from this node.
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);

View file

@ -46,11 +46,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
// only send to the NodeTypes that are interested in our jurisdiction details
const int numNodeTypes = 1;
const NODE_TYPE nodeTypes[numNodeTypes] = { NODE_TYPE_VOXEL_SERVER };
if (node->getActiveSocket() != NULL && memchr(nodeTypes, node->getType(), numNodeTypes)) {
if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) {
sockaddr* nodeAddress = node->getActiveSocket();
PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++;

View file

@ -109,8 +109,47 @@ JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector
init(rootOctalCode, endNodes);
}
void myDebugoutputBits(unsigned char byte, bool withNewLine) {
if (isalnum(byte)) {
printf("[ %d (%c): ", byte, byte);
} else {
printf("[ %d (0x%x): ", byte, byte);
}
for (int i = 0; i < 8; i++) {
printf("%d", byte >> (7 - i) & 1);
}
printf(" ] ");
if (withNewLine) {
printf("\n");
}
}
void myDebugPrintOctalCode(const unsigned char* octalCode, bool withNewLine) {
if (!octalCode) {
printf("NULL");
} else {
for (int i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) {
myDebugoutputBits(octalCode[i],false);
}
}
if (withNewLine) {
printf("\n");
}
}
JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHexCodes) {
qDebug("JurisdictionMap::JurisdictionMap(const char* rootHexCode=[%p] %s, const char* endNodesHexCodes=[%p] %s)\n",
rootHexCode, rootHexCode, endNodesHexCodes, endNodesHexCodes);
_rootOctalCode = hexStringToOctalCode(QString(rootHexCode));
qDebug("JurisdictionMap::JurisdictionMap() _rootOctalCode=%p octalCode=", _rootOctalCode);
myDebugPrintOctalCode(_rootOctalCode, true);
QString endNodesHexStrings(endNodesHexCodes);
QString delimiterPattern(",");
@ -120,8 +159,16 @@ JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHe
QString endNodeHexString = endNodeList.at(i);
unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString);
qDebug("JurisdictionMap::JurisdictionMap() endNodeList(%d)=%s\n",
i, endNodeHexString.toLocal8Bit().constData());
//printOctalCode(endNodeOctcode);
_endNodes.push_back(endNodeOctcode);
qDebug("JurisdictionMap::JurisdictionMap() endNodeOctcode=%p octalCode=", endNodeOctcode);
myDebugPrintOctalCode(endNodeOctcode, true);
}
}

View file

@ -35,7 +35,7 @@ void JurisdictionSender::processPacket(sockaddr& senderAddress, unsigned char*
if (node) {
QUuid nodeUUID = node->getUUID();
lockRequestingNodes();
_nodesRequestingJurisdictions.insert(nodeUUID);
_nodesRequestingJurisdictions.push(nodeUUID);
unlockRequestingNodes();
}
}
@ -59,18 +59,16 @@ bool JurisdictionSender::process() {
int nodeCount = 0;
lockRequestingNodes();
for (std::set<QUuid>::iterator nodeIterator = _nodesRequestingJurisdictions.begin();
nodeIterator != _nodesRequestingJurisdictions.end(); nodeIterator++) {
while (!_nodesRequestingJurisdictions.empty()) {
QUuid nodeUUID = *nodeIterator;
QUuid nodeUUID = _nodesRequestingJurisdictions.front();
_nodesRequestingJurisdictions.pop();
Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
if (node->getActiveSocket() != NULL) {
sockaddr* nodeAddress = node->getActiveSocket();
queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++;
// remove it from the set
_nodesRequestingJurisdictions.erase(nodeIterator);
}
}
unlockRequestingNodes();

View file

@ -11,7 +11,7 @@
#ifndef __shared__JurisdictionSender__
#define __shared__JurisdictionSender__
#include <set>
#include <queue>
#include <PacketSender.h>
#include <ReceivedPacketProcessor.h>
@ -44,6 +44,6 @@ protected:
private:
pthread_mutex_t _requestingNodeMutex;
JurisdictionMap* _jurisdictionMap;
std::set<QUuid> _nodesRequestingJurisdictions;
std::queue<QUuid> _nodesRequestingJurisdictions;
};
#endif // __shared__JurisdictionSender__

View file

@ -90,11 +90,8 @@ bool VoxelEditPacketSender::voxelServersExist() const {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
if (node->getActiveSocket()) {
if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
return true;
} else {
// we don't have an active socket for this node, ping it
nodeList->pingPublicAndLocalSocketsForInactiveNode(&(*node));
}
}
}
@ -109,12 +106,9 @@ void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned ch
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
if (node->getType() == NODE_TYPE_VOXEL_SERVER &&
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
if (node->getActiveSocket()) {
if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
sockaddr* nodeAddress = node->getActiveSocket();
queuePacketForSending(*nodeAddress, buffer, length);
} else {
// we don't have an active socket for this node, ping it
nodeList->pingPublicAndLocalSocketsForInactiveNode(&(*node));
}
}
}

View file

@ -42,6 +42,7 @@ VoxelQuery::VoxelQuery(Node* owningNode) :
}
VoxelQuery::~VoxelQuery() {
qDebug("VoxelQuery::~VoxelQuery()\n");
}
int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) {

View file

@ -35,7 +35,7 @@ class VoxelQuery : public NodeData {
public:
VoxelQuery(Node* owningNode = NULL);
~VoxelQuery();
virtual ~VoxelQuery();
int getBroadcastData(unsigned char* destinationBuffer);
int parseData(unsigned char* sourceBuffer, int numBytes);

View file

@ -581,13 +581,24 @@ void VoxelTree::processRemoveVoxelBitstream(unsigned char * bitstream, int buffe
int atByte = sizeof(short int) + numBytesForPacketHeader(bitstream);
unsigned char* voxelCode = (unsigned char*)&bitstream[atByte];
while (atByte < bufferSizeBytes) {
int codeLength = numberOfThreeBitSectionsInCode(voxelCode);
int maxSize = bufferSizeBytes - atByte;
int codeLength = numberOfThreeBitSectionsInCode(voxelCode, maxSize);
if (codeLength == OVERFLOWED_OCTCODE_BUFFER) {
printf("WARNING! Got remove voxel bitstream that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
printf("bailing processing of packet!\n");
break;
}
int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA;
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE);
voxelCode+=voxelDataSize;
atByte+=voxelDataSize;
if (atByte + voxelDataSize <= bufferSizeBytes) {
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE);
voxelCode += voxelDataSize;
atByte += voxelDataSize;
} else {
printf("WARNING! Got remove voxel bitstream that would overflow buffer, bailing processing!\n");
break;
}
}
}
@ -1795,9 +1806,10 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) {
while (!nodeBag.isEmpty()) {
VoxelNode* subTree = nodeBag.extract();
lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
unlock();
file.write((const char*)&outputBuffer[0], bytesWritten);
}
}

View file

@ -21,6 +21,7 @@
#include "VoxelEditPacketSender.h"
#include <QObject>
#include <QReadWriteLock>
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
@ -185,6 +186,13 @@ public:
// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const char *filename);
bool readFromSchematicFile(const char* filename);
// VoxelTree 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 getVoxelCount();
@ -266,6 +274,8 @@ private:
static bool nudgeCheck(VoxelNode* node, void* extraData);
void nudgeLeaf(VoxelNode* node, void* extraData);
void chunkifyLeaf(VoxelNode* node);
QReadWriteLock lock;
};
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);