// // Application.cpp // interface // // Created by Andrzej Kapolka on 5/10/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. #include #include #ifdef _WIN32 #include "Syssocket.h" #include "Systime.h" #else #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Application.h" #include "InterfaceConfig.h" #include "Log.h" #include "OculusManager.h" #include "Util.h" #include "renderer/ProgramObject.h" #include "ui/TextRenderer.h" using namespace std; //const CameraMode DEFAULT_CAMERA_MODE = CAMERA_MODE_FIRST_PERSON; const CameraMode DEFAULT_CAMERA_MODE = CAMERA_MODE_THIRD_PERSON; // Starfield information static char STAR_FILE[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt"; static char STAR_CACHE_FILE[] = "cachedStars.txt"; const glm::vec3 START_LOCATION(6.1f, 0, 1.4f); // Where one's own agent begins in the world // (will be overwritten if avatar data file is found) const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) const bool USING_MOUSE_VIEW_SHIFT = false; const float MOUSE_VIEW_SHIFT_RATE = 40.0f; const float MOUSE_VIEW_SHIFT_YAW_MARGIN = (float)(1200 * 0.2f); const float MOUSE_VIEW_SHIFT_PITCH_MARGIN = (float)(800 * 0.2f); const float MOUSE_VIEW_SHIFT_YAW_LIMIT = 45.0; const float MOUSE_VIEW_SHIFT_PITCH_LIMIT = 30.0; const bool DISPLAY_HEAD_MOUSE = true; // customized canvas that simply forwards requests/events to the singleton application class GLCanvas : public QGLWidget { protected: virtual void initializeGL(); virtual void paintGL(); virtual void resizeGL(int width, int height); virtual void keyPressEvent(QKeyEvent* event); virtual void keyReleaseEvent(QKeyEvent* event); virtual void mouseMoveEvent(QMouseEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); }; void GLCanvas::initializeGL() { static_cast(QCoreApplication::instance())->initializeGL(); } void GLCanvas::paintGL() { static_cast(QCoreApplication::instance())->paintGL(); } void GLCanvas::resizeGL(int width, int height) { static_cast(QCoreApplication::instance())->resizeGL(width, height); } void GLCanvas::keyPressEvent(QKeyEvent* event) { static_cast(QCoreApplication::instance())->keyPressEvent(event); } void GLCanvas::keyReleaseEvent(QKeyEvent* event) { static_cast(QCoreApplication::instance())->keyReleaseEvent(event); } void GLCanvas::mouseMoveEvent(QMouseEvent* event) { static_cast(QCoreApplication::instance())->mouseMoveEvent(event); } void GLCanvas::mousePressEvent(QMouseEvent* event) { static_cast(QCoreApplication::instance())->mousePressEvent(event); } void GLCanvas::mouseReleaseEvent(QMouseEvent* event) { static_cast(QCoreApplication::instance())->mouseReleaseEvent(event); } Application::Application(int& argc, char** argv) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), _displayLevels(false), _frameCount(0), _fps(120.0f), _justStarted(true), _wantToKillLocalVoxels(false), _frustumDrawingMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffsetYaw(-135.0), _viewFrustumOffsetPitch(0.0), _viewFrustumOffsetRoll(0.0), _viewFrustumOffsetDistance(25.0), _viewFrustumOffsetUp(0.0), _mouseViewShiftYaw(0.0f), _mouseViewShiftPitch(0.0f), _audioScope(256, 200, true), _myAvatar(true), _mouseX(0), _mouseY(0), _mousePressed(false), _mouseMode(NO_EDIT_MODE), _mouseVoxelScale(1.0f / 1024.0f), _paintOn(false), _dominantColor(0), _perfStatsOn(false), _chatEntryOn(false), _oculusTextureID(0), _oculusProgram(0), _oculusDistortionScale(1.25), #ifndef _WIN32 _audio(&_audioScope, &_myAvatar), #endif _stopNetworkReceiveThread(false), _packetCount(0), _packetsPerSecond(0), _bytesPerSecond(0), _bytesCount(0) { gettimeofday(&_applicationStartupTime, NULL); printLog("Interface Startup:\n"); _voxels.setViewFrustum(&_viewFrustum); shared_lib::printLog = & ::printLog; voxels_lib::printLog = & ::printLog; avatars_lib::printLog = & ::printLog; unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT; const char** constArgv = const_cast(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); if (portStr) { listenPort = atoi(portStr); } AgentList::createInstance(AGENT_TYPE_AVATAR, listenPort); _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); if (!_enableNetworkThread) { AgentList::getInstance()->getAgentSocket().setBlocking(false); } const char* domainIP = getCmdOption(argc, constArgv, "--domain"); if (domainIP) { strcpy(DOMAIN_IP, domainIP); } // Handle Local Domain testing with the --local command line if (cmdOptionExists(argc, constArgv, "--local")) { printLog("Local Domain MODE!\n"); int ip = getLocalAddress(); sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF)); } // 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 AgentList is attachNewHeadToAgent AgentList::getInstance()->linkedDataCreateCallback = &attachNewHeadToAgent; #ifndef _WIN32 AgentList::getInstance()->audioMixerSocketUpdate = &audioMixerUpdate; #endif #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); #endif // start the agentList threads AgentList::getInstance()->startSilentAgentRemovalThread(); AgentList::getInstance()->startDomainServerCheckInThread(); AgentList::getInstance()->startPingUnknownAgentsThread(); _window->setCentralWidget(_glWidget); initMenu(); QRect available = desktop()->availableGeometry(); _window->resize(available.size()); _window->setVisible(true); _glWidget->setFocus(); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); // initialization continues in initializeGL when OpenGL context is ready } void Application::initializeGL() { printLog( "Created Display Window.\n" ); int argc = 0; glutInit(&argc, 0); #ifdef _WIN32 glewInit(); printLog( "Glew Init complete.\n" ); #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(); printLog( "Initialized Display.\n" ); init(); printLog( "Init() complete.\n" ); // Check to see if the user passed in a command line option for randomizing colors bool wantColorRandomizer = !arguments().contains("--NoColorRandomizer"); // Check to see if the user passed in a command line option for loading a local // Voxel File. If so, load it now. if (!_voxelsFilename.isEmpty()) { _voxels.loadVoxelsFile(_voxelsFilename.constData(), wantColorRandomizer); printLog("Local Voxel File loaded.\n"); } // create thread for receipt of data via UDP if (_enableNetworkThread) { pthread_create(&_networkReceiveThread, NULL, networkReceive, NULL); printLog("Network receive thread created.\n"); } _myAvatar.readAvatarDataFromFile(); // 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 QTimer* idleTimer = new QTimer(this); connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); idleTimer->start(0); } void Application::paintGL() { PerfStat("display"); glEnable(GL_LINE_SMOOTH); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glPushMatrix(); { glLoadIdentity(); // camera settings if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myAvatar.setDisplayingHead(true); _myCamera.setUpShift (0.0); _myCamera.setDistance (0.2); _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getBodyYaw() - 180.0f, 0.0f, 0.0f); } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || OculusManager::isConnected()) { _myAvatar.setDisplayingHead(false); _myCamera.setUpShift (0.0f); _myCamera.setDistance (0.0f); _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getHeadPosition()); if (OculusManager::isConnected()) { _myCamera.setTargetRotation(_myAvatar.getBodyYaw() + _myAvatar.getHeadYaw(), -_myAvatar.getHeadPitch(), _myAvatar.getHeadRoll()); } else { _myCamera.setTargetRotation(_myAvatar.getAbsoluteHeadYaw()- _mouseViewShiftYaw, _myAvatar.getAbsoluteHeadPitch() + _myAvatar.getRenderPitch() + _mouseViewShiftPitch, 0.0f); } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myAvatar.setDisplayingHead(true); _myCamera.setUpShift (-0.2f); _myCamera.setDistance (1.5f); _myCamera.setTightness (8.0f); _myCamera.setTargetPosition(_myAvatar.getHeadPosition()); _myCamera.setTargetRotation(_myAvatar.getBodyYaw() - _mouseViewShiftYaw, _mouseViewShiftPitch, 0.0f); } // important... _myCamera.update( 1.f/_fps ); // Render anything (like HUD items) that we want to be in 3D but not in worldspace /* const float HUD_Z_OFFSET = -5.f; glPushMatrix(); glm::vec3 test(0.5, 0.5, 0.5); glTranslatef(1, 1, HUD_Z_OFFSET); drawVector(&test); glPopMatrix(); */ // 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. // // Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum() // code, we want to keep the state of "myCamera" intact, so we can render what the view frustum of // myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera Camera whichCamera = _myCamera; if (_viewFrustumFromOffset->isChecked() && _frustumOn->isChecked()) { // set the camera to third-person view but offset so we can see the frustum _viewFrustumOffsetCamera.setTargetYaw(_viewFrustumOffsetYaw + _myAvatar.getBodyYaw()); _viewFrustumOffsetCamera.setPitch (_viewFrustumOffsetPitch ); _viewFrustumOffsetCamera.setRoll (_viewFrustumOffsetRoll ); _viewFrustumOffsetCamera.setUpShift (_viewFrustumOffsetUp ); _viewFrustumOffsetCamera.setDistance (_viewFrustumOffsetDistance); _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; } // transform view according to whichCamera // could be myCamera (if in normal mode) // or could be viewFrustumOffsetCamera if in offset mode // I changed the ordering here - roll is FIRST (JJV) glRotatef ( whichCamera.getRoll(), IDENTITY_FRONT.x, IDENTITY_FRONT.y, IDENTITY_FRONT.z); glRotatef ( whichCamera.getPitch(), IDENTITY_RIGHT.x, IDENTITY_RIGHT.y, IDENTITY_RIGHT.z); glRotatef (180.0 - whichCamera.getYaw(), IDENTITY_UP.x, IDENTITY_UP.y, IDENTITY_UP.z ); glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z); // 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); GLfloat light_position0[] = { 1.0, 1.0, 0.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position0); GLfloat ambient_color[] = { 0.7, 0.7, 0.8 }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 }; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color); glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color); glMateriali(GL_FRONT, GL_SHININESS, 96); if (_oculusOn->isChecked()) { displayOculus(whichCamera); } else { displaySide(whichCamera); glPopMatrix(); displayOverlay(); } } _frameCount++; // If application has just started, report time from startup to now (first frame display) if (_justStarted) { float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime))/1000000.0; _justStarted = false; char title[30]; snprintf(title, 30, "Interface: %4.2f seconds", startupTime); _window->setWindowTitle(title); } } void Application::resizeGL(int width, int height) { float aspectRatio = ((float)width/(float)height); // based on screen resize // get the lens details from the current camera Camera& camera = _viewFrustumFromOffset->isChecked() ? _viewFrustumOffsetCamera : _myCamera; float nearClip = camera.getNearClip(); float farClip = camera.getFarClip(); float fov; if (_oculusOn->isChecked()) { // more magic numbers; see Oculus SDK docs, p. 32 camera.setAspectRatio(aspectRatio *= 0.5); camera.setFieldOfView(fov = 2 * atan((0.0468 * _oculusDistortionScale) / 0.041) * (180 / PI)); // resize the render texture if (_oculusTextureID != 0) { glBindTexture(GL_TEXTURE_2D, _oculusTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, 0); } } else { camera.setAspectRatio(aspectRatio); camera.setFieldOfView(fov = 60); } // Tell our viewFrustum about this change _viewFrustum.setAspectRatio(aspectRatio); glViewport(0, 0, width, height); // shouldn't this account for the menu??? glMatrixMode(GL_PROJECTION); glLoadIdentity(); // XXXBHG - If we're in view frustum mode, then we need to do this little bit of hackery so that // OpenGL won't clip our frustum rendering lines. This is a debug hack for sure! Basically, this makes // the near clip a little bit closer (therefor you see more) and the far clip a little bit farther (also, // to see more.) if (_frustumOn->isChecked()) { nearClip -= 0.01f; farClip += 0.01f; } // On window reshape, we need to tell OpenGL about our new setting gluPerspective(fov,aspectRatio,nearClip,farClip); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } static void sendVoxelServerEraseAll() { char message[100]; sprintf(message,"%c%s",'Z',"erase all"); int messageSize = strlen(message) + 1; AgentList::getInstance()->broadcastToAgents((unsigned char*) message, messageSize, &AGENT_TYPE_VOXEL, 1); } static void sendVoxelServerAddScene() { char message[100]; sprintf(message,"%c%s",'Z',"add scene"); int messageSize = strlen(message) + 1; AgentList::getInstance()->broadcastToAgents((unsigned char*)message, messageSize, &AGENT_TYPE_VOXEL, 1); } void Application::keyPressEvent(QKeyEvent* event) { if (_chatEntryOn) { if (_chatEntry.keyPressEvent(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(); _chatEntryOn = false; setMenuShortcutsEnabled(true); } return; } qDebug() << QKeySequence(event->key()); bool shifted = event->modifiers().testFlag(Qt::ShiftModifier); switch (event->key()) { case Qt::Key_BracketLeft: _viewFrustumOffsetYaw -= 0.5; break; case Qt::Key_BracketRight: _viewFrustumOffsetYaw += 0.5; break; case Qt::Key_BraceLeft: _viewFrustumOffsetPitch -= 0.5; break; case Qt::Key_BraceRight: _viewFrustumOffsetPitch += 0.5; break; case Qt::Key_ParenLeft: _viewFrustumOffsetRoll -= 0.5; break; case Qt::Key_ParenRight: _viewFrustumOffsetRoll += 0.5; break; case Qt::Key_Less: _viewFrustumOffsetDistance -= 0.5; break; case Qt::Key_Greater: _viewFrustumOffsetDistance += 0.5; break; case Qt::Key_Comma: _viewFrustumOffsetUp -= 0.05; break; case Qt::Key_Period: _viewFrustumOffsetUp += 0.05; break; case Qt::Key_Ampersand: _paintOn = !_paintOn; setupPaintingVoxel(); break; case Qt::Key_AsciiCircum: shiftPaintingColor(); break; case Qt::Key_Minus: sendVoxelServerEraseAll(); break; case Qt::Key_Percent: sendVoxelServerAddScene(); break; case Qt::Key_1: _mouseMode = (_mouseMode == ADD_VOXEL_MODE) ? NO_EDIT_MODE : ADD_VOXEL_MODE; break; case Qt::Key_2: _mouseMode = (_mouseMode == DELETE_VOXEL_MODE) ? NO_EDIT_MODE : DELETE_VOXEL_MODE; break; case Qt::Key_3: _mouseMode = (_mouseMode == COLOR_VOXEL_MODE) ? NO_EDIT_MODE : COLOR_VOXEL_MODE; break; case Qt::Key_4: addVoxelInFrontOfAvatar(); break; case Qt::Key_5: _mouseVoxelScale /= 2; break; case Qt::Key_6: _mouseVoxelScale *= 2; break; case Qt::Key_L: _displayLevels = !_displayLevels; break; case Qt::Key_E: _myAvatar.setDriveKeys(UP, 1); break; case Qt::Key_C: _myAvatar.setDriveKeys(DOWN, 1); break; case Qt::Key_W: _myAvatar.setDriveKeys(FWD, 1); break; case Qt::Key_S: _myAvatar.setDriveKeys(BACK, 1); break; case Qt::Key_Space: resetSensors(); break; case Qt::Key_A: _myAvatar.setDriveKeys(ROT_LEFT, 1); break; case Qt::Key_D: _myAvatar.setDriveKeys(ROT_RIGHT, 1); break; case Qt::Key_Return: case Qt::Key_Enter: _chatEntryOn = true; _myAvatar.setKeyState(NO_KEY_DOWN); _myAvatar.setChatMessage(string()); setMenuShortcutsEnabled(false); break; case Qt::Key_Up: _myAvatar.setDriveKeys(shifted ? UP : FWD, 1); break; case Qt::Key_Down: _myAvatar.setDriveKeys(shifted ? DOWN : BACK, 1); break; case Qt::Key_Left: _myAvatar.setDriveKeys(shifted ? LEFT : ROT_LEFT, 1); break; case Qt::Key_Right: _myAvatar.setDriveKeys(shifted ? RIGHT : ROT_RIGHT, 1); break; default: event->ignore(); break; } } void Application::keyReleaseEvent(QKeyEvent* event) { if (_chatEntryOn) { _myAvatar.setKeyState(NO_KEY_DOWN); return; } switch (event->key()) { 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; } } void Application::mouseMoveEvent(QMouseEvent* event) { _mouseX = event->x(); _mouseY = event->y(); } void Application::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); _mouseY = event->y(); _mousePressed = true; if (_mouseMode == ADD_VOXEL_MODE || _mouseMode == COLOR_VOXEL_MODE) { addVoxelUnderCursor(); } else if (_mouseMode == DELETE_VOXEL_MODE) { deleteVoxelUnderCursor(); } } else if (event->button() == Qt::RightButton && _mouseMode != NO_EDIT_MODE) { deleteVoxelUnderCursor(); } } void Application::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); _mouseY = event->y(); _mousePressed = false; } } // Every second, check the frame rates and other stuff void Application::timer() { gettimeofday(&_timerEnd, NULL); _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 (!_serialPort.active) { _serialPort.pair(); } } 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); } } //Find and return the gravity vector at this location static glm::vec3 getGravity(const glm::vec3& pos) { // // For now, we'll test this with a simple global lookup, but soon we will add getting this // from the domain/voxelserver (or something similar) // if ((pos.x > 0.f) && (pos.x < 10.f) && (pos.z > 0.f) && (pos.z < 10.f) && (pos.y > 0.f) && (pos.y < 3.f)) { // If above ground plane, turn gravity on return glm::vec3(0.f, -1.f, 0.f); } else { // If flying in space, turn gravity OFF return glm::vec3(0.f, 0.f, 0.f); } } void Application::idle() { timeval check; gettimeofday(&check, NULL); // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time if (diffclock(&_lastTimeIdle, &check) > IDLE_SIMULATE_MSECS) { float deltaTime = 1.f/_fps; // update behaviors for avatar hand movement: handControl takes mouse values as input, // and gives back 3D values modulated for smooth transitioning between interaction modes. _handControl.update(_mouseX, _mouseY); _myAvatar.setHandMovementValues(_handControl.getValues()); // tell my avatar if the mouse is being pressed... _myAvatar.setMousePressed(_mousePressed); // check what's under the mouse and update the mouse voxel glm::vec3 origin, direction; _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), origin, direction); float distance; BoxFace face; _mouseVoxel.s = 0.0f; if (_mouseMode != NO_EDIT_MODE && _voxels.findRayIntersection(origin, direction, _mouseVoxel, distance, face)) { // find the nearest voxel with the desired scale if (_mouseVoxelScale > _mouseVoxel.s) { // choose the larger voxel that encompasses the one selected _mouseVoxel.x = _mouseVoxelScale * floorf(_mouseVoxel.x / _mouseVoxelScale); _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) { // find the closest contained voxel glm::vec3 pt = (origin + direction * distance) / (float)TREE_SCALE - faceVector * (_mouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / _mouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; } if (_mouseMode == ADD_VOXEL_MODE) { // use the face to determine the side on which to create a neighbor _mouseVoxel.x += faceVector.x * _mouseVoxel.s; _mouseVoxel.y += faceVector.y * _mouseVoxel.s; _mouseVoxel.z += faceVector.z * _mouseVoxel.s; } } if (_mouseMode == COLOR_VOXEL_MODE) { _mouseVoxel.red = 0; _mouseVoxel.green = 255; _mouseVoxel.blue = 0; } else if (_mouseMode == DELETE_VOXEL_MODE) { // red indicates deletion _mouseVoxel.red = 255; _mouseVoxel.green = _mouseVoxel.blue = 0; } } // walking triggers the handControl to stop if (_myAvatar.getMode() == AVATAR_MODE_WALKING) { _handControl.stop(); _mouseViewShiftYaw *= 0.9; _mouseViewShiftPitch *= 0.9; } // Read serial port interface devices if (_serialPort.active) { _serialPort.readData(); } // Sample hardware, update view frustum if needed, and send avatar data to mixer/agents updateAvatar(deltaTime); // read incoming packets from network if (!_enableNetworkThread) { networkReceive(0); } //loop through all the remote avatars and simulate them... AgentList* agentList = AgentList::getInstance(); agentList->lock(); for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { if (agent->getLinkedData() != NULL) { Avatar *avatar = (Avatar *)agent->getLinkedData(); avatar->simulate(deltaTime); } } agentList->unlock(); _myAvatar.setGravity(getGravity(_myAvatar.getPosition())); _myAvatar.simulate(deltaTime); // Update audio stats for procedural sounds #ifndef _WIN32 _audio.setLastAcceleration(_myAvatar.getThrust()); _audio.setLastVelocity(_myAvatar.getVelocity()); #endif _glWidget->updateGL(); _lastTimeIdle = check; } } void Application::terminate() { // Close serial port // close(serial_fd); _myAvatar.writeAvatarDataToFile(); #ifndef _WIN32 _audio.terminate(); #endif if (_enableNetworkThread) { _stopNetworkReceiveThread = true; pthread_join(_networkReceiveThread, NULL); } } void Application::pair() { PairingHandler::sendPairRequest(); } void Application::setHead(bool head) { #ifndef _WIN32 _audio.setMixerLoopbackFlag(head); _myCamera.setMode(head ? CAMERA_MODE_MIRROR : DEFAULT_CAMERA_MODE); #endif } void Application::setNoise(bool noise) { _myAvatar.setNoise(noise); } void Application::setFullscreen(bool fullscreen) { _window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) : (_window->windowState() & ~Qt::WindowFullScreen)); } void Application::setRenderFirstPerson(bool firstPerson) { _myCamera.setMode(firstPerson ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); } void Application::setOculus(bool oculus) { resizeGL(_glWidget->width(), _glWidget->height()); } void Application::setFrustumOffset(bool frustumOffset) { // reshape so that OpenGL will get the right lens details for the camera of choice resizeGL(_glWidget->width(), _glWidget->height()); } void Application::cycleFrustumRenderMode() { _frustumDrawingMode = (FrustumDrawMode)((_frustumDrawingMode + 1) % FRUSTUM_DRAW_MODE_COUNT); updateFrustumRenderModeAction(); } void Application::setRenderWarnings(bool renderWarnings) { _voxels.setRenderPipelineWarnings(renderWarnings); } void Application::doKillLocalVoxels() { _wantToKillLocalVoxels = true; } void Application::doRandomizeVoxelColors() { _voxels.randomizeVoxelColors(); } void Application::doFalseRandomizeVoxelColors() { _voxels.falseColorizeRandom(); } void Application::doFalseRandomizeEveryOtherVoxelColors() { _voxels.falseColorizeRandomEveryOther(); } void Application::doFalseColorizeByDistance() { loadViewFrustum(_viewFrustum); _voxels.falseColorizeDistanceFromView(&_viewFrustum); } void Application::doFalseColorizeInView() { loadViewFrustum(_viewFrustum); // we probably want to make sure the viewFrustum is initialized first _voxels.falseColorizeInView(&_viewFrustum); } void Application::doTrueVoxelColors() { _voxels.trueColorize(); } void Application::doTreeStats() { _voxels.collectStatsForTreesAndVBOs(); } void Application::setWantsMonochrome(bool wantsMonochrome) { _myAvatar.setWantColor(!wantsMonochrome); } void Application::setWantsResIn(bool wantsResIn) { _myAvatar.setWantResIn(wantsResIn); } void Application::initMenu() { QMenuBar* menuBar = new QMenuBar(); _window->setMenuBar(menuBar); QMenu* fileMenu = menuBar->addMenu("File"); fileMenu->addAction("Pair", this, SLOT(pair())); fileMenu->addAction("Quit", this, SLOT(quit()), Qt::Key_Q); QMenu* optionsMenu = menuBar->addMenu("Options"); (_lookingInMirror = optionsMenu->addAction("Mirror", this, SLOT(setHead(bool)), Qt::Key_H))->setCheckable(true); optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true); (_gyroLook = optionsMenu->addAction("Gyro Look"))->setCheckable(true); _gyroLook->setChecked(true); optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F)->setCheckable(true); QMenu* renderMenu = menuBar->addMenu("Render"); (_renderVoxels = renderMenu->addAction("Voxels"))->setCheckable(true); _renderVoxels->setChecked(true); _renderVoxels->setShortcut(Qt::Key_V); (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); _renderStarsOn->setChecked(true); _renderStarsOn->setShortcut(Qt::Key_Asterisk); (_renderAtmosphereOn = renderMenu->addAction("Atmosphere"))->setCheckable(true); _renderAtmosphereOn->setChecked(true); _renderAtmosphereOn->setShortcut(Qt::SHIFT | Qt::Key_A); (_renderAvatarsOn = renderMenu->addAction("Avatars"))->setCheckable(true); _renderAvatarsOn->setChecked(true); renderMenu->addAction("First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P)->setCheckable(true); (_oculusOn = renderMenu->addAction("Oculus", this, SLOT(setOculus(bool)), Qt::Key_O))->setCheckable(true); QMenu* toolsMenu = menuBar->addMenu("Tools"); (_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true); _renderStatsOn->setShortcut(Qt::Key_Slash); (_logOn = toolsMenu->addAction("Log"))->setCheckable(true); _logOn->setChecked(true); QMenu* frustumMenu = menuBar->addMenu("Frustum"); (_frustumOn = frustumMenu->addAction("Display Frustum"))->setCheckable(true); _frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F); (_viewFrustumFromOffset = frustumMenu->addAction( "Use Offset Camera", this, SLOT(setFrustumOffset(bool)), Qt::SHIFT | Qt::Key_O))->setCheckable(true); (_cameraFrustum = frustumMenu->addAction("Switch Camera"))->setCheckable(true); _cameraFrustum->setChecked(true); _cameraFrustum->setShortcut(Qt::SHIFT | Qt::Key_C); _frustumRenderModeAction = frustumMenu->addAction( "Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R); updateFrustumRenderModeAction(); QMenu* debugMenu = menuBar->addMenu("Debug"); debugMenu->addAction("Show Render Pipeline Warnings", this, SLOT(setRenderWarnings(bool)))->setCheckable(true); debugMenu->addAction("Kill Local Voxels", this, SLOT(doKillLocalVoxels())); debugMenu->addAction("Randomize Voxel TRUE Colors", this, SLOT(doRandomizeVoxelColors())); debugMenu->addAction("FALSE Color Voxels Randomly", this, SLOT(doFalseRandomizeVoxelColors())); debugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors())); debugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance())); debugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView())); debugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors())); debugMenu->addAction("Calculate Tree Stats", this, SLOT(doTreeStats()), Qt::SHIFT | Qt::Key_S); debugMenu->addAction("Wants Res-In", this, SLOT(setWantsResIn(bool)))->setCheckable(true); debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); } void Application::updateFrustumRenderModeAction() { switch (_frustumDrawingMode) { case FRUSTUM_DRAW_MODE_ALL: _frustumRenderModeAction->setText("Render Mode - All"); break; case FRUSTUM_DRAW_MODE_VECTORS: _frustumRenderModeAction->setText("Render Mode - Vectors"); break; case FRUSTUM_DRAW_MODE_PLANES: _frustumRenderModeAction->setText("Render Mode - Planes"); break; case FRUSTUM_DRAW_MODE_NEAR_PLANE: _frustumRenderModeAction->setText("Render Mode - Near"); break; case FRUSTUM_DRAW_MODE_FAR_PLANE: _frustumRenderModeAction->setText("Render Mode - Far"); break; } } void Application::initDisplay() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glShadeModel (GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); } void Application::init() { _voxels.init(); _voxels.setViewerAvatar(&_myAvatar); _voxels.setCamera(&_myCamera); _environment.init(); _handControl.setScreenDimensions(_glWidget->width(), _glWidget->height()); _headMouseX = _glWidget->width()/2; _headMouseY = _glWidget->height()/2; _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); _myAvatar.setPosition(START_LOCATION); _myCamera.setMode(DEFAULT_CAMERA_MODE); OculusManager::connect(); gettimeofday(&_timerStart, NULL); gettimeofday(&_lastTimeIdle, NULL); } static void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) { unsigned char* bufferOut; int sizeOut; if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){ AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1); delete bufferOut; } } void Application::updateAvatar(float deltaTime) { // Update my avatar's head position from gyros _myAvatar.updateHeadFromGyros(deltaTime, &_serialPort, &_gravity); // Grab latest readings from the gyros float measuredPitchRate = _serialPort.getLastPitchRate(); float measuredYawRate = _serialPort.getLastYawRate(); // Update gyro-based mouse (X,Y on screen) const float MIN_MOUSE_RATE = 1.0; const float HORIZONTAL_PIXELS_PER_DEGREE = 2880.f / 45.f; const float VERTICAL_PIXELS_PER_DEGREE = 1800.f / 30.f; if (powf(measuredYawRate * measuredYawRate + measuredPitchRate * measuredPitchRate, 0.5) > MIN_MOUSE_RATE) { _headMouseX += measuredYawRate * HORIZONTAL_PIXELS_PER_DEGREE * deltaTime; _headMouseY -= measuredPitchRate * VERTICAL_PIXELS_PER_DEGREE * deltaTime; } _headMouseX = max(_headMouseX, 0); _headMouseX = min(_headMouseX, _glWidget->width()); _headMouseY = max(_headMouseY, 0); _headMouseY = min(_headMouseY, _glWidget->height()); // Update head and body pitch and yaw based on measured gyro rates if (_gyroLook->isChecked()) { // Render Yaw float renderYawSpring = fabs(_headMouseX - _glWidget->width() / 2.f) / (_glWidget->width() / 2.f); const float RENDER_YAW_MULTIPLY = 4.f; _myAvatar.setRenderYaw((1.f - renderYawSpring * deltaTime) * _myAvatar.getRenderYaw() + renderYawSpring * deltaTime * -_myAvatar.getHeadYaw() * RENDER_YAW_MULTIPLY); // Render Pitch float renderPitchSpring = fabs(_headMouseY - _glWidget->height() / 2.f) / (_glWidget->height() / 2.f); const float RENDER_PITCH_MULTIPLY = 4.f; _myAvatar.setRenderPitch((1.f - renderPitchSpring * deltaTime) * _myAvatar.getRenderPitch() + renderPitchSpring * deltaTime * -_myAvatar.getHeadPitch() * RENDER_PITCH_MULTIPLY); } if (USING_MOUSE_VIEW_SHIFT) { //make it so that when your mouse hits the edge of the screen, the camera shifts float rightBoundary = (float)_glWidget->width() - MOUSE_VIEW_SHIFT_YAW_MARGIN; float bottomBoundary = (float)_glWidget->height() - MOUSE_VIEW_SHIFT_PITCH_MARGIN; if (_mouseX > rightBoundary) { float f = (_mouseX - rightBoundary) / ( (float)_glWidget->width() - rightBoundary); _mouseViewShiftYaw += MOUSE_VIEW_SHIFT_RATE * f * deltaTime; if (_mouseViewShiftYaw > MOUSE_VIEW_SHIFT_YAW_LIMIT) { _mouseViewShiftYaw = MOUSE_VIEW_SHIFT_YAW_LIMIT; } } else if (_mouseX < MOUSE_VIEW_SHIFT_YAW_MARGIN) { float f = 1.0 - (_mouseX / MOUSE_VIEW_SHIFT_YAW_MARGIN); _mouseViewShiftYaw -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime; if (_mouseViewShiftYaw < -MOUSE_VIEW_SHIFT_YAW_LIMIT) { _mouseViewShiftYaw = -MOUSE_VIEW_SHIFT_YAW_LIMIT; } } if (_mouseY < MOUSE_VIEW_SHIFT_PITCH_MARGIN) { float f = 1.0 - (_mouseY / MOUSE_VIEW_SHIFT_PITCH_MARGIN); _mouseViewShiftPitch += MOUSE_VIEW_SHIFT_RATE * f * deltaTime; if (_mouseViewShiftPitch > MOUSE_VIEW_SHIFT_PITCH_LIMIT ) { _mouseViewShiftPitch = MOUSE_VIEW_SHIFT_PITCH_LIMIT; } } else if (_mouseY > bottomBoundary) { float f = (_mouseY - bottomBoundary) / ((float)_glWidget->height() - bottomBoundary); _mouseViewShiftPitch -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime; if (_mouseViewShiftPitch < -MOUSE_VIEW_SHIFT_PITCH_LIMIT) { _mouseViewShiftPitch = -MOUSE_VIEW_SHIFT_PITCH_LIMIT; } } } if (OculusManager::isConnected()) { float yaw, pitch, roll; OculusManager::getEulerAngles(yaw, pitch, roll); _myAvatar.setHeadYaw(-yaw); _myAvatar.setHeadPitch(pitch); _myAvatar.setHeadRoll(roll); } // Get audio loudness data from audio input device #ifndef _WIN32 _myAvatar.setLoudness(_audio.getInputLoudness()); #endif // Update Avatar with latest camera and view frustum data... // 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 // to the server. loadViewFrustum(_viewFrustum); _myAvatar.setCameraPosition(_viewFrustum.getPosition()); _myAvatar.setCameraDirection(_viewFrustum.getDirection()); _myAvatar.setCameraUp(_viewFrustum.getUp()); _myAvatar.setCameraRight(_viewFrustum.getRight()); _myAvatar.setCameraFov(_viewFrustum.getFieldOfView()); _myAvatar.setCameraAspectRatio(_viewFrustum.getAspectRatio()); _myAvatar.setCameraNearClip(_viewFrustum.getNearClip()); _myAvatar.setCameraFarClip(_viewFrustum.getFarClip()); AgentList* agentList = AgentList::getInstance(); if (agentList->getOwnerID() != UNKNOWN_AGENT_ID) { // if I know my ID, send head/hand data to the avatar mixer and voxel server unsigned char broadcastString[200]; unsigned char* endOfBroadcastStringWrite = broadcastString; *(endOfBroadcastStringWrite++) = PACKET_HEADER_HEAD_DATA; endOfBroadcastStringWrite += packAgentId(endOfBroadcastStringWrite, agentList->getOwnerID()); endOfBroadcastStringWrite += _myAvatar.getBroadcastData(endOfBroadcastStringWrite); const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL, AGENT_TYPE_AVATAR_MIXER}; AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers)); } // If I'm in paint mode, send a voxel out to VOXEL server agents. if (_paintOn) { glm::vec3 avatarPos = _myAvatar.getPosition(); // For some reason, we don't want to flip X and Z here. _paintingVoxel.x = avatarPos.x / 10.0; _paintingVoxel.y = avatarPos.y / 10.0; _paintingVoxel.z = avatarPos.z / 10.0; if (_paintingVoxel.x >= 0.0 && _paintingVoxel.x <= 1.0 && _paintingVoxel.y >= 0.0 && _paintingVoxel.y <= 1.0 && _paintingVoxel.z >= 0.0 && _paintingVoxel.z <= 1.0) { sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, _paintingVoxel); } } } ///////////////////////////////////////////////////////////////////////////////////// // loadViewFrustum() // // Description: this will load the view frustum bounds for EITHER the head // or the "myCamera". // void Application::loadViewFrustum(ViewFrustum& viewFrustum) { // We will use these below, from either the camera or head vectors calculated above glm::vec3 position; glm::vec3 direction; glm::vec3 up; glm::vec3 right; float fov, nearClip, farClip; // Camera or Head? if (_cameraFrustum->isChecked()) { position = _myCamera.getPosition(); } else { position = _myAvatar.getHeadPosition(); } fov = _myCamera.getFieldOfView(); nearClip = _myCamera.getNearClip(); farClip = _myCamera.getFarClip(); Orientation o = _myCamera.getOrientation(); direction = o.getFront(); up = o.getUp(); right = o.getRight(); /* printf("position.x=%f, position.y=%f, position.z=%f\n", position.x, position.y, position.z); printf("yaw=%f, pitch=%f, roll=%f\n", yaw,pitch,roll); printf("direction.x=%f, direction.y=%f, direction.z=%f\n", direction.x, direction.y, direction.z); printf("up.x=%f, up.y=%f, up.z=%f\n", up.x, up.y, up.z); printf("right.x=%f, right.y=%f, right.z=%f\n", right.x, right.y, right.z); printf("fov=%f\n", fov); printf("nearClip=%f\n", nearClip); printf("farClip=%f\n", farClip); */ // Set the viewFrustum up with the correct position and orientation of the camera viewFrustum.setPosition(position); viewFrustum.setOrientation(direction,up,right); // Also make sure it's got the correct lens details from the camera viewFrustum.setFieldOfView(fov); viewFrustum.setNearClip(nearClip); viewFrustum.setFarClip(farClip); // Ask the ViewFrustum class to calculate our corners viewFrustum.calculate(); } // this shader is an adaptation (HLSL -> GLSL, removed conditional) of the one in the Oculus sample // code (Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp), which is under the Apache license // (http://www.apache.org/licenses/LICENSE-2.0) static const char* DISTORTION_FRAGMENT_SHADER = "#version 120\n" "uniform sampler2D texture;" "uniform vec2 lensCenter;" "uniform vec2 screenCenter;" "uniform vec2 scale;" "uniform vec2 scaleIn;" "uniform vec4 hmdWarpParam;" "vec2 hmdWarp(vec2 in01) {" " vec2 theta = (in01 - lensCenter) * scaleIn;" " float rSq = theta.x * theta.x + theta.y * theta.y;" " vec2 theta1 = theta * (hmdWarpParam.x + hmdWarpParam.y * rSq + " " hmdWarpParam.z * rSq * rSq + hmdWarpParam.w * rSq * rSq * rSq);" " return lensCenter + scale * theta1;" "}" "void main(void) {" " vec2 tc = hmdWarp(gl_TexCoord[0].st);" " vec2 below = step(screenCenter.st + vec2(-0.25, -0.5), tc.st);" " vec2 above = vec2(1.0, 1.0) - step(screenCenter.st + vec2(0.25, 0.5), tc.st);" " gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), texture2D(texture, tc), " " above.s * above.t * below.s * below.t);" "}"; void Application::displayOculus(Camera& whichCamera) { // magic numbers ahoy! in order to avoid pulling in the Oculus utility library that calculates // the rendering parameters from the hardware stats, i just folded their calculations into // constants using the stats for the current-model hardware as contained in the SDK file // LibOVR/Src/Util/Util_Render_Stereo.cpp // eye // render the left eye view to the left side of the screen glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glTranslatef(0.151976, 0, 0); // +h, see Oculus SDK docs p. 26 gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(), whichCamera.getNearClip(), whichCamera.getFarClip()); glTranslatef(0.032, 0, 0); // dip/2, see p. 27 glMatrixMode(GL_MODELVIEW); glViewport(0, 0, _glWidget->width() / 2, _glWidget->height()); displaySide(whichCamera); // and the right eye to the right side glMatrixMode(GL_PROJECTION); glLoadIdentity(); glTranslatef(-0.151976, 0, 0); // -h gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(), whichCamera.getNearClip(), whichCamera.getFarClip()); glTranslatef(-0.032, 0, 0); glMatrixMode(GL_MODELVIEW); glViewport(_glWidget->width() / 2, 0, _glWidget->width() / 2, _glWidget->height()); displaySide(whichCamera); glPopMatrix(); // restore our normal viewport glViewport(0, 0, _glWidget->width(), _glWidget->height()); if (_oculusTextureID == 0) { glGenTextures(1, &_oculusTextureID); glBindTexture(GL_TEXTURE_2D, _oculusTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _glWidget->width(), _glWidget->height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); _oculusProgram = new ProgramObject(); _oculusProgram->attachFromSourceCode(GL_FRAGMENT_SHADER_ARB, DISTORTION_FRAGMENT_SHADER); _oculusProgram->link(); _textureLocation = _oculusProgram->getUniformLocation("texture"); _lensCenterLocation = _oculusProgram->getUniformLocation("lensCenter"); _screenCenterLocation = _oculusProgram->getUniformLocation("screenCenter"); _scaleLocation = _oculusProgram->getUniformLocation("scale"); _scaleInLocation = _oculusProgram->getUniformLocation("scaleIn"); _hmdWarpParamLocation = _oculusProgram->getUniformLocation("hmdWarpParam"); } else { glBindTexture(GL_TEXTURE_2D, _oculusTextureID); } glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _glWidget->width(), _glWidget->height()); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, _glWidget->width(), 0, _glWidget->height()); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); // for reference on setting these values, see SDK file Samples/OculusRoomTiny/RenderTiny_Device.cpp float scaleFactor = 1.0 / _oculusDistortionScale; float aspectRatio = (_glWidget->width() * 0.5) / _glWidget->height(); glDisable(GL_BLEND); glEnable(GL_TEXTURE_2D); _oculusProgram->bind(); _oculusProgram->setUniform(_textureLocation, 0); _oculusProgram->setUniform(_lensCenterLocation, 0.287994, 0.5); // see SDK docs, p. 29 _oculusProgram->setUniform(_screenCenterLocation, 0.25, 0.5); _oculusProgram->setUniform(_scaleLocation, 0.25 * scaleFactor, 0.5 * scaleFactor * aspectRatio); _oculusProgram->setUniform(_scaleInLocation, 4, 2 / aspectRatio); _oculusProgram->setUniform(_hmdWarpParamLocation, 1.0, 0.22, 0.24, 0); glColor3f(1, 0, 1); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(0.5, 0); glVertex2f(_glWidget->width()/2, 0); glTexCoord2f(0.5, 1); glVertex2f(_glWidget->width() / 2, _glWidget->height()); glTexCoord2f(0, 1); glVertex2f(0, _glWidget->height()); glEnd(); _oculusProgram->setUniform(_lensCenterLocation, 0.787994, 0.5); _oculusProgram->setUniform(_screenCenterLocation, 0.75, 0.5); glBegin(GL_QUADS); glTexCoord2f(0.5, 0); glVertex2f(_glWidget->width() / 2, 0); glTexCoord2f(1, 0); glVertex2f(_glWidget->width(), 0); glTexCoord2f(1, 1); glVertex2f(_glWidget->width(), _glWidget->height()); glTexCoord2f(0.5, 1); glVertex2f(_glWidget->width() / 2, _glWidget->height()); glEnd(); glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); _oculusProgram->release(); glPopMatrix(); } void Application::displaySide(Camera& whichCamera) { glPushMatrix(); if (_renderStarsOn->isChecked()) { // should be the first rendering pass - w/o depth buffer / lighting // compute starfield alpha based on distance from atmosphere float alpha = 1.0f; if (_renderAtmosphereOn->isChecked()) { float height = glm::distance(whichCamera.getPosition(), _environment.getAtmosphereCenter()); if (height < _environment.getAtmosphereInnerRadius()) { alpha = 0.0f; } else if (height < _environment.getAtmosphereOuterRadius()) { alpha = (height - _environment.getAtmosphereInnerRadius()) / (_environment.getAtmosphereOuterRadius() - _environment.getAtmosphereInnerRadius()); } } // finally render the starfield _stars.render(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(), whichCamera.getNearClip(), alpha); } // draw the sky dome if (_renderAtmosphereOn->isChecked()) { _environment.renderAtmosphere(whichCamera); } glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); // draw a red sphere float sphereRadius = 0.25f; glColor3f(1,0,0); glPushMatrix(); glutSolidSphere(sphereRadius, 15, 15); glPopMatrix(); //draw a grid ground plane.... drawGroundPlaneGrid(10.f); // Draw voxels if (_renderVoxels->isChecked()) { _voxels.render(); } // indicate what we'll be adding/removing in mouse mode, if anything if (_mouseVoxel.s != 0) { glPushMatrix(); if (_mouseMode == ADD_VOXEL_MODE) { // 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); } glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f, _mouseVoxel.y + _mouseVoxel.s*0.5f, _mouseVoxel.z + _mouseVoxel.s*0.5f); glLineWidth(4.0f); glutWireCube(_mouseVoxel.s); glLineWidth(1.0f); glPopMatrix(); } if (_renderAvatarsOn->isChecked()) { // Render avatars of other agents AgentList* agentList = AgentList::getInstance(); agentList->lock(); for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) { Avatar *avatar = (Avatar *)agent->getLinkedData(); avatar->render(0, _myCamera.getPosition()); } } agentList->unlock(); // Render my own Avatar _myAvatar.render(_lookingInMirror, _myCamera.getPosition()); } // Render the world box if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { render_world_box(); } // brad's frustum for debugging if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum); glPopMatrix(); } void Application::displayOverlay() { // Render 2D overlay: I/O level bar graphs and text glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0, _glWidget->width(), _glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); #ifndef _WIN32 _audio.render(_glWidget->width(), _glWidget->height()); _audioScope.render(20, _glWidget->height() - 200); #endif //noiseTest(_glWidget->width(), _glWidget->height()); if (DISPLAY_HEAD_MOUSE && !_lookingInMirror->isChecked() && USING_INVENSENSE_MPU9150) { // Display small target box at center or head mouse target that can also be used to measure LOD glColor3f(1.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); const int PIXEL_BOX = 20; glBegin(GL_LINE_STRIP); glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY - PIXEL_BOX/2); glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY - PIXEL_BOX/2); glVertex2f(_headMouseX + PIXEL_BOX/2, _headMouseY + PIXEL_BOX/2); glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY + PIXEL_BOX/2); glVertex2f(_headMouseX - PIXEL_BOX/2, _headMouseY - PIXEL_BOX/2); glEnd(); glEnable(GL_LINE_SMOOTH); } // Show detected levels from the serial I/O ADC channel sensors if (_displayLevels) _serialPort.renderLevels(_glWidget->width(), _glWidget->height()); // Display stats and log text onscreen glLineWidth(1.0f); glPointSize(1.0f); if (_renderStatsOn->isChecked()) { displayStats(); } if (_logOn->isChecked()) { logger.render(_glWidget->width(), _glWidget->height()); } // Show chat entry field if (_chatEntryOn) { _chatEntry.render(_glWidget->width(), _glWidget->height()); } // Stats at upper right of screen about who domain server is telling us about glPointSize(1.0f); char agents[100]; AgentList* agentList = AgentList::getInstance(); int totalAvatars = 0, totalServers = 0; for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) { agent->getType() == AGENT_TYPE_AVATAR ? totalAvatars++ : totalServers++; } sprintf(agents, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars); drawtext(_glWidget->width() - 150, 20, 0.10, 0, 1.0, 0, agents, 1, 0, 0); if (_paintOn) { char paintMessage[100]; sprintf(paintMessage,"Painting (%.3f,%.3f,%.3f/%.3f/%d,%d,%d)", _paintingVoxel.x, _paintingVoxel.y, _paintingVoxel.z, _paintingVoxel.s, (unsigned int)_paintingVoxel.red, (unsigned int)_paintingVoxel.green, (unsigned int)_paintingVoxel.blue); drawtext(_glWidget->width() - 350, 50, 0.10, 0, 1.0, 0, paintMessage, 1, 1, 0); } glPopMatrix(); } void Application::displayStats() { int statsVerticalOffset = 8; char stats[200]; sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps", _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats); std::stringstream voxelStats; voxelStats.precision(4); voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K"; drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxels Created: " << _voxels.getVoxelsCreated() / 1000.f << "K (" << _voxels.getVoxelsCreatedPerSecondAverage() / 1000.f << "Kps) "; drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxels Colored: " << _voxels.getVoxelsColored() / 1000.f << "K (" << _voxels.getVoxelsColoredPerSecondAverage() / 1000.f << "Kps) "; drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << "Voxel Bits Read: " << _voxels.getVoxelsBytesRead() * 8.f / 1000000.f << "M (" << _voxels.getVoxelsBytesReadPerSecondAverage() * 8.f / 1000000.f << " Mbps)"; drawtext(10, statsVerticalOffset + 290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); float voxelsBytesPerColored = _voxels.getVoxelsColored() ? ((float) _voxels.getVoxelsBytesRead() / _voxels.getVoxelsColored()) : 0; voxelStats << "Voxels Bits per Colored: " << voxelsBytesPerColored * 8; drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); Agent *avatarMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER); char avatarMixerStats[200]; if (avatarMixer) { sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps", roundf(avatarMixer->getAverageKilobitsPerSecond()), roundf(avatarMixer->getAveragePacketsPerSecond())); } else { sprintf(avatarMixerStats, "No Avatar Mixer"); } drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, avatarMixerStats); 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); int atZ = 150; // arbitrary place on screen that looks good for (int line=0; line < lines; line++) { drawtext(10, statsVerticalOffset + atZ, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]); delete perfStatLinesArray[line]; // we're responsible for cleanup perfStatLinesArray[line]=NULL; atZ+=20; // height of a line } delete []perfStatLinesArray; // we're responsible for cleanup } } ///////////////////////////////////////////////////////////////////////////////////// // renderViewFrustum() // // Description: this will render the view frustum bounds for EITHER the head // 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: // * Origin Direction/Up/Right vectors - these will be drawn at the point of the camera // * Near plane - this plane is drawn very close to the origin point. // * Right/Left planes - these two planes are drawn between the near and far planes. // * Far plane - the plane is drawn in the distance. // Modes - the following modes, will draw the following parts. // * All - draws all the parts listed above // * Planes - draws the planes but not the origin vectors // * Origin Vectors - draws the origin vectors ONLY // * Near Plane - draws only the near plane // * Far Plane - draws only the far plane void Application::renderViewFrustum(ViewFrustum& viewFrustum) { // Load it with the latest details! loadViewFrustum(viewFrustum); glm::vec3 position = viewFrustum.getPosition(); glm::vec3 direction = viewFrustum.getDirection(); glm::vec3 up = viewFrustum.getUp(); glm::vec3 right = viewFrustum.getRight(); // Get ready to draw some lines glDisable(GL_LIGHTING); glColor4f(1.0, 1.0, 1.0, 1.0); glLineWidth(1.0); glBegin(GL_LINES); if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_VECTORS) { // Calculate the origin direction vectors glm::vec3 lookingAt = position + (direction * 0.2f); glm::vec3 lookingAtUp = position + (up * 0.2f); glm::vec3 lookingAtRight = position + (right * 0.2f); // Looking At = white glColor3f(1,1,1); glVertex3f(position.x, position.y, position.z); glVertex3f(lookingAt.x, lookingAt.y, lookingAt.z); // Looking At Up = purple glColor3f(1,0,1); glVertex3f(position.x, position.y, position.z); glVertex3f(lookingAtUp.x, lookingAtUp.y, lookingAtUp.z); // Looking At Right = cyan glColor3f(0,1,1); glVertex3f(position.x, position.y, position.z); glVertex3f(lookingAtRight.x, lookingAtRight.y, lookingAtRight.z); } if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES || _frustumDrawingMode == FRUSTUM_DRAW_MODE_NEAR_PLANE) { // Drawing the bounds of the frustum // 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); // viewFrustum.getNear plane - top edge glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); // viewFrustum.getNear plane - right edge glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z); glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); // viewFrustum.getNear plane - left edge glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); } if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES || _frustumDrawingMode == FRUSTUM_DRAW_MODE_FAR_PLANE) { // viewFrustum.getFar plane - bottom edge glColor3f(0,1,0); // GREEN!!! glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); // viewFrustum.getFar plane - top edge glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); // viewFrustum.getFar plane - right edge glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); // viewFrustum.getFar plane - left edge glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); } if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES) { // RIGHT PLANE IS CYAN // 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); // right plane - top edge - viewFrustum.getNear to distant glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z); glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z); // LEFT PLANE IS BLUE // left plane - bottom edge - viewFrustum.getNear to distant glColor3f(0,0,1); glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z); glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); // 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); } glEnd(); glEnable(GL_LIGHTING); } void Application::setupPaintingVoxel() { glm::vec3 avatarPos = _myAvatar.getPosition(); _paintingVoxel.x = avatarPos.z/-10.0; // voxel space x is negative z head space _paintingVoxel.y = avatarPos.y/-10.0; // voxel space y is negative y head space _paintingVoxel.z = avatarPos.x/-10.0; // voxel space z is negative x head space _paintingVoxel.s = 1.0/256; shiftPaintingColor(); } void Application::shiftPaintingColor() { // About the color of the paintbrush... first determine the dominant color _dominantColor = (_dominantColor + 1) % 3; // 0=red,1=green,2=blue _paintingVoxel.red = (_dominantColor == 0) ? randIntInRange(200, 255) : randIntInRange(40, 100); _paintingVoxel.green = (_dominantColor == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100); _paintingVoxel.blue = (_dominantColor == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100); } void Application::addVoxelInFrontOfAvatar() { VoxelDetail detail; glm::vec3 position = (_myAvatar.getPosition() + _myAvatar.getCameraDirection()) * (1.0f / TREE_SCALE); detail.s = _mouseVoxelScale; detail.x = detail.s * floor(position.x / detail.s); detail.y = detail.s * floor(position.y / detail.s); detail.z = detail.s * floor(position.z / detail.s); detail.red = 128; detail.green = 128; detail.blue = 128; sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, detail); // create the voxel locally so it appears immediately _voxels.createVoxel(detail.x, detail.y, detail.z, detail.s, detail.red, detail.green, detail.blue); } void Application::addVoxelUnderCursor() { if (_mouseVoxel.s != 0) { sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, _mouseVoxel); // create the voxel locally so it appears immediately _voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s, _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue); } } void Application::deleteVoxelUnderCursor() { if (_mouseVoxel.s != 0) { sendVoxelEditMessage(PACKET_HEADER_ERASE_VOXEL, _mouseVoxel); // delete the voxel locally so it disappears immediately _voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); } } void Application::resetSensors() { _myAvatar.setPosition(START_LOCATION); _headMouseX = _glWidget->width() / 2; _headMouseY = _glWidget->height() / 2; _myAvatar.reset(); } static void setShortcutsEnabled(QWidget* widget, bool enabled) { foreach (QAction* action, widget->actions()) { QKeySequence shortcut = action->shortcut(); if (!shortcut.isEmpty() && (shortcut[0] & (Qt::CTRL | Qt::ALT | Qt::META)) == 0) { // it's a shortcut that may coincide with a "regular" key, so switch its context action->setShortcutContext(enabled ? Qt::WindowShortcut : Qt::WidgetShortcut); } } foreach (QObject* child, widget->children()) { if (child->isWidgetType()) { setShortcutsEnabled(static_cast(child), enabled); } } } void Application::setMenuShortcutsEnabled(bool enabled) { setShortcutsEnabled(_window->menuBar(), enabled); } void Application::attachNewHeadToAgent(Agent *newAgent) { if (newAgent->getLinkedData() == NULL) { newAgent->setLinkedData(new Avatar(false)); } } #ifndef _WIN32 void Application::audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) { static_cast(QCoreApplication::instance())->_audio.updateMixerParams(newMixerAddress, newMixerPort); } #endif // Receive packets from other agents/servers and decide what to do with them! void* Application::networkReceive(void* args) { sockaddr senderAddress; ssize_t bytesReceived; Application* app = static_cast(QCoreApplication::instance()); while (!app->_stopNetworkReceiveThread) { // check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that if (app->_wantToKillLocalVoxels) { app->_voxels.killLocalVoxels(); app->_wantToKillLocalVoxels = false; } if (AgentList::getInstance()->getAgentSocket().receive(&senderAddress, app->_incomingPacket, &bytesReceived)) { app->_packetCount++; app->_bytesCount += bytesReceived; switch (app->_incomingPacket[0]) { case PACKET_HEADER_TRANSMITTER_DATA: // Process UDP packets that are sent to the client from local sensor devices app->_myAvatar.processTransmitterData(app->_incomingPacket, bytesReceived); break; case PACKET_HEADER_VOXEL_DATA: case PACKET_HEADER_VOXEL_DATA_MONOCHROME: case PACKET_HEADER_Z_COMMAND: case PACKET_HEADER_ERASE_VOXEL: app->_voxels.parseData(app->_incomingPacket, bytesReceived); break; case PACKET_HEADER_ENVIRONMENT_DATA: app->_environment.parseData(app->_incomingPacket, bytesReceived); break; case PACKET_HEADER_BULK_AVATAR_DATA: AgentList::getInstance()->processBulkAgentData(&senderAddress, app->_incomingPacket, bytesReceived); break; default: AgentList::getInstance()->processAgentData(&senderAddress, app->_incomingPacket, bytesReceived); break; } } else if (!app->_enableNetworkThread) { break; } } if (app->_enableNetworkThread) { pthread_exit(0); } return NULL; }