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 distributions. A Windows build is planned for the future, but not currently in
development. 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 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! 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 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) 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 # link curl for synchronous script downloads
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIRS}) 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* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT); 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)); 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)); QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); 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 // 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 // hook in a constructor for audio injectorss
AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
@ -158,33 +160,14 @@ void Agent::run() {
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn();
} }
// find the audio-mixer in the NodeList so we can inject audio at it int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
if (audioMixer && audioMixer->getActiveSocket()) {
emit willSendAudioDataCallback();
}
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow();
if (usecToSleep > 0) { if (usecToSleep > 0) {
usleep(usecToSleep); 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()) { if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) {
timeval thisSend = {};
gettimeofday(&thisSend, NULL);
// allow the scripter's call back to setup visual data // allow the scripter's call back to setup visual data
emit willSendVisualDataCallback(); emit willSendVisualDataCallback();
@ -193,14 +176,13 @@ void Agent::run() {
// since we're in non-threaded mode, call process so that the packets are sent // since we're in non-threaded mode, call process so that the packets are sent
voxelScripter.getVoxelPacketSender()->process(); voxelScripter.getVoxelPacketSender()->process();
} }
if (engine.hasUncaughtException()) { if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber(); int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
} }
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)
&& packetVersionMatch(receivedData)) { && packetVersionMatch(receivedData)) {
if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {

View file

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

View file

@ -78,6 +78,11 @@ function updateCells() {
nextCells[i][j] = -1; 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), _isHoverVoxel(false),
_isHoverVoxelSounding(false), _isHoverVoxelSounding(false),
_mouseVoxelScale(1.0f / 1024.0f), _mouseVoxelScale(1.0f / 1024.0f),
_mouseVoxelScaleInitialized(false),
_justEditedVoxel(false), _justEditedVoxel(false),
_nudgeStarted(false), _nudgeStarted(false),
_lookingAlongX(false), _lookingAlongX(false),
@ -1782,7 +1783,7 @@ void Application::shrinkMirrorView() {
} }
const float MAX_AVATAR_EDIT_VELOCITY = 1.0f; 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; const float HEAD_SPHERE_RADIUS = 0.07;
static QUuid DEFAULT_NODE_ID_REF; static QUuid DEFAULT_NODE_ID_REF;
@ -1987,8 +1988,8 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3&
// deflect using Faceshift gaze data // deflect using Faceshift gaze data
glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition(); glm::vec3 origin = _myAvatar.getHead().calculateAverageEyePosition();
float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f;
const float PITCH_SCALE = 0.5f; const float PITCH_SCALE = 0.25f;
const float YAW_SCALE = 0.5f; const float YAW_SCALE = 0.25f;
lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3( lookAtSpot = origin + _myCamera.getRotation() * glm::quat(glm::radians(glm::vec3(
_faceshift.getEstimatedEyePitch() * pitchSign * PITCH_SCALE, _faceshift.getEstimatedEyeYaw() * YAW_SCALE, 0.0f))) * _faceshift.getEstimatedEyePitch() * pitchSign * PITCH_SCALE, _faceshift.getEstimatedEyeYaw() * YAW_SCALE, 0.0f))) *
glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin); glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin);
@ -2051,6 +2052,8 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin,
PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()"); PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()");
_mouseVoxel.s = 0.0f; _mouseVoxel.s = 0.0f;
bool wasInitialized = _mouseVoxelScaleInitialized;
_mouseVoxelScaleInitialized = false;
if (Menu::getInstance()->isVoxelModeActionChecked() && if (Menu::getInstance()->isVoxelModeActionChecked() &&
(fabs(_myAvatar.getVelocity().x) + (fabs(_myAvatar.getVelocity().x) +
fabs(_myAvatar.getVelocity().y) + 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 (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) {
if (distance < MAX_VOXEL_EDIT_DISTANCE) { 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 // find the nearest voxel with the desired scale
if (_mouseVoxelScale > _mouseVoxel.s) { if (_mouseVoxelScale > _mouseVoxel.s) {
// choose the larger voxel that encompasses the one selected // choose the larger voxel that encompasses the one selected
@ -2990,6 +2999,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glDisable(GL_LIGHTING); glDisable(GL_LIGHTING);
glPushMatrix(); glPushMatrix();
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
const float CUBE_EXPANSION = 1.01f;
if (_nudgeStarted) { if (_nudgeStarted) {
renderNudgeGuide(_nudgeGuidePosition.x, _nudgeGuidePosition.y, _nudgeGuidePosition.z, _nudgeVoxel.s); renderNudgeGuide(_nudgeGuidePosition.x, _nudgeGuidePosition.y, _nudgeGuidePosition.z, _nudgeVoxel.s);
renderNudgeGrid(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s, _mouseVoxel.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); _nudgeVoxel.z + _nudgeVoxel.s * 0.5f);
glColor3ub(255, 255, 255); glColor3ub(255, 255, 255);
glLineWidth(4.0f); glLineWidth(4.0f);
glutWireCube(_nudgeVoxel.s); glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION);
glPopMatrix(); glPopMatrix();
} else { } else {
renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); 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.y + _nudgeVoxel.s*0.5f,
_nudgeGuidePosition.z + _nudgeVoxel.s*0.5f); _nudgeGuidePosition.z + _nudgeVoxel.s*0.5f);
glLineWidth(4.0f); glLineWidth(4.0f);
glutWireCube(_nudgeVoxel.s); glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION);
} else { } else {
glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f, glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f,
_mouseVoxel.y + _mouseVoxel.s*0.5f, _mouseVoxel.y + _mouseVoxel.s*0.5f,
_mouseVoxel.z + _mouseVoxel.s*0.5f); _mouseVoxel.z + _mouseVoxel.s*0.5f);
glLineWidth(4.0f); glLineWidth(4.0f);
glutWireCube(_mouseVoxel.s); glutWireCube(_mouseVoxel.s * CUBE_EXPANSION);
} }
glLineWidth(1.0f); glLineWidth(1.0f);
glPopMatrix(); glPopMatrix();
@ -4105,11 +4115,13 @@ void Application::nodeKilled(Node* node) {
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs" // Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
fade.voxelDetails = rootDetails; VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
const float slightly_smaller = 0.99; fade.voxelDetails = rootDetails;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; const float slightly_smaller = 0.99;
_voxelFades.push_back(fade); fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
// we should remove it... // we should remove it...
_voxelServerJurisdictions.erase(nodeUUID); _voxelServerJurisdictions.erase(nodeUUID);
@ -4165,11 +4177,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs" // Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
fade.voxelDetails = rootDetails; VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
const float slightly_smaller = 0.99; fade.voxelDetails = rootDetails;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; const float slightly_smaller = 0.99;
_voxelFades.push_back(fade); fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
} }
// store jurisdiction details for later use // 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 // 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 VoxelDetail _mouseVoxel; // details of the voxel to be edited
float _mouseVoxelScale; // the scale for adding/removing voxels float _mouseVoxelScale; // the scale for adding/removing voxels
bool _mouseVoxelScaleInitialized;
glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit
bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel

