diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0a85a81692..2bbd549d4a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "Agent.h" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 635e5c16d7..64a50a5b99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -67,14 +68,14 @@ using namespace std; static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; -static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored +static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) static QTimer* idleTimer = NULL; const int STARTUP_JITTER_SAMPLES = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / 2; - // Startup optimistically with small jitter buffer that + // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. const int MIRROR_VIEW_TOP_PADDING = 5; @@ -129,7 +130,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _perfStatsOn(false), _chatEntryOn(false), _audio(&_audioScope, STARTUP_JITTER_SAMPLES), - _stopNetworkReceiveThread(false), + _stopNetworkReceiveThread(false), _voxelProcessor(), _voxelHideShowThread(&_voxels), _voxelEditSender(this), @@ -156,24 +157,24 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window->setMenuBar(Menu::getInstance()); qDebug("[VERSION] Build sequence: %i\n", BUILD_VERSION); - + unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } - + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AGENT, listenPort); - + // put the audio processing on a separate thread QThread* audioThread = new QThread(this); - + _audio.moveToThread(audioThread); connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); - + audioThread->start(); - + nodeList->addHook(&_voxels); nodeList->addHook(this); nodeList->addDomainListener(this); @@ -184,10 +185,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); - + // set the associated application properties applicationInfo.beginGroup("INFO"); - + setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(applicationInfo.value("version").toString()); setOrganizationName(applicationInfo.value("organizationName").toString()); @@ -198,52 +199,55 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); - + // the callback for our instance of NodeList is attachNewHeadToNode nodeList->linkedDataCreateCallback = &attachNewHeadToNode; - + #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); #endif - + // tell the NodeList instance who to tell the domain server we care about - const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, + const char nodeTypesOfInterest[] = {NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER, NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER}; nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000); - + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - + _networkAccessManager = new QNetworkAccessManager(this); QNetworkDiskCache* cache = new QNetworkDiskCache(_networkAccessManager); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); _networkAccessManager->setCache(cache); - + _window->setCentralWidget(_glWidget); - + restoreSizeAndPosition(); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); - + // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); - + // initialization continues in initializeGL when OpenGL context is ready - + // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); _particleEditSender.setServerJurisdictions(&_particleServerJurisdictions); + Particle::setVoxelEditPacketSender(&_voxelEditSender); + Particle::setParticleEditPacketSender(&_particleEditSender); + // For now we're going to set the PPS for outbound packets to be super high, this is - // probably not the right long term solution. But for now, we're going to do this to + // probably not the right long term solution. But for now, we're going to do this to // allow you to move a particle around in your hand _particleEditSender.setPacketsPerSecond(3000); // super high!! - + // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); } @@ -251,14 +255,14 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : Application::~Application() { qInstallMessageHandler(NULL); - + // make sure we don't call the idle timer any more delete idleTimer; // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); - + storeSizeAndPosition(); NodeList::getInstance()->removeHook(&_voxels); NodeList::getInstance()->removeHook(this); @@ -278,59 +282,59 @@ Application::~Application() { void Application::restoreSizeAndPosition() { QSettings* settings = new QSettings(this); QRect available = desktop()->availableGeometry(); - + settings->beginGroup("Window"); - + int x = (int)loadSetting(settings, "x", 0); int y = (int)loadSetting(settings, "y", 0); _window->move(x, y); - + int width = (int)loadSetting(settings, "width", available.width()); int height = (int)loadSetting(settings, "height", available.height()); _window->resize(width, height); - + settings->endGroup(); } void Application::storeSizeAndPosition() { QSettings* settings = new QSettings(this); - + settings->beginGroup("Window"); - + settings->setValue("width", _window->rect().width()); settings->setValue("height", _window->rect().height()); - + settings->setValue("x", _window->pos().x()); settings->setValue("y", _window->pos().y()); - + settings->endGroup(); } void Application::initializeGL() { qDebug( "Created Display Window.\n" ); - + // initialize glut for shape drawing; Qt apparently initializes it on OS X #ifndef __APPLE__ int argc = 0; glutInit(&argc, 0); #endif - + // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large // field of view and near and far clip to make it interesting. //viewFrustumOffsetCamera.setFieldOfView(90.0); _viewFrustumOffsetCamera.setNearClip(0.1); _viewFrustumOffsetCamera.setFarClip(500.0 * TREE_SCALE); - + initDisplay(); qDebug( "Initialized Display.\n" ); - + init(); qDebug( "Init() complete.\n" ); - + // create thread for receipt of data via UDP if (_enableNetworkThread) { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); - qDebug("Network receive thread created.\n"); + qDebug("Network receive thread created.\n"); } // create thread for parsing of voxel data independent of the main network and rendering threads @@ -341,21 +345,21 @@ void Application::initializeGL() { if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created.\n"); } - + // call terminate before exiting connect(this, SIGNAL(aboutToQuit()), SLOT(terminate())); - + // call our timer function every second QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(timer())); timer->start(1000); - + // call our idle function whenever we can idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); _idleLoopStdev.reset(); - + if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0; _justStarted = false; @@ -363,14 +367,14 @@ void Application::initializeGL() { sprintf(title, "Interface: %4.2f seconds\n", startupTime); qDebug("%s", title); const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; - + // ask the Logstash class to record the startup time Logging::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } - + // update before the first render update(0.0f); - + InfoView::showFirstTime(); } @@ -378,7 +382,7 @@ void Application::paintGL() { PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::paintGL()"); - + glEnable(GL_LINE_SMOOTH); if (OculusManager::isConnected()) { @@ -387,17 +391,17 @@ void Application::paintGL() { _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTightness (0.0f); // Camera is directly connected to head without smoothing _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); - + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness(0.0f); float headHeight = _myAvatar.getHead().calculateAverageEyePosition().y - _myAvatar.getPosition().y; @@ -408,9 +412,9 @@ void Application::paintGL() { // Update camera position _myCamera.update( 1.f/_fps ); - - - // Note: whichCamera is used to pick between the normal camera myCamera for our + + + // Note: whichCamera is used to pick between the normal camera myCamera for our // main camera, vs, an alternate camera. The alternate camera we support right now // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism // to add other cameras. @@ -421,7 +425,7 @@ void Application::paintGL() { Camera whichCamera = _myCamera; if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { - + ViewFrustumOffset viewFrustumOffset = Menu::getInstance()->getViewFrustumOffset(); // set the camera to third-person view but offset so we can see the frustum @@ -433,7 +437,7 @@ void Application::paintGL() { _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; - } + } if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { updateShadowMap(); @@ -442,22 +446,22 @@ void Application::paintGL() { if (OculusManager::isConnected()) { OculusManager::display(whichCamera); } else if (TV3DManager::isConnected()) { - _glowEffect.prepare(); + _glowEffect.prepare(); TV3DManager::display(whichCamera); _glowEffect.render(); } else { - _glowEffect.prepare(); - + _glowEffect.prepare(); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); displaySide(whichCamera); glPopMatrix(); - + _glowEffect.render(); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - + bool eyeRelativeCamera = false; if (_rearMirrorTools->getZoomLevel() == BODY) { _mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar.getScale()); @@ -469,47 +473,47 @@ void Application::paintGL() { // face/body so that the average eye position lies at the origin eyeRelativeCamera = true; _mirrorCamera.setTargetPosition(glm::vec3()); - + } else { _mirrorCamera.setTargetPosition(_myAvatar.getHead().calculateAverageEyePosition()); } } - + _mirrorCamera.setTargetRotation(_myAvatar.getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f))); _mirrorCamera.update(1.0f/_fps); - + // set the bounds of rear mirror view - glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); - glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), + glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height()); bool updateViewFrustum = false; updateProjectionMatrix(_mirrorCamera, updateViewFrustum); glEnable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + // render rear mirror view glPushMatrix(); if (eyeRelativeCamera) { // save absolute translations glm::vec3 absoluteSkeletonTranslation = _myAvatar.getSkeletonModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar.getHead().getFaceModel().getTranslation(); - + // get the eye positions relative to the neck and use them to set the face translation glm::vec3 leftEyePosition, rightEyePosition; _myAvatar.getHead().getFaceModel().setTranslation(glm::vec3()); _myAvatar.getHead().getFaceModel().getEyePositions(leftEyePosition, rightEyePosition); _myAvatar.getHead().getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f); - + // get the neck position relative to the body and use it to set the skeleton translation glm::vec3 neckPosition; _myAvatar.getSkeletonModel().setTranslation(glm::vec3()); _myAvatar.getSkeletonModel().getNeckPosition(neckPosition); _myAvatar.getSkeletonModel().setTranslation(_myAvatar.getHead().getFaceModel().getTranslation() - neckPosition); - + displaySide(_mirrorCamera, true); - + // restore absolute translations _myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation); _myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation); @@ -517,9 +521,9 @@ void Application::paintGL() { displaySide(_mirrorCamera, true); } glPopMatrix(); - + _rearMirrorTools->render(false); - + // reset Viewport and projection matrix glViewport(0, 0, _glWidget->width(), _glWidget->height()); glDisable(GL_SCISSOR_TEST); @@ -527,10 +531,10 @@ void Application::paintGL() { } else if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(true); } - + displayOverlay(); } - + _frameCount++; } @@ -548,7 +552,7 @@ void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) void Application::resizeGL(int width, int height) { resetCamerasOnResizeGL(_viewFrustumOffsetCamera, width, height); resetCamerasOnResizeGL(_myCamera, width, height); - + glViewport(0, 0, width, height); // shouldn't this account for the menu??? updateProjectionMatrix(); @@ -565,12 +569,12 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; - + // Tell our viewFrustum about this change, using the application camera if (updateViewFrustum) { loadViewFrustum(camera, _viewFrustum); computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - + // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { @@ -583,7 +587,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) tempViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); } glFrustum(left, right, bottom, top, nearVal, farVal); - + glMatrixMode(GL_MODELVIEW); } @@ -594,7 +598,7 @@ void Application::resetProfile(const QString& username) { updateWindowTitle(); } -void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, +void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { Application* self = getInstance(); for (int i = 0; i < numNodeTypes; ++i) { @@ -603,7 +607,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } - + // Perform the broadcast for one type int nReceivingNodes = NodeList::getInstance()->broadcastToNodes(broadcastData, dataBytes, & nodeTypes[i], 1); @@ -620,7 +624,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ default: continue; } - self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); + self->_bandwidthMeter.outputStream(channel).updateValue(nReceivingNodes * dataBytes); } } @@ -631,7 +635,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ? DELETE_KEY_DOWN : INSERT_KEY_DOWN); _myAvatar.setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR)); - + } else { _myAvatar.setChatMessage(_chatEntry.getContents()); _chatEntry.clear(); @@ -666,7 +670,7 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Apostrophe: _audioScope.inputPaused = !_audioScope.inputPaused; - break; + break; case Qt::Key_L: if (!isShifted && !isMeta) { _displayLevels = !_displayLevels; @@ -676,7 +680,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::Log); } break; - + case Qt::Key_E: if (_nudgeStarted) { _nudgeGuidePosition.y += _mouseVoxel.s; @@ -684,14 +688,14 @@ void Application::keyPressEvent(QKeyEvent* event) { if (!_myAvatar.getDriveKeys(UP)) { _myAvatar.jump(); } - _myAvatar.setDriveKeys(UP, 1); + _myAvatar.setDriveKeys(UP, 1); } break; case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::Stars); break; - + case Qt::Key_C: if (_nudgeStarted) { _nudgeGuidePosition.y -= _mouseVoxel.s; @@ -699,7 +703,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(DOWN, 1); } break; - + case Qt::Key_W: if (_nudgeStarted) { if (_lookingAlongX) { @@ -719,7 +723,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(FWD, 1); } break; - + case Qt::Key_S: if (isShifted && !isMeta) { _voxels.collectStatsForTreesAndVBOs(); @@ -743,11 +747,11 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(BACK, 1); } break; - + case Qt::Key_Space: resetSensors(); break; - + case Qt::Key_G: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Gravity); @@ -755,7 +759,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::VoxelGetColorMode); } break; - + case Qt::Key_A: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); @@ -777,7 +781,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(ROT_LEFT, 1); } break; - + case Qt::Key_D: if (_nudgeStarted) { if (_lookingAlongX) { @@ -797,7 +801,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(ROT_RIGHT, 1); } break; - + case Qt::Key_Return: case Qt::Key_Enter: if (_nudgeStarted) { @@ -809,7 +813,7 @@ void Application::keyPressEvent(QKeyEvent* event) { setMenuShortcutsEnabled(false); } break; - + case Qt::Key_Up: if (_nudgeStarted && !isShifted) { if (_lookingAlongX) { @@ -831,7 +835,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? UP : FWD, 1); } break; - + case Qt::Key_Down: if (_nudgeStarted && !isShifted) { if (_lookingAlongX) { @@ -853,7 +857,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? DOWN : BACK, 1); } break; - + case Qt::Key_Left: if (_nudgeStarted) { if (_lookingAlongX) { @@ -873,7 +877,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); } break; - + case Qt::Key_Right: if (_nudgeStarted) { if (_lookingAlongX) { @@ -893,7 +897,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); } break; - + case Qt::Key_I: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -903,7 +907,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_K: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -913,7 +917,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_J: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f); @@ -925,20 +929,20 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_M: if (isShifted) { _viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f); if (TV3DManager::isConnected()) { TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height()); } - + } else { _myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0)); } updateProjectionMatrix(); break; - + case Qt::Key_U: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -948,7 +952,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } updateProjectionMatrix(); break; - + case Qt::Key_Y: if (isShifted) { _myCamera.setEyeOffsetOrientation(glm::normalize( @@ -1040,7 +1044,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _myAvatar.setKeyState(NO_KEY_DOWN); return; } - + switch (event->key()) { case Qt::Key_Shift: _pasteMode = false; @@ -1048,47 +1052,47 @@ void Application::keyReleaseEvent(QKeyEvent* event) { case Qt::Key_E: _myAvatar.setDriveKeys(UP, 0); break; - + case Qt::Key_C: _myAvatar.setDriveKeys(DOWN, 0); break; - + case Qt::Key_W: _myAvatar.setDriveKeys(FWD, 0); break; - + case Qt::Key_S: _myAvatar.setDriveKeys(BACK, 0); break; - + case Qt::Key_A: _myAvatar.setDriveKeys(ROT_LEFT, 0); break; - + case Qt::Key_D: _myAvatar.setDriveKeys(ROT_RIGHT, 0); break; - + case Qt::Key_Up: _myAvatar.setDriveKeys(FWD, 0); _myAvatar.setDriveKeys(UP, 0); break; - + case Qt::Key_Down: _myAvatar.setDriveKeys(BACK, 0); _myAvatar.setDriveKeys(DOWN, 0); break; - + case Qt::Key_Left: _myAvatar.setDriveKeys(LEFT, 0); _myAvatar.setDriveKeys(ROT_LEFT, 0); break; - + case Qt::Key_Right: _myAvatar.setDriveKeys(RIGHT, 0); _myAvatar.setDriveKeys(ROT_RIGHT, 0); break; - + default: event->ignore(); break; @@ -1107,7 +1111,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (activeWindow() == _window) { int deltaX = event->x() - _mouseX; int deltaY = event->y() - _mouseY; - + _mouseX = event->x(); _mouseY = event->y(); @@ -1128,7 +1132,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { if (event->buttons().testFlag(Qt::LeftButton)) { maybeEditVoxelUnderCursor(); - + } else if (event->buttons().testFlag(Qt::RightButton) && Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } @@ -1154,17 +1158,17 @@ void Application::mousePressEvent(QMouseEvent* event) { _mousePressed = true; maybeEditVoxelUnderCursor(); - + if (_audio.mousePressEvent(_mouseX, _mouseY)) { // stop propagation return; } - + if (_rearMirrorTools->mousePressEvent(_mouseX, _mouseY)) { // stop propagation return; } - + if (!_palette.isActive() && (!_isHoverVoxel || _lookatTargetAvatar)) { // disable for now // _pieMenu.mousePressEvent(_mouseX, _mouseY); @@ -1183,18 +1187,18 @@ void Application::mousePressEvent(QMouseEvent* event) { const float GREEN_CLICK_FREQUENCY = 1250.f; const float BLUE_CLICK_FREQUENCY = 1330.f; const float MIDDLE_A_FREQUENCY = 440.f; - float frequency = MIDDLE_A_FREQUENCY + + float frequency = MIDDLE_A_FREQUENCY + (_hoverVoxel.red / 255.f * RED_CLICK_FREQUENCY + _hoverVoxel.green / 255.f * GREEN_CLICK_FREQUENCY + _hoverVoxel.blue / 255.f * BLUE_CLICK_FREQUENCY) / 3.f; - + _audio.startCollisionSound(1.0, frequency, 0.0, HOVER_VOXEL_DECAY, false); _isHoverVoxelSounding = true; - + const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f; glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel); glm::vec3 myPosition = _myAvatar.getPosition(); - + // If there is not an action tool set (add, delete, color), move to this voxel if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) && !(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) || @@ -1203,7 +1207,7 @@ void Application::mousePressEvent(QMouseEvent* event) { _myAvatar.setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD); } } - + } else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } @@ -1259,9 +1263,9 @@ void Application::touchEndEvent(QTouchEvent* event) { _isTouchPressed = false; } -const bool USE_MOUSEWHEEL = false; +const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { - // Wheel Events disabled for now because they are also activated by touch look pitch up/down. + // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { if (!Menu::getInstance()->isVoxelModeActionChecked()) { event->ignore(); @@ -1277,7 +1281,7 @@ void Application::wheelEvent(QWheelEvent* event) { void Application::sendPingPackets() { - const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, + const char nodesToPing[] = {NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER}; unsigned char pingPacket[MAX_PACKET_SIZE]; @@ -1290,24 +1294,24 @@ void Application::sendPingPackets() { void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data) { unsigned char packet[MAX_PACKET_SIZE]; unsigned char* packetPosition = packet; - + packetPosition += populateTypeAndVersion(packetPosition, PACKET_TYPE_AVATAR_FACE_VIDEO); - + QByteArray rfcUUID = NodeList::getInstance()->getOwnerUUID().toRfc4122(); memcpy(packetPosition, rfcUUID.constData(), rfcUUID.size()); packetPosition += rfcUUID.size(); - + *(uint32_t*)packetPosition = frameCount; packetPosition += sizeof(uint32_t); - + *(uint32_t*)packetPosition = data.size(); packetPosition += sizeof(uint32_t); - + uint32_t* offsetPosition = (uint32_t*)packetPosition; packetPosition += sizeof(uint32_t); - + int headerSize = packetPosition - packet; - + // break the data up into submessages of the maximum size (at least one, for zero-length packets) *offsetPosition = 0; do { @@ -1315,7 +1319,7 @@ void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& d memcpy(packetPosition, data.constData() + *offsetPosition, payloadSize); getInstance()->controlledBroadcastToNodes(packet, headerSize + payloadSize, &NODE_TYPE_AVATAR_MIXER, 1); *offsetPosition += payloadSize; - + } while (*offsetPosition < data.size()); } @@ -1326,24 +1330,24 @@ void Application::timer() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { sendPingPackets(); } - + _fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _packetsPerSecond = (float)_packetCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _bytesPerSecond = (float)_bytesCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f); _frameCount = 0; _packetCount = 0; _bytesCount = 0; - + gettimeofday(&_timerStart, NULL); - + // if we haven't detected gyros, check for them now if (!_serialHeadSensor.isActive()) { _serialHeadSensor.pair(); } - + // ask the node list to check in with the domain server NodeList::getInstance()->sendDomainServerCheckIn(); - + // give the MyAvatar object position to the Profile so it can propagate to the data-server _profile.updatePosition(_myAvatar.getPosition()); } @@ -1352,34 +1356,34 @@ static glm::vec3 getFaceVector(BoxFace face) { switch (face) { case MIN_X_FACE: return glm::vec3(-1, 0, 0); - + case MAX_X_FACE: return glm::vec3(1, 0, 0); - + case MIN_Y_FACE: return glm::vec3(0, -1, 0); - + case MAX_Y_FACE: return glm::vec3(0, 1, 0); - + case MIN_Z_FACE: return glm::vec3(0, 0, -1); - + case MAX_Z_FACE: return glm::vec3(0, 0, 1); } } void Application::idle() { - // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing - // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing + // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing + // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing // details normally. bool showWarnings = getLogger()->extraDebugging(); PerformanceWarning warn(showWarnings, "Application::idle()"); - + timeval check; gettimeofday(&check, NULL); - + // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check); @@ -1397,7 +1401,7 @@ void Application::idle() { PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); _lastTimeUpdated = check; _idleLoopStdev.addValue(timeSinceLastUpdate); - + // Record standard deviation and reset counter if needed const int STDEV_SAMPLES = 500; if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { @@ -1413,7 +1417,7 @@ void Application::idle() { void Application::terminate() { // Close serial port // close(serial_fd); - + LeapManager::terminate(); Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); @@ -1424,7 +1428,7 @@ void Application::terminate() { if (_enableNetworkThread) { _stopNetworkReceiveThread = true; - pthread_join(_networkReceiveThread, NULL); + pthread_join(_networkReceiveThread, NULL); } printf(""); @@ -1441,18 +1445,18 @@ static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& da if (avatarMixerNode) { avatarMixerNode->recordBytesReceived(dataBytes); } - + // skip the header int numBytesPacketHeader = numBytesForPacketHeader(packetData); packetData += numBytesPacketHeader; dataBytes -= numBytesPacketHeader; - + // read the node id QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData, NUM_BYTES_RFC4122_UUID)); - + packetData += NUM_BYTES_RFC4122_UUID; dataBytes -= NUM_BYTES_RFC4122_UUID; - + // make sure the node exists Node* node = NodeList::getInstance()->nodeWithUUID(nodeUUID); if (!node || !node->getLinkedData()) { @@ -1473,11 +1477,11 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat // don't ask over and over again. Instead use this message to // Tell the other avatars that your dataserver data has // changed. - + //QDataStream in(QByteArray((char*)packetData, dataBytes)); //QUrl voxelURL; //in >> voxelURL; - + // use this timing to as the data-server for an updated mesh for this avatar (if we have UUID) DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, avatar->getUUID()); @@ -1529,7 +1533,7 @@ void Application::removeVoxel(glm::vec3 position, voxel.z = position.z / TREE_SCALE; voxel.s = scale / TREE_SCALE; _voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel); - + // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); } @@ -1547,21 +1551,25 @@ void Application::shootParticle() { glm::vec3 gravity = DEFAULT_GRAVITY * 0.f; float damping = DEFAULT_DAMPING * 0.01f; QString script( - " function collisionWithVoxel(voxel) { " - " print('collisionWithVoxel(voxel)... '); " - " print('myID=' + Particle.getID() + '\\n'); " - " var voxelColor = voxel.getColor();" - " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " - " var myColor = Particle.getColor();" - " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " - " Particle.setColor(voxelColor); " - " } " + " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + " var voxelColor = voxel.getColor();" + " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + " var voxelAt = voxel.getPosition();" + " var voxelScale = voxel.getScale();" + " Voxels.queueVoxelDelete(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " + " print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " " Particle.collisionWithVoxel.connect(collisionWithVoxel); " ); - - ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color, + + ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color, velocity / (float)TREE_SCALE, gravity, damping, NOT_IN_HAND, script); - + // If we wanted to be able to edit this particle after shooting, then we could store this value // and use it for editing later. But we don't care about that for "shooting" and therefore we just // clean up our memory now. deleting a ParticleEditHandle does not effect the underlying particle, @@ -1576,14 +1584,14 @@ ParticleEditHandle* Application::newParticleEditHandle(uint32_t id) { } // Caller is responsible for managing this EditableParticle -ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, +ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript) { ParticleEditHandle* particleEditHandle = newParticleEditHandle(); particleEditHandle->createParticle(position, radius, color, velocity, gravity, damping, inHand, updateScript); return particleEditHandle; } - + void Application::makeVoxel(glm::vec3 position, float scale, @@ -1601,9 +1609,9 @@ void Application::makeVoxel(glm::vec3 position, voxel.blue = blue; PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET; _voxelEditSender.sendVoxelEditMessage(message, voxel); - + // create the voxel locally so it appears immediately - + _voxels.createVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.red, voxel.green, voxel.blue, isDestructive); @@ -1670,9 +1678,9 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX]; codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX]; - getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, + getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, codeColorBuffer, codeAndColorLength); - + delete[] codeColorBuffer; } return true; // keep going @@ -1682,7 +1690,7 @@ void Application::exportVoxels() { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/voxels.svo"); - QString fileNameString = QFileDialog::getSaveFileName(_glWidget, tr("Export Voxels"), suggestedName, + QString fileNameString = QFileDialog::getSaveFileName(_glWidget, tr("Export Voxels"), suggestedName, tr("Sparse Voxel Octree Files (*.svo)")); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); @@ -1729,7 +1737,7 @@ void Application::copyVoxels() { } void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestination) { - // Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to + // Recurse the clipboard tree, where everything is root relative, and send all the colored voxels to // the server as an set voxel message, this will also rebase the voxels to the new location SendVoxelsOperationArgs args; args.newBaseOctCode = octalCodeDestination; @@ -1748,7 +1756,7 @@ void Application::pasteVoxels() { VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); // we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the - // voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a + // voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a // target octalCode for where the user is pointing. const unsigned char* octalCodeDestination; if (selectedNode) { @@ -1758,7 +1766,7 @@ void Application::pasteVoxels() { } pasteVoxelsToOctalCode(octalCodeDestination); - + if (calculatedOctCode) { delete[] calculatedOctCode; } @@ -1788,7 +1796,7 @@ void Application::nudgeVoxels() { if (!Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && selectedNode) { Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode); } - + if (!_nudgeStarted && selectedNode) { _nudgeVoxel = _mouseVoxel; _nudgeStarted = true; @@ -1839,14 +1847,14 @@ void Application::init() { delete tmpTree; _voxelImporter.init(); - + _environment.init(); _glowEffect.init(); _ambientOcclusionEffect.init(); _voxelShader.init(); _pointShader.init(); - + _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; QCursor::setPos(_headMouseX, _headMouseY); @@ -1856,11 +1864,11 @@ void Application::init() { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); - + _mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); _mirrorCamera.setFieldOfView(30); - + OculusManager::connect(); if (OculusManager::isConnected()) { QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), @@ -1874,9 +1882,9 @@ void Application::init() { "trigger", Qt::QueuedConnection); } - + LeapManager::initialize(); - + gettimeofday(&_timerStart, NULL); gettimeofday(&_lastTimeUpdated, NULL); @@ -1885,14 +1893,14 @@ void Application::init() { _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); } qDebug("Loaded settings.\n"); - + if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL); DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL); } - // Set up VoxelSystem after loading preferences so we can get the desired max voxel count + // Set up VoxelSystem after loading preferences so we can get the desired max voxel count _voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels()); _voxels.setUseVoxelShader(Menu::getInstance()->isOptionChecked(MenuOption::UseVoxelShader)); _voxels.setVoxelsAsPoints(Menu::getInstance()->isOptionChecked(MenuOption::VoxelsAsPoints)); @@ -1901,11 +1909,11 @@ void Application::init() { _particles.init(); _particles.setViewFrustum(getViewFrustum()); - + _metavoxels.init(); - + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); - + _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); @@ -1923,7 +1931,7 @@ void Application::init() { _pieMenu.addAction(_followMode); _audio.init(_glWidget); - + _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); @@ -1941,7 +1949,7 @@ void Application::restoreMirrorView() { if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { Menu::getInstance()->triggerOption(MenuOption::Mirror);; } - + if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } @@ -1951,7 +1959,7 @@ void Application::shrinkMirrorView() { if (!Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { Menu::getInstance()->triggerOption(MenuOption::Mirror);; } - + if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } @@ -1967,15 +1975,15 @@ void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, cons glm::vec3& eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); - + if (!_mousePressed) { _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); } } - + Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { - + NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { @@ -1985,7 +1993,7 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); - + _lookatIndicatorScale = avatar->getHead().getScale(); _lookatOtherPosition = avatar->getHead().getPosition(); nodeUUID = avatar->getOwningNode()->getUUID(); @@ -1999,7 +2007,7 @@ Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, con bool Application::isLookingAtMyAvatar(Avatar* avatar) { glm::vec3 theirLookat = avatar->getHead().getLookAtPosition(); glm::vec3 myHeadPosition = _myAvatar.getHead().getPosition(); - + if (pointInSphere(theirLookat, myHeadPosition, HEAD_SPHERE_RADIUS * _myAvatar.getScale())) { return true; } @@ -2100,7 +2108,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); NodeList* nodeList = NodeList::getInstance(); - + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->lock(); if (node->getLinkedData()) { @@ -2113,7 +2121,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: } node->unlock(); } - + // simulate avatar fades for (vector::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) { Avatar* avatar = *fade; @@ -2123,7 +2131,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: if (avatar->getNewScale() < MINIMUM_SCALE) { delete avatar; _avatarFades.erase(fade--); - + } else { avatar->simulate(deltaTime, NULL); } @@ -2135,7 +2143,7 @@ void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); - _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), + _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), mouseRayOrigin, mouseRayDirection); // adjust for mirroring @@ -2150,7 +2158,7 @@ void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm // tell my avatar if the mouse is being pressed... _myAvatar.setMousePressed(_mousePressed); - // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position + // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position _myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection); } @@ -2158,30 +2166,30 @@ void Application::updateFaceshift() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateFaceshift()"); - + // Update faceshift _faceshift.update(); - + // Copy angular velocity if measured by faceshift, to the head if (_faceshift.isActive()) { _myAvatar.getHead().setAngularVelocity(_faceshift.getHeadAngularVelocity()); } } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, +void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); - + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { lookAtSpot = _myCamera.getPosition(); - + } else if (!_lookatTargetAvatar) { if (_isHoverVoxel) { // Look at the hovered voxel lookAtSpot = getMouseVoxelWorldCoordinates(_hoverVoxel); - + } else { // Just look in direction of the mouse ray const float FAR_AWAY_STARE = TREE_SCALE; @@ -2200,7 +2208,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _myAvatar.getHead().setLookAtPosition(lookAtSpot); } -void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, +void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -2222,21 +2230,21 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } else { // Voxel is not found, clear all _isHoverVoxelSounding = false; - _isHoverVoxel = false; + _isHoverVoxel = false; } } else { // Check for a new hover voxel glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s); - // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, + // only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled, // and make sure the tree is not already busy... because otherwise you'll have to wait. if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); } - if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && + if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) { - + _hoverVoxelOriginalColor[0] = _hoverVoxel.red; _hoverVoxelOriginalColor[1] = _hoverVoxel.green; _hoverVoxelOriginalColor[2] = _hoverVoxel.blue; @@ -2269,7 +2277,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _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 @@ -2277,7 +2285,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.y = _mouseVoxelScale * floorf(_mouseVoxel.y / _mouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(_mouseVoxel.z / _mouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; - + } else { glm::vec3 faceVector = getFaceVector(face); if (_mouseVoxelScale < _mouseVoxel.s) { @@ -2309,7 +2317,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { // red indicates deletion _mouseVoxel.red = 255; @@ -2328,7 +2336,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, _mouseVoxel.green = paintColor.green(); _mouseVoxel.blue = paintColor.blue(); } - + // if we just edited, use the currently selected voxel as the "last" for drag detection if (_justEditedVoxel) { _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); @@ -2364,7 +2372,7 @@ void Application::updateLeap(float deltaTime) { void Application::updateSixense(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateSixense()"); - + _sixenseManager.update(deltaTime); } @@ -2440,7 +2448,7 @@ void Application::updateTransmitter(float deltaTime) { _transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position; glm::vec3 direction = _myAvatar.getOrientation() * glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT; - + // check against voxels, avatars const float MAX_PICK_DISTANCE = 100.0f; float minDistance = MAX_PICK_DISTANCE; @@ -2451,7 +2459,7 @@ void Application::updateTransmitter(float deltaTime) { minDistance = min(minDistance, distance); } _transmitterPickEnd = _transmitterPickStart + direction * minDistance; - + } else { _transmitterPickStart = _transmitterPickEnd = glm::vec3(); } @@ -2461,7 +2469,7 @@ void Application::updateCamera(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); - if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { + if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); @@ -2478,15 +2486,15 @@ void Application::updateCamera(float deltaTime) { _myCamera.setModeShiftRate(1.0f); } } - + if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f; if (_faceshift.isActive()) { const float EYE_OFFSET_SCALE = 0.025f; glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE; - _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); + _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); updateProjectionMatrix(); - + } else if (_webcam.isActive()) { const float EYE_OFFSET_SCALE = 0.5f; glm::vec3 position = _webcam.getEstimatedPosition() * EYE_OFFSET_SCALE; @@ -2506,7 +2514,7 @@ void Application::updateDialogs(float deltaTime) { if (bandwidthDialog) { bandwidthDialog->update(); } - + VoxelStatsDialog* voxelStatsDialog = Menu::getInstance()->getVoxelStatsDialog(); if (voxelStatsDialog) { voxelStatsDialog->update(); @@ -2549,22 +2557,22 @@ void Application::updateCursor(float deltaTime) { void Application::update(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); - + // check what's under the mouse and update the mouse voxel glm::vec3 mouseRayOrigin, mouseRayDirection; updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection); - + // Set where I am looking based on my mouse ray (so that other people can see) glm::vec3 lookAtSpot; - + updateFaceshift(); updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); - + // Find the voxel we are hovering over, and respond if clicked float distance; BoxFace face; - + updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors @@ -2582,7 +2590,7 @@ void Application::update(float deltaTime) { updateDialogs(deltaTime); // update various stats dialogs if present updateAudio(deltaTime); // Update audio stats for procedural sounds updateCursor(deltaTime); // Handle cursor updates - + _particles.update(); // update the particles... _particleCollisionSystem.update(); // handle collisions for the particles... } @@ -2590,39 +2598,39 @@ void Application::update(float deltaTime) { void Application::updateAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatar()"); - + // rotate body yaw for yaw received from multitouch _myAvatar.setOrientation(_myAvatar.getOrientation() * glm::quat(glm::vec3(0, _yawFromTouch, 0))); _yawFromTouch = 0.f; - + // apply pitch from touch _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + _myAvatar.getHand().getPitchUpdate() + _pitchFromTouch); _myAvatar.getHand().setPitchUpdate(0.f); _pitchFromTouch = 0.0f; - + // Update my avatar's state from gyros and/or webcam _myAvatar.updateFromGyrosAndOrWebcam(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); - + // Update head mouse from faceshift if active if (_faceshift.isActive()) { glm::vec3 headVelocity = _faceshift.getHeadAngularVelocity(); - + // sets how quickly head angular rotation moves the head mouse const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f; const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f; _headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE; _headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE; } - + if (_serialHeadSensor.isActive()) { // Grab latest readings from the gyros float measuredPitchRate = _serialHeadSensor.getLastPitchRate(); float measuredYawRate = _serialHeadSensor.getLastYawRate(); - + // Update gyro-based mouse (X,Y on screen) const float MIN_MOUSE_RATE = 3.0; const float HORIZONTAL_PIXELS_PER_DEGREE = 2880.f / 45.f; @@ -2634,7 +2642,7 @@ void Application::updateAvatar(float deltaTime) { } const float MIDPOINT_OF_SCREEN = 0.5; - + // Only use gyro to set lookAt if mouse hasn't selected an avatar if (!_lookatTargetAvatar) { @@ -2651,7 +2659,7 @@ void Application::updateAvatar(float deltaTime) { } } - + // Constrain head-driven mouse to edges of screen _headMouseX = glm::clamp(_headMouseX, 0, _glWidget->width()); _headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); @@ -2659,33 +2667,33 @@ void Application::updateAvatar(float deltaTime) { if (OculusManager::isConnected()) { float yaw, pitch, roll; OculusManager::getEulerAngles(yaw, pitch, roll); - + _myAvatar.getHead().setYaw(yaw); _myAvatar.getHead().setPitch(pitch); _myAvatar.getHead().setRoll(roll); } - + // Get audio loudness data from audio input device _myAvatar.getHead().setAudioLoudness(_audio.getLastInputLoudness()); - + NodeList* nodeList = NodeList::getInstance(); - + // send head/hand data to the avatar mixer and voxel server unsigned char broadcastString[MAX_PACKET_SIZE]; unsigned char* endOfBroadcastStringWrite = broadcastString; - + endOfBroadcastStringWrite += populateTypeAndVersion(endOfBroadcastStringWrite, PACKET_TYPE_HEAD_DATA); - + QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); memcpy(endOfBroadcastStringWrite, ownerUUID.constData(), ownerUUID.size()); endOfBroadcastStringWrite += ownerUUID.size(); - + endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); - + const char nodeTypesOfInterest[] = { NODE_TYPE_AVATAR_MIXER }; controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - + const float AVATAR_URLS_SEND_INTERVAL = 1.0f; if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) { QUrl empty; @@ -2695,10 +2703,10 @@ void Application::updateAvatar(float deltaTime) { // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera // We could optimize this to not actually load the viewFrustum, since we don't - // actually need to calculate the view frustum planes to send these details + // actually need to calculate the view frustum planes to send these details // to the server. loadViewFrustum(_myCamera, _viewFrustum); - + // Update my voxel servers with my current voxel query... queryOctree(NODE_TYPE_VOXEL_SERVER, PACKET_TYPE_VOXEL_QUERY, _voxelServerJurisdictions); queryOctree(NODE_TYPE_PARTICLE_SERVER, PACKET_TYPE_PARTICLE_QUERY, _particleServerJurisdictions); @@ -2710,16 +2718,16 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node if (!Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { return; } - + bool wantExtraDebugging = getLogger()->extraDebugging(); - + // These will be the same for all servers, so we can set them up once and then reuse for each server we send to. _voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes)); _voxelQuery.setWantColor(!Menu::getInstance()->isOptionChecked(MenuOption::DisableColorVoxels)); _voxelQuery.setWantDelta(!Menu::getInstance()->isOptionChecked(MenuOption::DisableDeltaSending)); _voxelQuery.setWantOcclusionCulling(Menu::getInstance()->isOptionChecked(MenuOption::EnableOcclusionCulling)); _voxelQuery.setWantCompression(Menu::getInstance()->isOptionChecked(MenuOption::EnableVoxelPacketCompression)); - + _voxelQuery.setCameraPosition(_viewFrustum.getPosition()); _voxelQuery.setCameraOrientation(_viewFrustum.getOrientation()); _voxelQuery.setCameraFov(_viewFrustum.getFieldOfView()); @@ -2738,7 +2746,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node int totalServers = 0; int inViewServers = 0; int unknownJurisdictionServers = 0; - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are serverType @@ -2747,7 +2755,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // get the server bounds for this server QUuid nodeUUID = node->getUUID(); - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2756,7 +2764,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node const JurisdictionMap& map = (jurisdictions)[nodeUUID]; unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2774,7 +2782,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node } if (wantExtraDebugging && unknownJurisdictionServers > 0) { - qDebug("Servers: total %d, in view %d, unknown jurisdiction %d \n", + qDebug("Servers: total %d, in view %d, unknown jurisdiction %d \n", totalServers, inViewServers, unknownJurisdictionServers); } @@ -2793,11 +2801,11 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node perUnknownServer = (totalPPS / unknownJurisdictionServers); } } - + if (wantExtraDebugging && unknownJurisdictionServers > 0) { qDebug("perServerPPS: %d perUnknownServer: %d\n", perServerPPS, perUnknownServer); } - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { // only send to the NodeTypes that are serverType if (node->getActiveSocket() != NULL && node->getType() == serverType) { @@ -2808,7 +2816,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node bool inView = false; bool unknownView = false; - + // if we haven't heard from this voxel server, go ahead and send it a query, so we // can get the jurisdiction... if (jurisdictions.find(nodeUUID) == jurisdictions.end()) { @@ -2820,7 +2828,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node const JurisdictionMap& map = (jurisdictions)[nodeUUID]; unsigned char* rootCode = map.getRootOctalCode(); - + if (rootCode) { VoxelPositionSize rootDetails; voxelDetailsForCode(rootCode, rootDetails); @@ -2839,17 +2847,17 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node } } } - + if (inView) { _voxelQuery.setMaxOctreePacketsPerSecond(perServerPPS); } else if (unknownView) { if (wantExtraDebugging) { - qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " + qDebug() << "no known jurisdiction for node " << *node << ", give it budget of " << perUnknownServer << " to send us jurisdiction.\n"; } - + // set the query's position/orientation to be degenerate in a manner that will get the scene quickly - // If there's only one server, then don't do this, and just let the normal voxel query pass through + // If there's only one server, then don't do this, and just let the normal voxel query pass through // as expected... this way, we will actually get a valid scene if there is one to be seen if (totalServers > 1) { _voxelQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1)); @@ -2880,7 +2888,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // encode the query data... endOfVoxelQueryPacket += _voxelQuery.getBroadcastData(endOfVoxelQueryPacket); - + int packetLength = endOfVoxelQueryPacket - voxelQueryPacket; // make sure we still have an active socket @@ -2899,10 +2907,10 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node // loadViewFrustum() // // Description: this will load the view frustum bounds for EITHER the head -// or the "myCamera". +// or the "myCamera". // void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { - // We will use these below, from either the camera or head vectors calculated above + // We will use these below, from either the camera or head vectors calculated above glm::vec3 position(camera.getPosition()); float fov = camera.getFieldOfView(); float nearClip = camera.getNearClip(); @@ -2911,10 +2919,10 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { glm::quat rotation = camera.getRotation(); - // Set the viewFrustum up with the correct position and orientation of the camera + // Set the viewFrustum up with the correct position and orientation of the camera viewFrustum.setPosition(position); viewFrustum.setOrientation(rotation); - + // Also make sure it's got the correct lens details from the camera viewFrustum.setAspectRatio(aspectRatio); viewFrustum.setFieldOfView(fov); @@ -2936,9 +2944,9 @@ void Application::updateShadowMap() { fbo->bind(); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + glViewport(0, 0, fbo->width(), fbo->height()); - + glm::vec3 lightDirection = -getSunDirection(); glm::quat rotation = glm::inverse(rotationBetween(IDENTITY_FRONT, lightDirection)); glm::vec3 translation = glm::vec3(); @@ -2960,57 +2968,57 @@ void Application::updateShadowMap() { minima = glm::min(minima, points[i]); maxima = glm::max(maxima, points[i]); } - + // stretch out our extents in z so that we get all of the avatars minima.z -= _viewFrustum.getFarClip() * 0.5f; maxima.z += _viewFrustum.getFarClip() * 0.5f; - + // save the combined matrix for rendering _shadowMatrix = glm::transpose(glm::translate(0.5f, 0.5f, 0.5f) * glm::scale(0.5f, 0.5f, 0.5f) * glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(rotation) * glm::translate(translation)); - + glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); - + glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); - + // store view matrix without translation, which we'll use for precision-sensitive objects glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); _viewMatrixTranslation = translation; - + glTranslatef(translation.x, translation.y, translation.z); - + renderAvatars(true); - _particles.render(); - + _particles.render(); + glPopMatrix(); - + glMatrixMode(GL_PROJECTION); glPopMatrix(); - + glMatrixMode(GL_MODELVIEW); - + fbo->release(); - + glViewport(0, 0, _glWidget->width(), _glWidget->height()); } - + const GLfloat WHITE_SPECULAR_COLOR[] = { 1.0f, 1.0f, 1.0f, 1.0f }; const GLfloat NO_SPECULAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f }; void Application::setupWorldLight() { - + // Setup 3D lights (after the camera transform, so that they are positioned in world space) glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - + glm::vec3 sunDirection = getSunDirection(); GLfloat light_position0[] = { sunDirection.x, sunDirection.y, sunDirection.z, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); @@ -3018,8 +3026,8 @@ void Application::setupWorldLight() { glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 }; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); - - glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR); + + glLightfv(GL_LIGHT0, GL_SPECULAR, WHITE_SPECULAR_COLOR); glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); glMateriali(GL_FRONT, GL_SHININESS, 96); } @@ -3032,7 +3040,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { glScalef(-1.0f, 1.0f, 1.0f); glFrontFace(GL_CW); - + } else { glFrontFace(GL_CCW); } @@ -3059,9 +3067,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // Setup 3D lights (after the camera transform, so that they are positioned in world space) setupWorldLight(); - + if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... stars..."); if (!_stars.isStarsLoaded()) { _stars.generate(STARFIELD_NUM_STARS, STARFIELD_SEED); @@ -3075,7 +3083,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { float height = glm::distance(whichCamera.getPosition(), closestData.getAtmosphereCenter()); if (height < closestData.getAtmosphereInnerRadius()) { alpha = 0.0f; - + } else if (height < closestData.getAtmosphereOuterRadius()) { alpha = (height - closestData.getAtmosphereInnerRadius()) / (closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius()); @@ -3088,20 +3096,20 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // draw the sky dome if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); _environment.renderAtmospheres(whichCamera); } - + glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); - + // Enable to show line from me to the voxel I am touching //renderLineToTouchedVoxel(); //renderThrustAtVoxel(_voxelThrust); - + if (!selfAvatarOnly) { - // draw a red sphere + // draw a red sphere float sphereRadius = 0.25f; glColor3f(1,0,0); glPushMatrix(); @@ -3117,33 +3125,33 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } // Draw voxels if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) { _voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures)); } } - + // also, metavoxels if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... metavoxels..."); _metavoxels.render(); - } + } // render particles... _particles.render(); - + // render the ambient occlusion effect if enabled if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... AmbientOcclusion..."); _ambientOcclusionEffect.render(); } - + // restore default, white specular glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR); - + // Render the highlighted voxel if (_isHighlightVoxel) { renderHighlightVoxel(_highlightVoxel); @@ -3151,7 +3159,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // indicate what we'll be adding/removing in mouse mode, if anything if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels TOOLS UX..."); glDisable(GL_LIGHTING); @@ -3172,14 +3180,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } else { renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { // use a contrasting color so that we can see what we're doing glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128); } else { glColor3ub(_mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue); } - + if (_nudgeStarted) { // render nudge guide cube glTranslatef(_nudgeGuidePosition.x + _nudgeVoxel.s*0.5f, @@ -3198,9 +3206,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glPopMatrix(); glEnable(GL_LIGHTING); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... PASTE Preview..."); glPushMatrix(); @@ -3223,17 +3231,17 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { renderWorldBox(); } - + // brad's frustum for debugging if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... renderViewFrustum..."); renderViewFrustum(_viewFrustum); } // render voxel fades if they exist if (_voxelFades.size() > 0) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxel fades..."); for(std::vector::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { fade->render(); @@ -3245,15 +3253,15 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } } - { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... renderFollowIndicator..."); renderFollowIndicator(); } - + // render transmitter pick ray, if non-empty if (_transmitterPickStart != _transmitterPickEnd) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... transmitter pick ray..."); Glower glower; @@ -3265,13 +3273,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); glEnd(); glLineWidth(1.0f); - + glPushMatrix(); glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z); - + const float PICK_END_RADIUS = 0.025f; glutSolidSphere(PICK_END_RADIUS, 8, 8); - + glPopMatrix(); } } @@ -3285,21 +3293,21 @@ void Application::loadTranslatedViewMatrix(const glm::vec3& translation) { void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& near, float& far, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const { - + _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, near, far, nearClipPlane, farClipPlane); } void Application::displayOverlay() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displayOverlay()"); - // Render 2D overlay: I/O level bar graphs and text + // Render 2D overlay: I/O level bar graphs and text glMatrixMode(GL_PROJECTION); glPushMatrix(); - glLoadIdentity(); + glLoadIdentity(); gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); - + // Display a single screen-size quad to create an alpha blended 'collision' flash if (_audio.getCollisionFlashesScreen()) { float collisionSoundMagnitude = _audio.getCollisionSoundMagnitude(); @@ -3308,7 +3316,7 @@ void Application::displayOverlay() { renderCollisionOverlay(_glWidget->width(), _glWidget->height(), _audio.getCollisionSoundMagnitude()); } } - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { _audio.render(_glWidget->width(), _glWidget->height()); if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { @@ -3317,7 +3325,7 @@ void Application::displayOverlay() { } //noiseTest(_glWidget->width(), _glWidget->height()); - + if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse) && USING_INVENSENSE_MPU9150) { // Display small target box at center or head mouse target that can also be used to measure LOD @@ -3329,7 +3337,7 @@ void Application::displayOverlay() { glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY); glVertex2f(_headMouseX, _headMouseY - PIXEL_BOX/2); glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); - glEnd(); + glEnd(); glEnable(GL_LINE_SMOOTH); glColor3f(1.f, 0.f, 0.f); glPointSize(3.0f); @@ -3342,7 +3350,7 @@ void Application::displayOverlay() { const float EYE_TARGET_PIXELS_PER_DEGREE = 40.0; int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; - + glColor3f(0.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); glBegin(GL_LINES); @@ -3354,10 +3362,10 @@ void Application::displayOverlay() { } } - + // Show detected levels from the serial I/O ADC channel sensors if (_displayLevels) _serialHeadSensor.renderLevels(_glWidget->width(), _glWidget->height()); - + // Show hand transmitter data if detected if (_myTransmitter.isConnected()) { _myTransmitter.renderLevels(_glWidget->width(), _glWidget->height()); @@ -3365,21 +3373,21 @@ void Application::displayOverlay() { // Display stats and log text onscreen glLineWidth(1.0f); glPointSize(1.0f); - + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - // Onscreen text about position, servers, etc + // Onscreen text about position, servers, etc displayStats(); - // Bandwidth meter + // Bandwidth meter if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); } // Stats at upper right of screen about who domain server is telling us about glPointSize(1.0f); char nodes[100]; - + NodeList* nodeList = NodeList::getInstance(); int totalAvatars = 0, totalServers = 0; - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->getType() == NODE_TYPE_AGENT ? totalAvatars++ : totalServers++; } @@ -3391,7 +3399,7 @@ void Application::displayOverlay() { if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMapV2)) { renderCoverageMapV2(); } - + if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) { renderCoverageMap(); } @@ -3400,7 +3408,7 @@ void Application::displayOverlay() { if (_chatEntryOn) { _chatEntry.render(_glWidget->width(), _glWidget->height()); } - + // Show on-screen msec timer if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; @@ -3410,7 +3418,7 @@ void Application::displayOverlay() { drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); } - + // render the webcam input frame _webcam.renderPreview(_glWidget->width(), _glWidget->height()); @@ -3465,7 +3473,7 @@ void Application::displayOverlay() { if (_pieMenu.isDisplayed()) { _pieMenu.render(); } - + glPopMatrix(); } @@ -3474,7 +3482,7 @@ void Application::displayStats() { const int PELS_PER_LINE = 15; char stats[200]; statsVerticalOffset += PELS_PER_LINE; - sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ", + sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps ", _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, stats); @@ -3510,7 +3518,7 @@ void Application::displayStats() { sprintf(pingStats, "Ping audio/avatar/voxel: %d / %d / %d avg %d max ", pingAudio, pingAvatar, pingVoxel, pingVoxelMax); drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, pingStats); } - + char avatarStats[200]; statsVerticalOffset += PELS_PER_LINE; glm::vec3 avatarPos = _myAvatar.getPosition(); @@ -3530,7 +3538,7 @@ void Application::displayStats() { drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarMixerStats); - // Used for formatting voxel stats details + // Used for formatting voxel stats details statsVerticalOffset += PELS_PER_LINE; // skip a line for voxels QLocale locale(QLocale::English); std::stringstream voxelStats; @@ -3578,7 +3586,7 @@ void Application::displayStats() { // Server Voxels voxelStats.str(""); - voxelStats << + voxelStats << "Server Voxels Total: " << serversTotalString.toLocal8Bit().constData() << " / " << "Internal: " << serversInternalString.toLocal8Bit().constData() << " / " << "Leaves: " << serversLeavesString.toLocal8Bit().constData() << ""; @@ -3594,7 +3602,7 @@ void Application::displayStats() { // Local Voxels voxelStats.str(""); - voxelStats << + voxelStats << "Local Voxels Total: " << localTotalString.toLocal8Bit().constData() << " / " << "Internal: " << localInternalString.toLocal8Bit().constData() << " / " << "Leaves: " << localLeavesString.toLocal8Bit().constData() << ""; @@ -3603,7 +3611,7 @@ void Application::displayStats() { // Local Voxel Memory Usage voxelStats.str(""); - voxelStats << + voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB " "Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " << "VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB "; @@ -3616,8 +3624,8 @@ void Application::displayStats() { // Voxel Rendering voxelStats.str(""); voxelStats.precision(4); - voxelStats << "Voxel Rendering Slots " << - "Max: " << _voxels.getMaxVoxels() / 1000.f << "K " << + voxelStats << "Voxel Rendering Slots " << + "Max: " << _voxels.getMaxVoxels() / 1000.f << "K " << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " << "Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K "; statsVerticalOffset += PELS_PER_LINE; @@ -3632,9 +3640,9 @@ void Application::displayStats() { int voxelPacketsToProcess = _voxelProcessor.packetsToProcessCount(); QString packetsString = locale.toString((int)voxelPacketsToProcess); QString maxString = locale.toString((int)_recentMaxPackets); - voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() + voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData() << " [Recent Max: " << maxString.toLocal8Bit().constData() << "]"; - + if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) { _recentMaxPackets = 0; _resetRecentMaxPacketsSoon = false; @@ -3650,15 +3658,15 @@ void Application::displayStats() { drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); - // Leap data + // Leap data statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)LeapManager::statusString().c_str()); - + if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups char** perfStatLinesArray = new char*[PerfStat::getGroupCount()+1]; int lines = PerfStat::DumpStats(perfStatLinesArray); - + for (int line=0; line < lines; line++) { statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); @@ -3699,12 +3707,12 @@ void Application::renderLineToTouchedVoxel() { glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { float horizontalScale = _glWidget->width() / 2.0f; float verticalScale = _glWidget->height() / 2.0f; - - // -1,-1 is 0,windowHeight + + // -1,-1 is 0,windowHeight // 1,1 is windowWidth,0 - + // -1,1 1,1 - // +-----------------------+ + // +-----------------------+ // | | | // | | | // | -1,0 | | @@ -3715,23 +3723,23 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { // | | | // +-----------------------+ // -1,-1 1,-1 - - glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, + + glm::vec2 screenPoint((projectedPoint.x + 1.0) * horizontalScale, ((projectedPoint.y + 1.0) * -verticalScale) + _glWidget->height()); - + return screenPoint; } // render the coverage map on screen void Application::renderCoverageMapV2() { - + //qDebug("renderCoverageMap()\n"); - + glDisable(GL_LIGHTING); glLineWidth(2.0); glBegin(GL_LINES); glColor3f(0,1,1); - + renderCoverageMapsV2Recursively(&_voxels.myCoverageMapV2); glEnd(); @@ -3745,7 +3753,7 @@ void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) { glm::vec2 firstPoint = getScaledScreenPoint(box.getVertex(0)); glm::vec2 lastPoint(firstPoint); - + for (int i = 1; i < box.getVertexCount(); i++) { glm::vec2 thisPoint = getScaledScreenPoint(box.getVertex(i)); @@ -3769,14 +3777,14 @@ void Application::renderCoverageMapsV2Recursively(CoverageMapV2* map) { // render the coverage map on screen void Application::renderCoverageMap() { - + //qDebug("renderCoverageMap()\n"); - + glDisable(GL_LIGHTING); glLineWidth(2.0); glBegin(GL_LINES); glColor3f(0,0,1); - + renderCoverageMapsRecursively(&_voxels.myCoverageMap); glEnd(); @@ -3785,9 +3793,9 @@ void Application::renderCoverageMap() { void Application::renderCoverageMapsRecursively(CoverageMap* map) { for (int i = 0; i < map->getPolygonCount(); i++) { - + OctreeProjectedPolygon* polygon = map->getPolygon(i); - + if (polygon->getProjectionType() == (PROJECTION_RIGHT | PROJECTION_NEAR | PROJECTION_BOTTOM)) { glColor3f(.5,0,0); // dark red } else if (polygon->getProjectionType() == (PROJECTION_NEAR | PROJECTION_RIGHT)) { @@ -3814,7 +3822,7 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { glm::vec2 firstPoint = getScaledScreenPoint(polygon->getVertex(0)); glm::vec2 lastPoint(firstPoint); - + for (int i = 1; i < polygon->getVertexCount(); i++) { glm::vec2 thisPoint = getScaledScreenPoint(polygon->getVertex(i)); @@ -3840,16 +3848,16 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { if (!Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { return; } - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); if (!selfAvatarOnly) { // Render avatars of other nodes NodeList* nodeList = NodeList::getInstance(); - + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->lock(); - + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar *avatar = (Avatar *)node->getLinkedData(); if (!avatar->isInitialized()) { @@ -3858,17 +3866,17 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { avatar->render(false); avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); } - + node->unlock(); } - + // render avatar fades Glower glower; for (vector::iterator fade = _avatarFades.begin(); fade != _avatarFades.end(); fade++) { (*fade)->render(false); } } - + // Render my own Avatar _myAvatar.render(forceRenderHead); _myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); @@ -3881,7 +3889,7 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { // renderViewFrustum() // // Description: this will render the view frustum bounds for EITHER the head -// or the "myCamera". +// or the "myCamera". // // Frustum rendering mode. For debug purposes, we allow drawing the frustum in a couple of different ways. // We can draw it with each of these parts: @@ -3898,12 +3906,12 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { void Application::renderViewFrustum(ViewFrustum& viewFrustum) { // Load it with the latest details! loadViewFrustum(_myCamera, viewFrustum); - + glm::vec3 position = viewFrustum.getOffsetPosition(); glm::vec3 direction = viewFrustum.getOffsetDirection(); glm::vec3 up = viewFrustum.getOffsetUp(); glm::vec3 right = viewFrustum.getOffsetRight(); - + // Get ready to draw some lines glDisable(GL_LIGHTING); glColor4f(1.0, 1.0, 1.0, 1.0); @@ -3937,7 +3945,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_NEAR_PLANE) { // Drawing the bounds of the frustum - // viewFrustum.getNear plane - bottom edge + // viewFrustum.getNear plane - bottom edge glColor3f(1,0,0); glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); @@ -3958,7 +3966,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_FAR_PLANE) { - // viewFrustum.getFar plane - bottom edge + // viewFrustum.getFar plane - bottom edge glColor3f(0,1,0); glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); @@ -3979,7 +3987,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES) { // RIGHT PLANE IS CYAN - // right plane - bottom edge - viewFrustum.getNear to distant + // right plane - bottom edge - viewFrustum.getNear to distant glColor3f(0,1,1); glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); @@ -3997,7 +4005,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { // left plane - top edge - viewFrustum.getNear to distant glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); - + // focal plane - bottom edge glColor3f(1.0f, 0.0f, 1.0f); float focalProportion = (viewFrustum.getFocalLength() - viewFrustum.getNearClip()) / @@ -4051,10 +4059,10 @@ bool Application::maybeEditVoxelUnderCursor() { _mouseVoxel.green, _mouseVoxel.blue, Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel)); - + // remember the position for drag detection _justEditedVoxel = true; - + } } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { deleteVoxelUnderCursor(); @@ -4066,7 +4074,7 @@ bool Application::maybeEditVoxelUnderCursor() { fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger; fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; _voxelFades.push_back(fade); - + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)) { eyedropperVoxelUnderCursor(); } else { @@ -4092,8 +4100,8 @@ void Application::deleteVoxelUnderCursor() { void Application::eyedropperVoxelUnderCursor() { VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); if (selectedNode && selectedNode->isColored()) { - QColor selectedColor(selectedNode->getColor()[RED_INDEX], - selectedNode->getColor()[GREEN_INDEX], + QColor selectedColor(selectedNode->getColor()[RED_INDEX], + selectedNode->getColor()[GREEN_INDEX], selectedNode->getColor()[BLUE_INDEX]); if (selectedColor.isValid()) { @@ -4119,14 +4127,14 @@ void Application::toggleFollowMode() { void Application::resetSensors() { _headMouseX = _mouseX = _glWidget->width() / 2; _headMouseY = _mouseY = _glWidget->height() / 2; - + if (_serialHeadSensor.isActive()) { _serialHeadSensor.resetAverages(); } _webcam.reset(); _faceshift.reset(); LeapManager::reset(); - + if (OculusManager::isConnected()) { OculusManager::reset(); } @@ -4136,7 +4144,7 @@ void Application::resetSensors() { _myTransmitter.resetLevels(); _myAvatar.setVelocity(glm::vec3(0,0,0)); _myAvatar.setThrust(glm::vec3(0,0,0)); - + QMetaObject::invokeMethod(&_audio, "reset", Qt::QueuedConnection); } @@ -4183,12 +4191,12 @@ void Application::updateWindowTitle(){ void Application::domainChanged(QString domain) { // update the user's last domain in their Profile (which will propagate to data-server) _profile.updateDomain(domain); - + updateWindowTitle(); // reset the environment so that we don't erroneously end up with multiple _environment.resetToDefault(); - + // reset our node to stats and node to jurisdiction maps... since these must be changing... _voxelServerJurisdictions.clear(); _octreeServerSceneStats.clear(); @@ -4196,7 +4204,7 @@ void Application::domainChanged(QString domain) { } void Application::nodeAdded(Node* node) { - + } void Application::nodeKilled(Node* node) { @@ -4210,7 +4218,7 @@ void Application::nodeKilled(Node* node) { printf("voxel server going away...... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); - + // Add the jurisditionDetails object to the list of "fade outs" if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); @@ -4219,18 +4227,18 @@ void Application::nodeKilled(Node* node) { fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; _voxelFades.push_back(fade); } - + // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server _voxelServerJurisdictions.erase(nodeUUID); } - + // also clean up scene stats for that server _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } _voxelSceneStatsLock.unlock(); - + } else if (node->getType() == NODE_TYPE_PARTICLE_SERVER) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... @@ -4241,7 +4249,7 @@ void Application::nodeKilled(Node* node) { printf("particle server going away...... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); - + // Add the jurisditionDetails object to the list of "fade outs" if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); @@ -4250,39 +4258,39 @@ void Application::nodeKilled(Node* node) { fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; _voxelFades.push_back(fade); } - + // If the voxel server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server _particleServerJurisdictions.erase(nodeUUID); } - + // also clean up scene stats for that server _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { _octreeServerSceneStats.erase(nodeUUID); } _voxelSceneStatsLock.unlock(); - + } else if (node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = static_cast(node->getLinkedData()); if (avatar == _lookatTargetAvatar) { _lookatTargetAvatar = NULL; } - + // take over the avatar in order to fade it out node->setLinkedData(NULL); - + _avatarFades.push_back(avatar); } } -void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, +void Application::trackIncomingVoxelPacket(unsigned char* messageData, ssize_t messageLength, const HifiSockAddr& senderSockAddr, bool wasStatsPacket) { - + // Attempt to identify the sender from it's address. Node* serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (serverNode) { QUuid nodeUUID = serverNode->getUUID(); - + // now that we know the node ID, let's add these stats to the stats for that node... _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { @@ -4297,16 +4305,16 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // But, also identify the sender, and keep track of the contained jurisdiction root for this server Node* server = NodeList::getInstance()->nodeWithAddress(senderSockAddr); - - // parse the incoming stats datas stick it in a temporary object for now, while we + + // parse the incoming stats datas stick it in a temporary object for now, while we // determine which server it belongs to VoxelSceneStats temp; int statsMessageLength = temp.unpackFromMessage(messageData, messageLength); - + // quick fix for crash... why would voxelServer be NULL? if (server) { QUuid nodeUUID = server->getUUID(); - + // now that we know the node ID, let's add these stats to the stats for that node... _voxelSceneStatsLock.lockForWrite(); if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { @@ -4315,10 +4323,10 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen _octreeServerSceneStats[nodeUUID] = temp; } _voxelSceneStatsLock.unlock(); - + VoxelPositionSize rootDetails; voxelDetailsForCode(temp.getJurisdictionRoot(), rootDetails); - + // see if this is the first we've heard of this node... NodeToJurisdictionMap* jurisdiction = NULL; if (server->getType() == NODE_TYPE_VOXEL_SERVER) { @@ -4326,8 +4334,8 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen } else { jurisdiction = &_particleServerJurisdictions; } - - + + if (jurisdiction->find(nodeUUID) == jurisdiction->end()) { printf("stats from new server... v[%f, %f, %f, %f]\n", rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); @@ -4354,12 +4362,12 @@ int Application::parseOctreeStats(unsigned char* messageData, ssize_t messageLen // Receive packets from other nodes/servers and decide what to do with them! void* Application::networkReceive(void* args) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()"); HifiSockAddr senderSockAddr; ssize_t bytesReceived; - + Application* app = Application::getInstance(); while (!app->_stopNetworkReceiveThread) { if (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams() && @@ -4367,36 +4375,36 @@ void* Application::networkReceive(void* args) { MAX_PACKET_SIZE, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()))) { - + app->_packetCount++; app->_bytesCount += bytesReceived; - + if (packetVersionMatch(app->_incomingPacket)) { // only process this packet if we have a match on the packet version switch (app->_incomingPacket[0]) { case PACKET_TYPE_TRANSMITTER_DATA_V2: // V2 = IOS transmitter app app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived); - + break; case PACKET_TYPE_MIXED_AUDIO: QMetaObject::invokeMethod(&app->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray((char*) app->_incomingPacket, bytesReceived))); break; - + case PACKET_TYPE_PARTICLE_ADD_RESPONSE: // look up our ParticleEditHanders.... ParticleEditHandle::handleAddResponse(app->_incomingPacket, bytesReceived); break; - + case PACKET_TYPE_PARTICLE_DATA: case PACKET_TYPE_VOXEL_DATA: case PACKET_TYPE_VOXEL_ERASE: case PACKET_TYPE_OCTREE_STATS: case PACKET_TYPE_ENVIRONMENT_DATA: { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); - + bool wantExtraDebugging = app->getLogger()->extraDebugging(); if (wantExtraDebugging && app->_incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) { int numBytesPacketHeader = numBytesForPacketHeader(app->_incomingPacket); @@ -4408,10 +4416,10 @@ void* Application::networkReceive(void* args) { dataAt += sizeof(VOXEL_PACKET_SENT_TIME); VOXEL_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; - + printf("got PACKET_TYPE_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); - } - + } + // add this packet to our list of voxel packets and process them on the voxel processing app->_voxelProcessor.queueReceivedPacket(senderSockAddr, app->_incomingPacket, bytesReceived); break; @@ -4443,15 +4451,15 @@ void* Application::networkReceive(void* args) { break; } } - + if (app->_enableNetworkThread) { - pthread_exit(0); + pthread_exit(0); } - return NULL; + return NULL; } void Application::packetSentNotification(ssize_t length) { - _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); + _bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length); } void Application::loadScript() { @@ -4459,11 +4467,11 @@ void Application::loadScript() { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/script.js"); - QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, + QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName, tr("JavaScript Files (*.js)")); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); const char* fileName = fileNameAscii.data(); - + printf("fileName:%s\n",fileName); std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); @@ -4481,7 +4489,7 @@ void Application::loadScript() { char* entireFile = new char[fileLength+1]; file.read((char*)entireFile, fileLength); file.close(); - + entireFile[fileLength] = 0;// null terminate QString script(entireFile); delete[] entireFile; @@ -4490,10 +4498,10 @@ void Application::loadScript() { bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself - ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), + ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), &_controllerScriptingInterface); scriptEngine->setupMenuItems(); - + // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender); @@ -4501,7 +4509,7 @@ void Application::loadScript() { QThread* workerThread = new QThread(this); - // when the worker thread is started, call our engine's run.. + // when the worker thread is started, call our engine's run.. connect(workerThread, SIGNAL(started()), scriptEngine, SLOT(run())); // when the thread is terminated, add both scriptEngine and thread to the deleteLater queue @@ -4510,9 +4518,9 @@ void Application::loadScript() { // when the application is about to quit, stop our script engine so it unwinds properly connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); - + scriptEngine->moveToThread(workerThread); - + // Starts an event loop, and emits workerThread->started() workerThread->start(); diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index 598db13614..ccdf1ed8e2 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -27,19 +27,19 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer bool OctreeSendThread::process() { uint64_t start = usecTimestampNow(); bool gotLock = false; - + // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - + if (node) { // make sure the node list doesn't kill our node while we're using it if (node->trylock()) { gotLock = true; OctreeQueryNode* nodeData = NULL; - + nodeData = (OctreeQueryNode*) node->getLinkedData(); - + int packetsSent = 0; // Sometimes the node data has not yet been linked, in which case we can't really do anything @@ -50,7 +50,7 @@ bool OctreeSendThread::process() { } packetsSent = packetDistributor(node, nodeData, viewFrustumChanged); } - + node->unlock(); // we're done with this node for now. } } @@ -59,7 +59,7 @@ bool OctreeSendThread::process() { qDebug("OctreeSendThread::process() waiting for isInitialLoadComplete()\n"); } } - + // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap if (isStillRunning() && gotLock) { // dynamically sleep until we need to fire off the next set of octree elements @@ -99,14 +99,14 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in nodeData->resetOctreePacket(true); // we still need to reset it though! return packetsSent; // without sending... } - + const unsigned char* messageData = nodeData->getPacket(); int numBytesPacketHeader = numBytesForPacketHeader(messageData); const unsigned char* dataAt = messageData + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); - + // If we've got a stats message ready to send, then see if we can piggyback them together if (nodeData->stats.isReadyToSend()) { @@ -126,13 +126,14 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in int thisWastedBytes = 0; _totalWastedBytes += thisWastedBytes; _totalBytes += nodeData->getPacketLength(); - _totalPackets++; + _totalPackets++; if (debug) { - qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << - " size: " << nodeData->getPacketLength() << " [" << _totalBytes << + qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + " statsMessageLength: " << statsMessageLength << + " original size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } - + // actually send it NodeList::getInstance()->getNodeSocket().writeDatagram((char*) statsMessage, statsMessageLength, node->getActiveSocket()->getAddress(), @@ -151,7 +152,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += statsMessageLength; _totalPackets++; if (debug) { - qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << statsMessageLength << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -171,7 +172,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += nodeData->getPacketLength(); _totalPackets++; if (debug) { - qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -191,7 +192,7 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in _totalBytes += nodeData->getPacketLength(); _totalPackets++; if (debug) { - qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << + qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence << " size: " << nodeData->getPacketLength() << " [" << _totalBytes << "] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]\n"; } @@ -205,12 +206,13 @@ int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, in packetsSent++; nodeData->resetOctreePacket(); } - + return packetsSent; } /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + bool forceDebugging = false; int truePacketsSent = 0; int trueBytesSent = 0; @@ -220,29 +222,32 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b // FOR NOW... node tells us if it wants to receive only view frustum deltas bool wantDelta = viewFrustumChanged && nodeData->getWantDelta(); - // If our packet already has content in it, then we must use the color choice of the waiting packet. - // If we're starting a fresh packet, then... - // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use + // If our packet already has content in it, then we must use the color choice of the waiting packet. + // If we're starting a fresh packet, then... + // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // the clients requested color state. bool wantColor = nodeData->getWantColor(); bool wantCompression = nodeData->getWantCompression(); // If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color - // then let's just send that waiting packet. + // then let's just send that waiting packet. if (!nodeData->getCurrentPacketFormatMatches()) { if (nodeData->isPacketWaiting()) { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- format change " + "wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s " + "currentPacketIsCompressed=%s\n", + __LINE__, debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n", debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), + debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); } nodeData->resetOctreePacket(); @@ -252,57 +257,57 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n", __LINE__, debug::valueOf(wantCompression), targetSize); } - + _packetData.changeSettings(wantCompression, targetSize); } - + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), + qDebug("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n", + debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); } const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n", + debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), debug::valueOf(nodeData->getViewSent()) ); } - - // If the current view frustum has changed OR we have nothing to send, then search against + + // If the current view frustum has changed OR we have nothing to send, then search against // the current view frustum for things to send. if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { uint64_t now = usecTimestampNow(); - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n", debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); if (nodeData->getLastTimeBagEmpty() > 0) { float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; if (viewFrustumChanged) { - printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); + qDebug("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); } else { - printf("elapsed time to send scene = %f seconds", elapsedSceneSend); + qDebug("elapsed time to send scene = %f seconds", elapsedSceneSend); } - printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), + qDebug(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n", + debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), debug::valueOf(wantColor)); } } - + // if our view has changed, we need to reset these things... if (viewFrustumChanged) { if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) { nodeData->dumpOutOfView(); } nodeData->map.erase(); - } - + } + if (!viewFrustumChanged && !nodeData->getWantDelta()) { // only set our last sent time if we weren't resetting due to frustum change uint64_t now = usecTimestampNow(); @@ -313,34 +318,42 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b nodeData->stats.sceneCompleted(); ::endSceneSleepTime = _usleepTime; unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; - + unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - if (_myServer->wantsDebugSending()) { - qDebug() << "Scene completed at " << usecTimestampNow() - << "encodeTime:" << encodeTime - << " sleepTime:" << sleepTime - << " elapsed:" << elapsedTime - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- completed scene \n", __LINE__ ); + } + int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + packetsSentThisInterval += packetsJustSent; + if (forceDebugging) { + qDebug("packetsJustSent=%d packetsSentThisInterval=%d\n", packetsJustSent, packetsSentThisInterval); + } + + if (forceDebugging || _myServer->wantsDebugSending()) { + qDebug() << "Scene completed at " << usecTimestampNow() + << "encodeTime:" << encodeTime + << " sleepTime:" << sleepTime + << " elapsed:" << elapsedTime + << " Packets:" << _totalPackets + << " Bytes:" << _totalBytes << " Wasted:" << _totalWastedBytes << "\n"; } - + // start tracking our stats - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - + // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { nodeData->nodeBag.deleteAll(); } - if (_myServer->wantsDebugSending()) { - qDebug() << "Scene started at " << usecTimestampNow() - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes + if (forceDebugging || _myServer->wantsDebugSending()) { + qDebug() << "Scene started at " << usecTimestampNow() + << " Packets:" << _totalPackets + << " Bytes:" << _totalBytes << " Wasted:" << _totalWastedBytes << "\n"; } @@ -367,18 +380,19 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - + if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } - + int extraPackingAttempts = 0; + bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); } @@ -391,27 +405,32 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b float voxelSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() + int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); - - EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, + + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction()); - + _myServer->getOctree()->lockForRead(); nodeData->stats.encodeStarted(); bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - + + // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've + // sent the entire scene. We want to know this below so we'll actually write this content into + // the packet and send it + completedScene = nodeData->nodeBag.isEmpty(); + // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. if (_packetData.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) { - if (_packetData.hasContent() && bytesWritten == 0 && + if (_packetData.hasContent() && bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { lastNodeDidntFit = true; } @@ -432,51 +451,56 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b bytesWritten = 0; somethingToSend = false; // this will cause us to drop out of the loop... } - + // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a - // little bit more in this packet. To do this we - + // little bit more in this packet. To do this we write into the packet, but don't send it yet, we'll + // keep attempting to write in compressed mode to add more compressed segments + // We only consider sending anything if there is something in the _packetData to send... But // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases - // mean we should send the previous packet contents and reset it. - if (lastNodeDidntFit) { + // mean we should send the previous packet contents and reset it. + if (completedScene || lastNodeDidntFit) { if (_packetData.hasContent()) { // if for some reason the finalized size is greater than our available size, then probably the "compressed" // form actually inflated beyond our padding, and in this case we will send the current packet, then // write to out new packet... - int writtenSize = _packetData.getFinalizedSize() + int writtenSize = _packetData.getFinalizedSize() + (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0); - - + + if (writtenSize > nodeData->getAvailable()) { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", - writtenSize, nodeData->getAvailable()); + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug("about to call handlePacketSend() .... line: %d -- " + "writtenSize[%d] > available[%d] too big, sending packet as is.\n", + __LINE__, writtenSize, nodeData->getAvailable()); } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", - nodeData->getAvailable(), _packetData.getFinalizedSize(), + if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { + qDebug(">>>>>> calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", + nodeData->getAvailable(), _packetData.getFinalizedSize(), _packetData.getUncompressedSize(), _packetData.getTargetSize()); } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); extraPackingAttempts = 0; } - - // If we're not running compressed, the we know we can just send now. Or if we're running compressed, but + + // If we're not running compressed, then we know we can just send now. Or if we're running compressed, but // the packet doesn't have enough space to bother attempting to pack more... bool sendNow = true; - - if (nodeData->getCurrentPacketIsCompressed() && + + if (nodeData->getCurrentPacketIsCompressed() && nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { sendNow = false; // try to pack more } - + int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { + if (forceDebugging) { + qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE\n", __LINE__); + } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); @@ -491,14 +515,14 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, debug::valueOf(nodeData->getWantCompression()), targetSize); } _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset } } - - + + // Here's where we can/should allow the server to send other data... // send the environment packet if (_myServer->hasSpecialPacketToSend()) { @@ -506,14 +530,14 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b truePacketsSent++; packetsSentThisInterval++; } - - + + uint64_t end = usecTimestampNow(); int elapsedmsec = (end - start)/1000; uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls(); int elapsedCompressCalls = endCompressCalls - startCompressCalls; - + uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; @@ -521,18 +545,24 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b if (elapsedmsec > 100) { if (elapsedmsec > 1000) { int elapsedsec = (end - start)/1000000; - printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets %d nodes still to send\n", + elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } else { - printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } } else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); + qDebug("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " + "to generate %d bytes in %d packets, %d nodes still to send\n", + elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, + trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); } - - // if after sending packets we've emptied our bag, then we want to remember that we've sent all + + // if after sending packets we've emptied our bag, then we want to remember that we've sent all // the voxels from the current view frustum if (nodeData->nodeBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); @@ -544,11 +574,13 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b } if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); + qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d " + "server PPI=%d nodePPS=%d nodePPI=%d\n", + truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, + _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), + clientMaxPacketsPerInterval); } - + } // end if bag wasn't empty, and so we sent stuff... return truePacketsSent; diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index 2ab1c3cfec..2a7f073ca4 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -514,7 +514,7 @@ void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSo if (packetType == getMyQueryMessageType()) { bool debug = false; if (debug) { - qDebug() << "Got PACKET_TYPE_VOXEL_QUERY at " << usecTimestampNow() << "\n"; + qDebug() << "Got PACKET_TYPE_VOXEL_QUERY at" << usecTimestampNow() << "\n"; } int numBytesPacketHeader = numBytesForPacketHeader((unsigned char*) dataByteArray.data()); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index f59121877a..0fad79e625 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -54,7 +54,7 @@ void OctreeElement::init(unsigned char * octalCode) { memcpy(_octalCode.buffer, octalCode, octalCodeLength); delete[] octalCode; } - + // set up the _children union _childBitmask = 0; _childrenExternal = false; @@ -64,7 +64,7 @@ void OctreeElement::init(unsigned char * octalCode) { _singleChildrenCount++; #endif _childrenCount[0]++; - + // default pointers to child nodes to NULL #ifdef HAS_AUDIT_CHILDREN for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { @@ -81,7 +81,7 @@ void OctreeElement::init(unsigned char * octalCode) { #ifdef SIMPLE_EXTERNAL_CHILDREN _children.single = NULL; #endif - + _isDirty = true; _shouldRender = false; _sourceUUIDKey = 0; @@ -100,13 +100,13 @@ OctreeElement::~OctreeElement() { _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); delete[] _octalCode.pointer; } - + // delete all of this node's children, this also takes care of all population tracking data deleteAllChildren(); } -void OctreeElement::markWithChangedTime() { - _lastChanged = usecTimestampNow(); +void OctreeElement::markWithChangedTime() { + _lastChanged = usecTimestampNow(); notifyUpdateHooks(); // if the node has changed, notify our hooks } @@ -121,7 +121,7 @@ void OctreeElement::handleSubtreeChanged(Octree* myTree) { if (myTree->getShouldReaverage()) { calculateAverageFromChildren(); } - + markWithChangedTime(); } @@ -184,10 +184,10 @@ void OctreeElement::setShouldRender(bool shouldRender) { void OctreeElement::calculateAABox() { glm::vec3 corner; - + // copy corner into box copyFirstVertexForCode(getOctalCode(),(float*)&corner); - + // this tells you the "size" of the voxel float voxelScale = 1 / powf(2, numberOfThreeBitSectionsInCode(getOctalCode())); _box.setBox(corner,voxelScale); @@ -201,7 +201,7 @@ void OctreeElement::deleteChildAtIndex(int childIndex) { setChildAtIndex(childIndex, NULL); _isDirty = true; markWithChangedTime(); - + // after deleting the child, check to see if we're a leaf if (isLeaf()) { _voxelNodeLeafCount++; @@ -219,13 +219,13 @@ OctreeElement* OctreeElement::removeChildAtIndex(int childIndex) { setChildAtIndex(childIndex, NULL); _isDirty = true; markWithChangedTime(); - + // after removing the child, check to see if we're a leaf if (isLeaf()) { _voxelNodeLeafCount++; } } - + #ifdef HAS_AUDIT_CHILDREN auditChildren("removeChildAtIndex()"); #endif // def HAS_AUDIT_CHILDREN @@ -238,12 +238,12 @@ void OctreeElement::auditChildren(const char* label) const { for (int childIndex = 0; childIndex < NUMBER_OF_CHILDREN; childIndex++) { OctreeElement* testChildNew = getChildAtIndex(childIndex); OctreeElement* testChildOld = _childrenArray[childIndex]; - + if (testChildNew != testChildOld) { auditFailed = true; } } - + const bool alwaysReport = false; // set this to true to get additional debugging if (alwaysReport || auditFailed) { qDebug("%s... auditChildren() %s <<<< \n", label, (auditFailed ? "FAILED" : "PASSED")); @@ -309,7 +309,7 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { return NULL; } } break; - + default : { return _children.external[childIndex]; } break; @@ -320,11 +320,11 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { PerformanceWarning warn(false,"getChildAtIndex",false,&_getChildAtIndexTime,&_getChildAtIndexCalls); OctreeElement* result = NULL; int childCount = getChildCount(); - + #ifdef HAS_AUDIT_CHILDREN const char* caseStr = NULL; #endif - + switch (childCount) { case 0: #ifdef HAS_AUDIT_CHILDREN @@ -424,7 +424,7 @@ OctreeElement* OctreeElement::getChildAtIndex(int childIndex) const { caseStr, result,_childrenArray[childIndex]); } #endif // def HAS_AUDIT_CHILDREN - return result; + return result; #endif } @@ -435,7 +435,7 @@ void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* chi const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset)) { // if previously external, then clean it up... @@ -455,7 +455,7 @@ void OctreeElement::storeTwoChildren(OctreeElement* childOne, OctreeElement* chi _twoChildrenOffsetCount++; } else { // encode in array - + // if not previously external, then allocate appropriately if (!_childrenExternal) { _childrenExternal = true; @@ -516,7 +516,7 @@ void OctreeElement::encodeThreeOffsets(int64_t offsetOne, int64_t offsetTwo, int const uint64_t ENCODE_BITS = 21; const uint64_t ENCODE_MASK = 0xFFFFF; const uint64_t ENCODE_MASK_SIGN = 0x100000; - + uint64_t offsetEncodedOne, offsetEncodedTwo, offsetEncodedThree; if (offsetOne < 0) { offsetEncodedOne = ((-offsetOne & ENCODE_MASK) | ENCODE_MASK_SIGN); @@ -544,13 +544,13 @@ void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* c int64_t offsetOne = (uint8_t*)childOne - (uint8_t*)this; int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; - + const int64_t minOffset = -1048576; // what can fit in 20 bits // std::numeric_limits::min(); const int64_t maxOffset = 1048576; // what can fit in 20 bits // std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset)) { // if previously external, then clean it up... @@ -566,7 +566,7 @@ void OctreeElement::storeThreeChildren(OctreeElement* childOne, OctreeElement* c _threeChildrenOffsetCount++; } else { // encode in array - + // if not previously external, then allocate appropriately if (!_childrenExternal) { _childrenExternal = true; @@ -609,13 +609,13 @@ void OctreeElement::checkStoreFourChildren(OctreeElement* childOne, OctreeElemen int64_t offsetTwo = (uint8_t*)childTwo - (uint8_t*)this; int64_t offsetThree = (uint8_t*)childThree - (uint8_t*)this; int64_t offsetFour = (uint8_t*)childFour - (uint8_t*)this; - + const int64_t minOffset = std::numeric_limits::min(); const int64_t maxOffset = std::numeric_limits::max(); - + bool forceExternal = true; if (!forceExternal && - isBetween(offsetOne, maxOffset, minOffset) && + isBetween(offsetOne, maxOffset, minOffset) && isBetween(offsetTwo, maxOffset, minOffset) && isBetween(offsetThree, maxOffset, minOffset) && isBetween(offsetFour, maxOffset, minOffset) @@ -671,10 +671,10 @@ void OctreeElement::deleteAllChildren() { _externalChildrenCount--; _childrenCount[childCount]--; } break; - - + + } - + // If we had externally stored children, clean them too. if (_childrenExternal && _children.external) { delete[] _children.external; @@ -734,7 +734,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { _children.external[childIndex] = child; _externalChildrenMemoryUsage += NUMBER_OF_CHILDREN * sizeof(OctreeElement*); - + } else if (previousChildCount == 2 && newChildCount == 1) { assert(child == NULL); // we are removing a child, so this must be true! OctreeElement* previousFirstChild = _children.external[firstIndex]; @@ -757,7 +757,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // Here's how we store things... // If we have 0 or 1 children, then we just store them in the _children.single; - // If we have 2 children, + // If we have 2 children, // then if we can we store them as 32 bit signed offsets from our own this pointer, // _children.offsetsTwoChildren[0]-[1] // these are 32 bit offsets @@ -770,7 +770,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { clearAtBit(_childBitmask, childIndex); } int newChildCount = getChildCount(); - + // track our population data if (previousChildCount != newChildCount) { _childrenCount[previousChildCount]--; @@ -781,7 +781,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { if (previousChildCount == 0 && newChildCount == 0) { // nothing to do... } else if ((previousChildCount == 0 || previousChildCount == 1) && newChildCount == 1) { - // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, + // If we had 0 children, and we're setting our first child or if we had 1 child, or we're resetting the same child, // then we can just store it in _children.single _children.single = child; } else if (previousChildCount == 1 && newChildCount == 0) { @@ -803,21 +803,21 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } _singleChildrenCount--; - storeTwoChildren(childOne, childTwo); + storeTwoChildren(childOne, childTwo); } else if (previousChildCount == 2 && newChildCount == 1) { // If we had 2 children, and we're removing one, then we know we can go down to single mode //assert(child == NULL); // this is the only logical case - + int indexTwo = getNthBit(previousChildMask, 2); bool keepChildOne = indexTwo == childIndex; OctreeElement* childOne; OctreeElement* childTwo; - retrieveTwoChildren(childOne, childTwo); + retrieveTwoChildren(childOne, childTwo); _singleChildrenCount++; - + if (keepChildOne) { _children.single = childOne; } else { @@ -825,14 +825,14 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } } else if (previousChildCount == 2 && newChildCount == 2) { // If we had 2 children, and still have 2, then we know we are resetting one of our existing children - + int indexOne = getNthBit(previousChildMask, 1); bool replaceChildOne = indexOne == childIndex; - // Get the existing two children out of their encoding... + // Get the existing two children out of their encoding... OctreeElement* childOne; OctreeElement* childTwo; - retrieveTwoChildren(childOne, childTwo); + retrieveTwoChildren(childOne, childTwo); if (replaceChildOne) { childOne = child; @@ -841,7 +841,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } storeTwoChildren(childOne, childTwo); - + } else if (previousChildCount == 2 && newChildCount == 3) { // If we had 2 children, and now have 3, then we know we are going to an external case... @@ -850,8 +850,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveTwoChildren(childOne, childTwo); + // Get the existing two children out of their encoding... + retrieveTwoChildren(childOne, childTwo); // determine order of the existing children int indexOne = getNthBit(previousChildMask, 1); @@ -870,7 +870,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { storeThreeChildren(childOne, childTwo, childThree); } else if (previousChildCount == 3 && newChildCount == 2) { // If we had 3 children, and now have 2, then we know we are going from an external case to a potential internal case - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -882,8 +882,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); if (removeChildOne) { childOne = childTwo; @@ -894,10 +894,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // removing child three, nothing to do. } - storeTwoChildren(childOne, childTwo); + storeTwoChildren(childOne, childTwo); } else if (previousChildCount == 3 && newChildCount == 3) { // If we had 3 children, and now have 3, then we need to determine which item we're replacing... - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -909,8 +909,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childTwo; OctreeElement* childThree; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); if (replaceChildOne) { childOne = child; @@ -930,8 +930,8 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* childThree; OctreeElement* childFour; - // Get the existing two children out of their encoding... - retrieveThreeChildren(childOne, childTwo, childThree); + // Get the existing two children out of their encoding... + retrieveThreeChildren(childOne, childTwo, childThree); // determine order of the existing children int indexOne = getNthBit(previousChildMask, 1); @@ -959,9 +959,9 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { const int newChildCount = 4; _children.external = new OctreeElement*[newChildCount]; memset(_children.external, 0, sizeof(OctreeElement*) * newChildCount); - + _externalChildrenMemoryUsage += newChildCount * sizeof(OctreeElement*); - + _children.external[0] = childOne; _children.external[1] = childTwo; _children.external[2] = childThree; @@ -970,7 +970,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } else if (previousChildCount == 4 && newChildCount == 3) { // If we had 4 children, and now have 3, then we know we are going from an external case to a potential internal case //assert(_children.external && _childrenExternal && previousChildCount == 4); - + // We need to determine which children we had, and which one we got rid of... int indexOne = getNthBit(previousChildMask, 1); int indexTwo = getNthBit(previousChildMask, 2); @@ -1008,7 +1008,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { } else if (previousChildCount == newChildCount) { //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount); - + // 4 or more children, one item being replaced, we know we're stored externally, we just need to find the one // that needs to be replaced and replace it. for (int ordinal = 1; ordinal <= 8; ordinal++) { @@ -1024,12 +1024,12 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { // Growing case... previous must be 4 or greater //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount-1); - + // 4 or more children, one item being added, we know we're stored externally, we just figure out where to insert // this child pointer into our external list OctreeElement** newExternalList = new OctreeElement*[newChildCount]; memset(newExternalList, 0, sizeof(OctreeElement*) * newChildCount); - + int copiedCount = 0; for (int ordinal = 1; ordinal <= newChildCount; ordinal++) { int index = getNthBit(previousChildMask, ordinal); @@ -1037,10 +1037,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { newExternalList[ordinal - 1] = _children.external[ordinal - 1]; copiedCount++; } else { - + // insert our new child here... newExternalList[ordinal - 1] = child; - + // if we didn't copy all of our previous children, then we need to if (copiedCount < previousChildCount) { // our child needs to be inserted before this index, and everything else pushed out... @@ -1060,10 +1060,10 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { //assert(_children.external && _childrenExternal && previousChildCount >= 4); //assert(previousChildCount == newChildCount+1); - // 4 or more children, one item being removed, we know we're stored externally, we just figure out which + // 4 or more children, one item being removed, we know we're stored externally, we just figure out which // item to remove from our external list OctreeElement** newExternalList = new OctreeElement*[newChildCount]; - + for (int ordinal = 1; ordinal <= previousChildCount; ordinal++) { int index = getNthBit(previousChildMask, ordinal); //assert(index != -1); @@ -1090,7 +1090,7 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { if (getChildCount() == 4 && _childrenExternal && _children.external) { checkStoreFourChildren(_children.external[0], _children.external[1], _children.external[2], _children.external[3]); } - + #ifdef HAS_AUDIT_CHILDREN _childrenArray[childIndex] = child; @@ -1104,11 +1104,11 @@ void OctreeElement::setChildAtIndex(int childIndex, OctreeElement* child) { OctreeElement* OctreeElement::addChildAtIndex(int childIndex) { OctreeElement* childAt = getChildAtIndex(childIndex); if (!childAt) { - // before adding a child, see if we're currently a leaf + // before adding a child, see if we're currently a leaf if (isLeaf()) { _voxelNodeLeafCount--; } - + unsigned char* newChildCode = childOctalCode(getOctalCode(), childIndex); childAt = createNewElement(newChildCode); setChildAtIndex(childIndex, childAt); @@ -1133,11 +1133,15 @@ bool OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCoun if (!childToDelete->isLeaf()) { // delete all it's children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - deleteApproved = childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); - if (!deleteApproved) { - break; // no point in continuing... + if (childToDelete->getChildAtIndex(i)) { + deleteApproved = childToDelete->safeDeepDeleteChildAtIndex(i,recursionCount+1); + if (!deleteApproved) { + break; // no point in continuing... + } } } + } else { + deleteApproved = true; // because we got here after checking that delete was approved } if (deleteApproved) { deleteChildAtIndex(childIndex); @@ -1155,14 +1159,14 @@ void OctreeElement::printDebugDetails(const char* label) const { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElement* childAt = getChildAtIndex(i); if (childAt) { - setAtBit(childBits,i); + setAtBit(childBits,i); } } qDebug("%s - Voxel at corner=(%f,%f,%f) size=%f\n isLeaf=%s isDirty=%s shouldRender=%s\n children=", label, _box.getCorner().x, _box.getCorner().y, _box.getCorner().z, _box.getScale(), debug::valueOf(isLeaf()), debug::valueOf(isDirty()), debug::valueOf(getShouldRender())); - + outputBits(childBits, false); qDebug("\n octalCode="); printOctalCode(getOctalCode()); @@ -1188,10 +1192,10 @@ ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) c // There are two types of nodes for which we want to "render" // 1) Leaves that are in the LOD // 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children -// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE +// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE // in the LOD, and others that are not. In this case we want to render the parent, and none of the children. // -// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" +// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" // corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of. // By doing this, we don't need to test each child voxel's position vs the LOD boundary bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const { @@ -1285,7 +1289,7 @@ void OctreeElement::notifyUpdateHooks() { } } -bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, +bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const { return _box.findSpherePenetration(center, radius, penetration); } @@ -1307,7 +1311,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float } // otherwise, we need to find which of our children we should recurse glm::vec3 ourCenter = _box.calcCenter(); - + int childIndex = CHILD_UNKNOWN; // left half if (x > ourCenter.x) { @@ -1352,13 +1356,13 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float } } } - + // Now, check if we have a child at that location child = getChildAtIndex(childIndex); if (!child) { child = addChildAtIndex(childIndex); } - + // Now that we have the child to recurse down, let it answer the original question... return child->getOrCreateChildElementAt(x, y, z, s); } diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index edca3a1fc0..3323dec88b 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -7,39 +7,43 @@ // // -#include #include +#include #include #include // usecTimestampNow() -#include - #include -#include "ParticlesScriptingInterface.h" +// This is not ideal, but adding script-engine as a linked library, will cause a circular reference +// I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others +// headers, but not link to each other, this is essentially what this construct is doing, but would be +// better to add includes to the include path, but not link +#include "../../script-engine/src/ScriptEngine.h" + +#include "ParticlesScriptingInterface.h" #include "Particle.h" uint32_t Particle::_nextID = 0; -VoxelsScriptingInterface* Particle::_voxelsScriptingInterface = NULL; -ParticlesScriptingInterface* Particle::_particlesScriptingInterface = NULL; +VoxelEditPacketSender* Particle::_voxelEditSender = NULL; +ParticleEditPacketSender* Particle::_particleEditSender = NULL; -Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, +Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { - + init(position, radius, color, velocity, gravity, damping, inHand, updateScript, id); } Particle::Particle() { rgbColor noColor = { 0, 0, 0 }; - init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), + init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), DEFAULT_GRAVITY, DEFAULT_DAMPING, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE); } Particle::~Particle() { } -void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, +void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { if (id == NEW_PARTICLE) { _id = _nextID; @@ -51,7 +55,7 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 _lastEdited = now; _lastUpdated = now; _created = now; // will get updated as appropriate in setLifetime() - + _position = position; _radius = radius; memcpy(_color, color, sizeof(_color)); @@ -215,7 +219,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef _script = tempString; dataAt += scriptLength; bytesRead += scriptLength; - + //printf("Particle::readParticleDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -227,11 +231,11 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe unsigned char* dataAt = data; processedBytes = 0; - // the first part of the data is our octcode... + // the first part of the data is our octcode... int octets = numberOfThreeBitSectionsInCode(data); int lengthOfOctcode = bytesRequiredForCodeLength(octets); - - // we don't actually do anything with this octcode... + + // we don't actually do anything with this octcode... dataAt += lengthOfOctcode; processedBytes += lengthOfOctcode; @@ -240,7 +244,7 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe memcpy(&editID, dataAt, sizeof(editID)); dataAt += sizeof(editID); processedBytes += sizeof(editID); - + // special case for handling "new" particles if (editID == NEW_PARTICLE) { // If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that @@ -251,19 +255,19 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe processedBytes += sizeof(creatorTokenID); newParticle.setCreatorTokenID(creatorTokenID); newParticle._newlyCreated = true; - + newParticle.setLifetime(0); // this guy is new! } else { newParticle._id = editID; newParticle._newlyCreated = false; - } + } // lastEdited memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); dataAt += sizeof(newParticle._lastEdited); processedBytes += sizeof(newParticle._lastEdited); - + // radius memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); @@ -283,12 +287,12 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity)); dataAt += sizeof(newParticle._velocity); processedBytes += sizeof(newParticle._velocity); - + // gravity memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity)); dataAt += sizeof(newParticle._gravity); processedBytes += sizeof(newParticle._gravity); - + // damping memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping)); dataAt += sizeof(newParticle._damping); @@ -311,11 +315,11 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe const bool wantDebugging = false; if (wantDebugging) { - printf("Particle::fromEditPacket()...\n"); + printf("Particle::fromEditPacket()...\n"); printf(" Particle id in packet:%u\n", editID); newParticle.debugDump(); } - + return newParticle; } @@ -329,7 +333,7 @@ void Particle::debugDump() const { printf(" color:%d,%d,%d\n", _color[0], _color[1], _color[2]); } -bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, +bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut) { bool success = true; // assume the best @@ -338,13 +342,13 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, for (int i = 0; i < count && success; i++) { // get the octal code for the particle - unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, + unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, details[i].position.z, details[i].radius); int octets = numberOfThreeBitSectionsInCode(octcode); int lengthOfOctcode = bytesRequiredForCodeLength(octets); int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes(); - + // make sure we have room to copy this particle if (sizeOut + lenfthOfEditData > sizeIn) { success = false; @@ -353,9 +357,9 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, memcpy(copyAt, octcode, lengthOfOctcode); copyAt += lengthOfOctcode; sizeOut += lengthOfOctcode; - + // Now add our edit content details... - + // id memcpy(copyAt, &details[i].id, sizeof(details[i].id)); copyAt += sizeof(details[i].id); @@ -419,7 +423,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, sizeOut += scriptLength; bool wantDebugging = false; - if (wantDebugging) { + if (wantDebugging) { printf("encodeParticleEditMessageDetails()....\n"); printf("Particle id :%u\n", details[i].id); printf(" nextID:%u\n", _nextID); @@ -433,7 +437,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, } // adjust any internal timestamps to fix clock skew for this server -void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { +void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { unsigned char* dataAt = codeColorBuffer; int octets = numberOfThreeBitSectionsInCode(dataAt); int lengthOfOctcode = bytesRequiredForCodeLength(octets); @@ -456,7 +460,7 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz uint64_t lastEditedInServerTime = lastEditedInLocalTime + clockSkew; memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime)); const bool wantDebug = false; - if (wantDebug) { + if (wantDebug) { qDebug("Particle::adjustEditPacketForClockSkew()...\n"); qDebug() << " lastEditedInLocalTime: " << lastEditedInLocalTime << "\n"; qDebug() << " clockSkew: " << clockSkew << "\n"; @@ -470,7 +474,7 @@ void Particle::update() { float elapsed = static_cast(now - _lastUpdated); _lastUpdated = now; float timeElapsed = elapsed / static_cast(USECS_PER_SECOND); - + // calculate our default shouldDie state... then allow script to change it if it wants... float velocityScalar = glm::length(getVelocity()); const float STILL_MOVING = 0.05f / static_cast(TREE_SCALE); @@ -480,13 +484,13 @@ void Particle::update() { bool isInHand = getInHand(); bool shouldDie = getShouldDie() || (!isInHand && !isStillMoving && isReallyOld); setShouldDie(shouldDie); - + runUpdateScript(); // allow the javascript to alter our state - + // If the ball is in hand, it doesn't move or have gravity effect it if (!isInHand) { _position += _velocity * timeElapsed; - + // handle bounces off the ground... if (_position.y <= 0) { _velocity = _velocity * glm::vec3(1,-1,1); @@ -505,72 +509,59 @@ void Particle::update() { void Particle::runUpdateScript() { if (!_script.isEmpty()) { + ScriptEngine engine(_script); // no menu or controller interface... - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); - + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); - - QScriptValue result = engine.evaluate(_script); - + engine.registerGlobalObject("Particle", &particleScriptable); + + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); + particleScriptable.emitUpdate(); - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); + } + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } void Particle::collisionWithParticle(Particle* other) { if (!_script.isEmpty()) { + ScriptEngine engine(_script); // no menu or controller interface... - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); - + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); + engine.registerGlobalObject("Particle", &particleScriptable); + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); - if (getVoxelsScriptingInterface()) { - QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface()); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - } - - if (getParticlesScriptingInterface()) { - QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface()); - engine.globalObject().setProperty("Particles", particleScripterValue); - } - - QScriptValue result = engine.evaluate(_script); - ParticleScriptObject otherParticleScriptable(other); particleScriptable.emitCollisionWithParticle(&otherParticleScriptable); - if (getVoxelsScriptingInterface()) { - getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages(); + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); } - - if (getParticlesScriptingInterface()) { - getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages(); - } - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } @@ -578,45 +569,32 @@ void Particle::collisionWithParticle(Particle* other) { void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) { if (!_script.isEmpty()) { - QScriptEngine engine; - - // register meta-type for glm::vec3 and rgbColor conversions - registerMetaTypes(&engine); - + ScriptEngine engine(_script); // no menu or controller interface... + + // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so + // we can use the same ones as our context. + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the Particle object ParticleScriptObject particleScriptable(this); - QScriptValue particleValue = engine.newQObject(&particleScriptable); - engine.globalObject().setProperty("Particle", particleValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE); + engine.registerGlobalObject("Particle", &particleScriptable); + // init and evaluate the script, but return so we can emit the collision + engine.evaluate(); - if (getVoxelsScriptingInterface()) { - QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface()); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - } - - if (getParticlesScriptingInterface()) { - QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface()); - engine.globalObject().setProperty("Particles", particleScripterValue); - } - - QScriptValue result = engine.evaluate(_script); - VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails); particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable); - if (getVoxelsScriptingInterface()) { - getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages(); + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); } - - if (getParticlesScriptingInterface()) { - getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages(); - } - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); } } } @@ -625,7 +603,7 @@ void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) { void Particle::setLifetime(float lifetime) { uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND; - _created = usecTimestampNow() - lifetimeInUsecs; + _created = usecTimestampNow() - lifetimeInUsecs; } void Particle::copyChangedProperties(const Particle& other) { diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 9c92d0ec48..e93790eab7 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -21,6 +21,8 @@ class VoxelsScriptingInterface; class ParticlesScriptingInterface; +class VoxelEditPacketSender; +class ParticleEditPacketSender; const uint32_t NEW_PARTICLE = 0xFFFFFFFF; @@ -48,19 +50,19 @@ const bool IN_HAND = true; // it's in a hand const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand class Particle { - + public: Particle(); - Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, + Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, + glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE); - + /// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer - static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes); - + static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes); + virtual ~Particle(); - virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, + virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, + glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE); const glm::vec3& getPosition() const { return _position; } @@ -71,7 +73,7 @@ public: const glm::vec3& getGravity() const { return _gravity; } bool getInHand() const { return _inHand; } float getDamping() const { return _damping; } - + /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source uint64_t getLastUpdated() const { return _lastUpdated; } @@ -91,9 +93,9 @@ public: void setVelocity(const glm::vec3& value) { _velocity = value; } void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } void setColor(const xColor& value) { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color[RED_INDEX] = value.red; + _color[GREEN_INDEX] = value.green; + _color[BLUE_INDEX] = value.blue; } void setRadius(float value) { _radius = value; } void setGravity(const glm::vec3& value) { _gravity = value; } @@ -102,15 +104,15 @@ public: void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; } void setScript(QString updateScript) { _script = updateScript; } void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } - + bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); static int expectedBytes(); static int expectedEditMessageBytes(); - static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, + static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut); - + static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); void update(); @@ -118,22 +120,23 @@ public: void collisionWithVoxel(VoxelDetail* voxel); void debugDump() const; - + // similar to assignment/copy, but it handles keeping lifetime accurate void copyChangedProperties(const Particle& other); - - static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return _voxelsScriptingInterface; } - static ParticlesScriptingInterface* getParticlesScriptingInterface() { return _particlesScriptingInterface; } - static void setVoxelsScriptingInterface(VoxelsScriptingInterface* interface) - { _voxelsScriptingInterface = interface; } - - static void setParticlesScriptingInterface(ParticlesScriptingInterface* interface) - { _particlesScriptingInterface = interface; } - + static VoxelEditPacketSender* getVoxelEditPacketSender() { return _voxelEditSender; } + static ParticleEditPacketSender* getParticleEditPacketSender() { return _particleEditSender; } + + static void setVoxelEditPacketSender(VoxelEditPacketSender* interface) + { _voxelEditSender = interface; } + + static void setParticleEditPacketSender(ParticleEditPacketSender* interface) + { _particleEditSender = interface; } + + protected: - static VoxelsScriptingInterface* _voxelsScriptingInterface; - static ParticlesScriptingInterface* _particlesScriptingInterface; + static VoxelEditPacketSender* _voxelEditSender; + static ParticleEditPacketSender* _particleEditSender; void runUpdateScript(); static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); @@ -141,8 +144,8 @@ protected: static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color); static void xColorFromScriptValue(const QScriptValue &object, xColor& color); - void setLifetime(float lifetime); - + void setLifetime(float lifetime); + glm::vec3 _position; rgbColor _color; float _radius; @@ -184,7 +187,7 @@ public slots: float getRadius() const { return _particle->getRadius(); } bool getShouldDie() { return _particle->getShouldDie(); } float getLifetime() const { return _particle->getLifetime(); } - + void setPosition(glm::vec3 value) { _particle->setPosition(value); } void setVelocity(glm::vec3 value) { _particle->setVelocity(value); } void setGravity(glm::vec3 value) { _particle->setGravity(value); } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 1e1f3955fe..0760413e7f 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -19,12 +19,12 @@ #include "ParticleEditPacketSender.h" #include "ParticleTree.h" -ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, +ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { init(packetSender, particles, voxels, audio, selfAvatar); } -void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, +void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { _packetSender = packetSender; _particles = particles; @@ -82,7 +82,7 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); applyHardCollision(particle, penetration, ELASTICITY, DAMPING); - + delete voxelDetails; // cleanup returned details } } @@ -96,16 +96,16 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particle) { glm::vec3 penetration; Particle* penetratedParticle; if (_particles->findSpherePenetration(center, radius, penetration, (void**)&penetratedParticle)) { - + // let the particles run their collision scripts if they have them particle->collisionWithParticle(penetratedParticle); penetratedParticle->collisionWithParticle(particle); penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - + // apply a hard collision to both particles of half the penetration each - + float particleShare, penetratedParticleShare; if (particle->getInHand() && penetratedParticle->getInHand()) { particleShare = 0.5f; @@ -140,7 +140,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; const PalmData* collidingPalm = NULL; - + // first check the selfAvatar if set... if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; @@ -158,7 +158,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // apply a hard collision when ball collides with hand penetration /= static_cast(TREE_SCALE); updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - + // determine if the palm that collided was moving, if so, then we add that palm velocity as well... glm::vec3 addedVelocity = NO_ADDED_VELOCITY; if (collidingPalm) { @@ -185,7 +185,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. AvatarData* avatar = static_cast(node->getLinkedData()); //printf("updateCollisionWithAvatars()...avatar=%p\n", avatar); - + // check hands... const HandData* handData = avatar->getHandData(); @@ -215,15 +215,15 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } -void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, +void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping, const glm::vec3& addedVelocity) { // // Update the particle in response to a hard collision. Position will be reset exactly // to outside the colliding surface. Velocity will be modified according to elasticity. // // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) - // if elasticity = 1.0, collision is 100% elastic. - // + // if elasticity = 1.0, collision is 100% elastic. + // glm::vec3 position = particle->getPosition(); glm::vec3 velocity = particle->getVelocity(); @@ -249,15 +249,15 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm:: printf("ParticleCollisionSystem::applyHardCollision() particle id:%d new velocity:%f,%f,%f inHand:%s\n", particle->getID(), velocity.x, velocity.y, velocity.z, debug::valueOf(particle->getInHand())); } - + ParticleEditHandle particleEditHandle(_packetSender, _particles, particle->getID()); particleEditHandle.updateParticle(position, particle->getRadius(), particle->getXColor(), velocity, particle->getGravity(), particle->getDamping(), particle->getInHand(), particle->getScript()); } - + void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { - + // consider whether to have the collision make a sound const float AUDIBLE_COLLISION_THRESHOLD = 0.1f; const float COLLISION_LOUDNESS = 1.f; @@ -269,16 +269,16 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm // how do we want to handle this?? // glm::vec3 gravity = particle->getGravity() * static_cast(TREE_SCALE); - + if (glm::length(gravity) > EPSILON) { // If gravity is on, remove the effect of gravity on velocity for this - // frame, so that we are not constantly colliding with the surface + // frame, so that we are not constantly colliding with the surface velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); } */ float velocityTowardCollision = glm::dot(velocity, glm::normalize(penetration)); float velocityTangentToCollision = glm::length(velocity) - velocityTowardCollision; - + if (velocityTowardCollision > AUDIBLE_COLLISION_THRESHOLD) { // Volume is proportional to collision velocity // Base frequency is modified upward by the angle of the collision diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 64bcb93696..86b6e8884e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,11 +19,14 @@ #include #include #include +#include #include #include "ScriptEngine.h" +const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; + int ScriptEngine::_scriptNumber = 1; VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -31,7 +34,7 @@ ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) { QUrl soundURL = QUrl(context->argument(0).toString()); QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership); - + return soundScriptValue; } @@ -41,7 +44,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, _scriptContents = scriptContents; _isFinished = false; _isRunning = false; - + _isInitialized = false; + // some clients will use these menu features _wantMenuItems = wantMenuItems; if (scriptMenuName) { @@ -54,15 +58,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, } _menu = menu; _controllerScriptingInterface = controllerScriptingInterface; - - // hook up our interfaces - if (!Particle::getVoxelsScriptingInterface()) { - Particle::setVoxelsScriptingInterface(getVoxelsScriptingInterface()); - } - - if (!Particle::getParticlesScriptingInterface()) { - Particle::setParticlesScriptingInterface(getParticlesScriptingInterface()); - } } ScriptEngine::~ScriptEngine() { @@ -92,63 +87,89 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) { Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*) -void ScriptEngine::run() { - _isRunning = true; - QScriptEngine engine; - +void ScriptEngine::init() { + if (_isInitialized) { + return; // only initialize once + } + + _isInitialized = true; + _voxelsScriptingInterface.init(); _particlesScriptingInterface.init(); - - // register meta-type for glm::vec3 conversions - registerMetaTypes(&engine); - - QScriptValue agentValue = engine.newQObject(this); - engine.globalObject().setProperty("Agent", agentValue); - - QScriptValue voxelScripterValue = engine.newQObject(&_voxelsScriptingInterface); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - QScriptValue particleScripterValue = engine.newQObject(&_particlesScriptingInterface); - engine.globalObject().setProperty("Particles", particleScripterValue); - - - QScriptValue soundConstructorValue = engine.newFunction(soundConstructor); - QScriptValue soundMetaObject = engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); - engine.globalObject().setProperty("Sound", soundMetaObject); - - QScriptValue injectionOptionValue = engine.scriptValueFromQMetaObject(); - engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - - QScriptValue audioScriptingInterfaceValue = engine.newQObject(&_audioScriptingInterface); - engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); - + // register meta-type for glm::vec3 conversions + registerMetaTypes(&_engine); + + QScriptValue agentValue = _engine.newQObject(this); + _engine.globalObject().setProperty("Agent", agentValue); + + QScriptValue voxelScripterValue = _engine.newQObject(&_voxelsScriptingInterface); + _engine.globalObject().setProperty("Voxels", voxelScripterValue); + + QScriptValue particleScripterValue = _engine.newQObject(&_particlesScriptingInterface); + _engine.globalObject().setProperty("Particles", particleScripterValue); + + QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor); + QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); + _engine.globalObject().setProperty("Sound", soundMetaObject); + + QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); + _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); + + QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface); + _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); + if (_controllerScriptingInterface) { - QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface); - engine.globalObject().setProperty("Controller", controllerScripterValue); + QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface); + _engine.globalObject().setProperty("Controller", controllerScripterValue); } - - 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; - + + QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE)); + _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + // let the VoxelPacketSender know how frequently we plan to call it _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); //qDebug() << "Script:\n" << _scriptContents << "\n"; - - QScriptValue result = engine.evaluate(_scriptContents); +} + +void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { + QScriptValue value = _engine.newQObject(object); + _engine.globalObject().setProperty(name, value); +} + +void ScriptEngine::evaluate() { + if (!_isInitialized) { + init(); + } + + QScriptValue result = _engine.evaluate(_scriptContents); qDebug() << "Evaluated script.\n"; - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); + + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; } - +} + +void ScriptEngine::run() { + if (!_isInitialized) { + init(); + } + _isRunning = true; + + QScriptValue result = _engine.evaluate(_scriptContents); + qDebug() << "Evaluated script.\n"; + + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + } + timeval startTime; gettimeofday(&startTime, NULL); - + int thisFrame = 0; while (!_isFinished) { @@ -166,15 +187,15 @@ void ScriptEngine::run() { if (_isFinished) { break; } - + bool willSendVisualDataCallBack = false; - if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { + if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { // allow the scripter's call back to setup visual data willSendVisualDataCallBack = true; - + // release the queue of edit voxel messages. _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) { _voxelsScriptingInterface.getVoxelPacketSender()->process(); @@ -184,23 +205,23 @@ void ScriptEngine::run() { if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) { // allow the scripter's call back to setup visual data willSendVisualDataCallBack = true; - + // release the queue of edit voxel messages. _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); - + // since we're in non-threaded mode, call process so that the packets are sent if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) { _particlesScriptingInterface.getParticlePacketSender()->process(); } } - + if (willSendVisualDataCallBack) { emit willSendVisualDataCallback(); } - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; + if (_engine.hasUncaughtException()) { + int line = _engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << _engine.uncaughtException().toString() << "\n"; } } cleanMenuItems(); @@ -213,9 +234,8 @@ void ScriptEngine::run() { emit finished(); _isRunning = false; } - -void ScriptEngine::stop() { - _isFinished = true; +void ScriptEngine::stop() { + _isFinished = true; } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 49ed913744..842b902fcf 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -17,9 +17,10 @@ #include #include -#include #include +class ParticlesScriptingInterface; + #include "AbstractControllerScriptingInterface.h" const QString NO_SCRIPT(""); @@ -27,12 +28,12 @@ const QString NO_SCRIPT(""); class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, + ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL, AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ~ScriptEngine(); - + /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } @@ -44,11 +45,15 @@ public: void setupMenuItems(); void cleanMenuItems(); - + + void registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name + public slots: - void run(); + void init(); + void run(); /// runs continuously until Agent.stop() is called void stop(); - + void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller + signals: void willSendAudioDataCallback(); void willSendVisualDataCallback(); @@ -57,7 +62,8 @@ protected: QString _scriptContents; bool _isFinished; bool _isRunning; - + bool _isInitialized; + QScriptEngine _engine; private: static VoxelsScriptingInterface _voxelsScriptingInterface;