View file

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

View file

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

View file

@ -2645,23 +2645,8 @@ bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) {
void VoxelSystem::nodeKilled(Node* node) { void VoxelSystem::nodeKilled(Node* node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
_voxelServerCount--; _voxelServerCount--;
QUuid nodeUUID = node->getUUID(); QUuid nodeUUID = node->getUUID();
qDebug("VoxelSystem... voxel server %s removed...\n", nodeUUID.toString().toLocal8Bit().constData()); 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), _leadingAvatar(NULL),
_voxels(this), _voxels(this),
_moving(false), _moving(false),
_hoverOnDuration(0.0f),
_hoverOffDuration(0.0f),
_initialized(false), _initialized(false),
_handHoldingPosition(0.0f, 0.0f, 0.0f), _handHoldingPosition(0.0f, 0.0f, 0.0f),
_maxArmLength(0.0f), _maxArmLength(0.0f),
@ -257,6 +255,11 @@ Avatar::~Avatar() {
delete _balls; delete _balls;
} }
void Avatar::deleteOrDeleteLater() {
this->deleteLater();
}
void Avatar::init() { void Avatar::init() {
_head.init(); _head.init();
_hand.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); _skeletonModel.simulate(deltaTime);
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
glm::vec3 headPosition; glm::vec3 headPosition;

View file

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

View file

@ -17,6 +17,7 @@
#include "Application.h" #include "Application.h"
#include "DataServerClient.h" #include "DataServerClient.h"
#include "Menu.h"
#include "MyAvatar.h" #include "MyAvatar.h"
#include "Physics.h" #include "Physics.h"
#include "devices/OculusManager.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 // reset hand and arm positions according to hand movement
glm::vec3 up = orientation * IDENTITY_UP; glm::vec3 up = orientation * IDENTITY_UP;
bool pointing = false;
if (enableHandMovement && glm::length(_mouseRayDirection) > EPSILON && !Application::getInstance()->isMouseHidden()) { if (enableHandMovement && glm::length(_mouseRayDirection) > EPSILON && !Application::getInstance()->isMouseHidden()) {
// confine to the approximate shoulder plane // confine to the approximate shoulder plane
glm::vec3 pointDirection = _mouseRayDirection; glm::vec3 pointDirection = _mouseRayDirection;
@ -841,6 +843,7 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
} }
const float FAR_AWAY_POINT = TREE_SCALE; const float FAR_AWAY_POINT = TREE_SCALE;
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = _mouseRayOrigin + pointDirection * FAR_AWAY_POINT; _skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = _mouseRayOrigin + pointDirection * FAR_AWAY_POINT;
pointing = true;
} }
_avatarTouch.setMyBodyPosition(_position); _avatarTouch.setMyBodyPosition(_position);
@ -932,6 +935,8 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
if (_mousePressed) { if (_mousePressed) {
_handState = HAND_STATE_GRASPING; _handState = HAND_STATE_GRASPING;
} else if (pointing) {
_handState = HAND_STATE_POINTING;
} else { } else {
_handState = HAND_STATE_NULL; _handState = HAND_STATE_NULL;
} }
@ -1059,6 +1064,11 @@ void MyAvatar::updateAvatarCollisions(float deltaTime) {
// detect collisions with other avatars and respond // detect collisions with other avatars and respond
void MyAvatar::applyCollisionWithOtherAvatar(Avatar * otherAvatar, float deltaTime) { 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); 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 // 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) { void MyAvatar::updateChatCircle(float deltaTime) {
if (!Menu::getInstance()->isOptionChecked(MenuOption::ChatCircling)) {
return;
}
// find all members and sort by distance // find all members and sort by distance
QVector<SortedAvatar> sortedAvatars; QVector<SortedAvatar> sortedAvatars;
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();

View file

@ -28,7 +28,12 @@ void SkeletonModel::simulate(float deltaTime) {
Model::simulate(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) { bool SkeletonModel::render(float alpha) {

View file

@ -68,7 +68,7 @@ void Faceshift::update() {
(_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f)))); (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))));
// compute and subtract the long term average // 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) { if (!_longTermAverageInitialized) {
_longTermAverageEyePitch = eulers.x; _longTermAverageEyePitch = eulers.x;
_longTermAverageEyeYaw = eulers.y; _longTermAverageEyeYaw = eulers.y;

View file

@ -477,6 +477,10 @@ bool Model::setLeftHandPosition(const glm::vec3& position) {
return isActive() && setJointPosition(_geometry->getFBXGeometry().leftHandJointIndex, 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) { bool Model::setLeftHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().leftHandJointIndex, 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); 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) { bool Model::setRightHandRotation(const glm::quat& rotation) {
return isActive() && setJointRotation(_geometry->getFBXGeometry().rightHandJointIndex, rotation); return isActive() && setJointRotation(_geometry->getFBXGeometry().rightHandJointIndex, rotation);
} }
@ -617,6 +625,19 @@ bool Model::setJointRotation(int jointIndex, const glm::quat& rotation) {
return true; 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() { void Model::deleteGeometry() {
foreach (Model* attachment, _attachments) { foreach (Model* attachment, _attachments) {
delete attachment; delete attachment;

View file

@ -74,6 +74,11 @@ public:
/// \return whether or not the left hand joint was found /// \return whether or not the left hand joint was found
bool setLeftHandPosition(const glm::vec3& position); 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. /// Sets the rotation of the left hand.
/// \return whether or not the left hand joint was found /// \return whether or not the left hand joint was found
bool setLeftHandRotation(const glm::quat& rotation); bool setLeftHandRotation(const glm::quat& rotation);
@ -82,6 +87,11 @@ public:
/// \return whether or not the right hand joint was found /// \return whether or not the right hand joint was found
bool setRightHandPosition(const glm::vec3& position); 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. /// Sets the rotation of the right hand.
/// \return whether or not the right hand joint was found /// \return whether or not the right hand joint was found
bool setRightHandRotation(const glm::quat& rotation); bool setRightHandRotation(const glm::quat& rotation);
@ -130,6 +140,12 @@ protected:
bool setJointPosition(int jointIndex, const glm::vec3& position); bool setJointPosition(int jointIndex, const glm::vec3& position);
bool setJointRotation(int jointIndex, const glm::quat& rotation); 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: private:
void deleteGeometry(); void deleteGeometry();

View file

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

View file

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

View file

@ -21,6 +21,8 @@ public:
virtual ~NodeData() = 0; virtual ~NodeData() = 0;
virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0; virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
virtual void deleteOrDeleteLater();
Node* getOwningNode() { return _owningNode; } Node* getOwningNode() { return _owningNode; }
protected: protected:
Node* _owningNode; 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++) { for(NodeList::iterator node = begin(); node != end(); node++) {
// only send to the NodeTypes we are asked to send to. // only send to the NodeTypes we are asked to send to.
if (memchr(nodeTypes, node->getType(), numNodeTypes)) { if (memchr(nodeTypes, node->getType(), numNodeTypes)) {
if (node->getActiveSocket()) { if (getNodeActiveSocketOrPing(&(*node))) {
// we know which socket is good for this node, send there // we know which socket is good for this node, send there
_nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes); _nodeSocket.send(node->getActiveSocket(), broadcastData, dataBytes);
++n; ++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) { void NodeList::activateSocketFromNodeCommunication(sockaddr *nodeAddress) {
for(NodeList::iterator node = begin(); node != end(); node++) { for(NodeList::iterator node = begin(); node != end(); node++) {
if (!node->getActiveSocket()) { if (!node->getActiveSocket()) {

View file

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

View file

@ -16,10 +16,15 @@
#include "SharedUtil.h" #include "SharedUtil.h"
#include "OctalCode.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); assert(octalCode);
if (*octalCode == 255) { 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 { } else {
return *octalCode; return *octalCode;
} }

View file

@ -24,7 +24,15 @@ void printOctalCode(const unsigned char* octalCode);
int bytesRequiredForCodeLength(unsigned char threeBitCodes); int bytesRequiredForCodeLength(unsigned char threeBitCodes);
int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode); int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsigned char* descendantOctalCode);
unsigned char* childOctalCode(const unsigned char* parentOctalCode, char childNumber); 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* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels);
unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode, unsigned char* rebaseOctalCode(const unsigned char* originalOctalCode, const unsigned char* newParentOctalCode,
bool includeColorSpace = false); bool includeColorSpace = false);

View file

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

View file

@ -31,12 +31,13 @@ bool ReceivedPacketProcessor::process() {
usleep(RECEIVED_THREAD_SLEEP_INTERVAL); usleep(RECEIVED_THREAD_SLEEP_INTERVAL);
} }
while (_packets.size() > 0) { while (_packets.size() > 0) {
NetworkPacket& packet = _packets.front();
processPacket(packet.getAddress(), packet.getData(), packet.getLength());
lock(); lock(); // lock to make sure nothing changes on us
_packets.erase(_packets.begin()); NetworkPacket& packet = _packets.front(); // get the oldest packet
unlock(); 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 return isStillRunning(); // keep running till they terminate us
} }

View file

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

View file

@ -24,7 +24,7 @@ class VoxelServer;
class VoxelNodeData : public VoxelQuery { class VoxelNodeData : public VoxelQuery {
public: public:
VoxelNodeData(Node* owningNode); VoxelNodeData(Node* owningNode);
~VoxelNodeData(); virtual ~VoxelNodeData();
void resetVoxelPacket(); // resets voxel packet to after "V" header 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), _tree(tree),
_filename(filename), _filename(filename),
_persistInterval(persistInterval), _persistInterval(persistInterval),
_initialLoad(false) { _initialLoadComplete(false),
_loadTimeUSecs(0) {
} }
bool VoxelPersistThread::process() { bool VoxelPersistThread::process() {
if (!_initialLoad) { if (!_initialLoadComplete) {
_initialLoad = true; uint64_t loadStarted = usecTimestampNow();
qDebug("loading voxels from file: %s...\n", _filename); qDebug("loading voxels from file: %s...\n", _filename);
bool persistantFileRead; bool persistantFileRead;
_tree->lockForWrite();
{ {
PerformanceWarning warn(true, "Loading Voxel File", true); PerformanceWarning warn(true, "Loading Voxel File", true);
persistantFileRead = _tree->readFromSVOFile(_filename); persistantFileRead = _tree->readFromSVOFile(_filename);
} }
_tree->unlock();
if (persistantFileRead) { _loadCompleted = time(0);
PerformanceWarning warn(true, "Voxels Re-Averaging", true); uint64_t loadDone = usecTimestampNow();
_loadTimeUSecs = loadDone - loadStarted;
// 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");
}
_tree->clearDirtyBit(); // the tree is clean since we just loaded it _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 voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
@ -61,6 +59,7 @@ bool VoxelPersistThread::process() {
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet); VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
} }
uint64_t MSECS_TO_USECS = 1000; uint64_t MSECS_TO_USECS = 1000;

View file

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

View file

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

View file

@ -10,6 +10,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <time.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QString> #include <QtCore/QString>
@ -45,7 +46,11 @@ const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxe
void attachVoxelNodeDataToNode(Node* newNode) { void attachVoxelNodeDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) { 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; _voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL; _voxelPersistThread = NULL;
_parsedArgV = NULL; _parsedArgV = NULL;
_started = time(0);
_startedUSecs = usecTimestampNow();
_theInstance = this; _theInstance = this;
} }
@ -106,6 +114,19 @@ void VoxelServer::initMongoose(int port) {
int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
const struct mg_request_info* ri = mg_get_request_info(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) { if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) {
uint64_t checkSum; 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", "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", "\r\n");
mg_printf(connection, "%s", "Configuration: \r\n "); mg_printf(connection, "%s", "Configuration: \r\n ");
@ -283,8 +384,6 @@ void VoxelServer::run() {
parsePayload(); parsePayload();
} }
pthread_mutex_init(&_treeLock, NULL);
qInstallMessageHandler(Logging::verboseMessageHandler); qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort"; const char* STATUS_PORT = "--statusPort";
@ -438,12 +537,24 @@ void VoxelServer::run() {
_voxelServerPacketProcessor->initialize(true); _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 // loop to send to nodes requesting data
while (true) { while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
break; 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) { if (_jurisdictionSender) {
_jurisdictionSender->terminate(); _jurisdictionSender->terminate();
delete _jurisdictionSender; delete _jurisdictionSender;
} }
qDebug() << "VoxelServer::run()... terminating _voxelServerPacketProcessor\n";
if (_voxelServerPacketProcessor) { if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate(); _voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor; delete _voxelServerPacketProcessor;
} }
qDebug() << "VoxelServer::run()... terminating _voxelPersistThread\n";
if (_voxelPersistThread) { if (_voxelPersistThread) {
_voxelPersistThread->terminate(); _voxelPersistThread->terminate();
delete _voxelPersistThread; delete _voxelPersistThread;
} }
// tell our NodeList we're done with notifications // tell our NodeList we're done with notifications
qDebug() << "VoxelServer::run()... nodeList->removeHook(&_nodeWatcher)\n";
nodeList->removeHook(&_nodeWatcher); 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__ #define __voxel_server__VoxelServer__
#include <QStringList> #include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <Assignment.h> #include <Assignment.h>
@ -47,10 +48,6 @@ public:
VoxelTree& getServerTree() { return _serverTree; } VoxelTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; } 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; } int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; } bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; } EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
@ -58,6 +55,10 @@ public:
static VoxelServer* GetInstance() { return _theInstance; } 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: private:
int _argc; int _argc;
const char** _argv; const char** _argv;
@ -79,7 +80,6 @@ private:
JurisdictionSender* _jurisdictionSender; JurisdictionSender* _jurisdictionSender;
VoxelServerPacketProcessor* _voxelServerPacketProcessor; VoxelServerPacketProcessor* _voxelServerPacketProcessor;
VoxelPersistThread* _voxelPersistThread; VoxelPersistThread* _voxelPersistThread;
pthread_mutex_t _treeLock;
EnvironmentData _environmentData[3]; EnvironmentData _environmentData[3];
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed
@ -91,6 +91,8 @@ private:
static int civetwebRequestHandler(struct mg_connection *connection); static int civetwebRequestHandler(struct mg_connection *connection);
static VoxelServer* _theInstance; static VoxelServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
}; };
#endif // __voxel_server__VoxelServer__ #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) { 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); int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) { 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); int atByte = numBytesPacketHeader + sizeof(itemNumber);
unsigned char* voxelData = (unsigned char*)&packetData[atByte]; unsigned char* voxelData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) { 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; const int COLOR_SIZE_IN_BYTES = 3;
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES; int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int voxelCodeSize = bytesRequiredForCodeLength(octets); int voxelCodeSize = bytesRequiredForCodeLength(octets);
if (_myServer->wantShowAnimationDebug()) { if (atByte + voxelDataSize <= packetLength) {
int red = voxelData[voxelCodeSize + 0]; if (_myServer->wantShowAnimationDebug()) {
int green = voxelData[voxelCodeSize + 1]; int red = voxelData[voxelCodeSize + RED_INDEX];
int blue = voxelData[voxelCodeSize + 2]; int green = voxelData[voxelCodeSize + GREEN_INDEX];
int blue = voxelData[voxelCodeSize + BLUE_INDEX];
float* vertices = firstVertexForCode(voxelData); 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); printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
delete[] vertices; 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 if (debugProcessPacket) {
voxelData += voxelDataSize; printf("VoxelServerPacketProcessor::processPacket(() DONE LOOPING FOR %s packetData=%p packetLength=%ld voxelData=%p atByte=%d\n",
atByte += voxelDataSize; 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. // 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) { } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them // Send these bits off to the VoxelTree class to process them
_myServer->lockTree(); _myServer->getServerTree().lockForWrite();
_myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength); _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. // Make sure our Node and NodeList knows we've heard from this node.
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);

View file

@ -46,11 +46,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
if (nodeList->getNodeActiveSocketOrPing(&(*node)) && node->getType() == NODE_TYPE_VOXEL_SERVER) {
// 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)) {
sockaddr* nodeAddress = node->getActiveSocket(); sockaddr* nodeAddress = node->getActiveSocket();
PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut); PacketSender::queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++; nodeCount++;

View file

@ -109,8 +109,47 @@ JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector
init(rootOctalCode, endNodes); 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) { 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)); _rootOctalCode = hexStringToOctalCode(QString(rootHexCode));
qDebug("JurisdictionMap::JurisdictionMap() _rootOctalCode=%p octalCode=", _rootOctalCode);
myDebugPrintOctalCode(_rootOctalCode, true);
QString endNodesHexStrings(endNodesHexCodes); QString endNodesHexStrings(endNodesHexCodes);
QString delimiterPattern(","); QString delimiterPattern(",");
@ -120,8 +159,16 @@ JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHe
QString endNodeHexString = endNodeList.at(i); QString endNodeHexString = endNodeList.at(i);
unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString); unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString);
qDebug("JurisdictionMap::JurisdictionMap() endNodeList(%d)=%s\n",
i, endNodeHexString.toLocal8Bit().constData());
//printOctalCode(endNodeOctcode); //printOctalCode(endNodeOctcode);
_endNodes.push_back(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) { if (node) {
QUuid nodeUUID = node->getUUID(); QUuid nodeUUID = node->getUUID();
lockRequestingNodes(); lockRequestingNodes();
_nodesRequestingJurisdictions.insert(nodeUUID); _nodesRequestingJurisdictions.push(nodeUUID);
unlockRequestingNodes(); unlockRequestingNodes();
} }
} }
@ -59,18 +59,16 @@ bool JurisdictionSender::process() {
int nodeCount = 0; int nodeCount = 0;
lockRequestingNodes(); lockRequestingNodes();
for (std::set<QUuid>::iterator nodeIterator = _nodesRequestingJurisdictions.begin(); while (!_nodesRequestingJurisdictions.empty()) {
nodeIterator != _nodesRequestingJurisdictions.end(); nodeIterator++) {
QUuid nodeUUID = *nodeIterator; QUuid nodeUUID = _nodesRequestingJurisdictions.front();
_nodesRequestingJurisdictions.pop();
Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID);
if (node->getActiveSocket() != NULL) { if (node->getActiveSocket() != NULL) {
sockaddr* nodeAddress = node->getActiveSocket(); sockaddr* nodeAddress = node->getActiveSocket();
queuePacketForSending(*nodeAddress, bufferOut, sizeOut); queuePacketForSending(*nodeAddress, bufferOut, sizeOut);
nodeCount++; nodeCount++;
// remove it from the set
_nodesRequestingJurisdictions.erase(nodeIterator);
} }
} }
unlockRequestingNodes(); unlockRequestingNodes();

View file

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

View file

@ -90,11 +90,8 @@ bool VoxelEditPacketSender::voxelServersExist() const {
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
// only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
if (node->getType() == NODE_TYPE_VOXEL_SERVER) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
if (node->getActiveSocket()) { if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
return true; 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 // only send to the NodeTypes that are NODE_TYPE_VOXEL_SERVER
if (node->getType() == NODE_TYPE_VOXEL_SERVER && if (node->getType() == NODE_TYPE_VOXEL_SERVER &&
((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) { ((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
if (node->getActiveSocket()) { if (nodeList->getNodeActiveSocketOrPing(&(*node))) {
sockaddr* nodeAddress = node->getActiveSocket(); sockaddr* nodeAddress = node->getActiveSocket();
queuePacketForSending(*nodeAddress, buffer, length); 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() { VoxelQuery::~VoxelQuery() {
qDebug("VoxelQuery::~VoxelQuery()\n");
} }
int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) { int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) {

View file

@ -35,7 +35,7 @@ class VoxelQuery : public NodeData {
public: public:
VoxelQuery(Node* owningNode = NULL); VoxelQuery(Node* owningNode = NULL);
~VoxelQuery(); virtual ~VoxelQuery();
int getBroadcastData(unsigned char* destinationBuffer); int getBroadcastData(unsigned char* destinationBuffer);
int parseData(unsigned char* sourceBuffer, int numBytes); 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); int atByte = sizeof(short int) + numBytesForPacketHeader(bitstream);
unsigned char* voxelCode = (unsigned char*)&bitstream[atByte]; unsigned char* voxelCode = (unsigned char*)&bitstream[atByte];
while (atByte < bufferSizeBytes) { 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; int voxelDataSize = bytesRequiredForCodeLength(codeLength) + SIZE_OF_COLOR_DATA;
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE); if (atByte + voxelDataSize <= bufferSizeBytes) {
deleteVoxelCodeFromTree(voxelCode, COLLAPSE_EMPTY_TREE);
voxelCode+=voxelDataSize; voxelCode += voxelDataSize;
atByte+=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()) { while (!nodeBag.isEmpty()) {
VoxelNode* subTree = nodeBag.extract(); 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); EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
unlock();
file.write((const char*)&outputBuffer[0], bytesWritten); file.write((const char*)&outputBuffer[0], bytesWritten);
} }
} }

View file

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