2141 lines · 77.6 KB · C++
RawDownload
//  //  Interface////  Allows you to connect to and see/hear the shared 3D space.//  Optionally uses serialUSB connection to get gyro data for head movement.//  Optionally gets UDP stream from transmitter to animate controller/hand.//  //  Usage:  The interface client first attempts to contact a domain server to//          discover the appropriate audio, voxel, and avatar servers to contact.//          Right now, the default domain server is "highfidelity.below92.com"//          You can change the domain server to use your own by editing the//          DOMAIN_HOSTNAME or DOMAIN_IP strings in the file AgentList.cpp//////  Welcome Aboard!// #include <math.h>#include <string.h>#include <sstream>#include <stdio.h>#include <stdlib.h> #ifdef _WIN32#include "Syssocket.h"#include "Systime.h"#else#include <sys/time.h>#include <arpa/inet.h>#include <ifaddrs.h>#endif #include <pthread.h>  #include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp> #include "InterfaceConfig.h" #include "Log.h"#include "shared_Log.h"#include "voxels_Log.h"#include "avatars_Log.h" #include "world.h"#include "Util.h"#ifndef _WIN32#include "Audio.h"#endif #include "AngleUtil.h"#include "Stars.h" #include "ui/ChatEntry.h"#include "ui/MenuRow.h"#include "ui/MenuColumn.h"#include "ui/Menu.h"#include "ui/TextRenderer.h"#include "renderer/ProgramObject.h"#include "renderer/ShaderObject.h" #include "Application.h"#include "Camera.h"#include "Avatar.h"#include <AgentList.h>#include <AgentTypes.h>#include "VoxelSystem.h"#include "Environment.h"#include "Oscilloscope.h"#include "UDPSocket.h"#include "SerialInterface.h"#include <PacketHeaders.h>#include <AvatarData.h>#include <PerfStat.h>#include <SimpleMovingAverage.h> #include "ViewFrustum.h"#include "HandControl.h"#include "AvatarRenderer.h"#include "OculusManager.h" using namespace std; void reshape(int width, int height); // will be defined belowvoid loadViewFrustum(ViewFrustum& viewFrustum);  // will be defined below glm::vec3 getGravity(glm::vec3 pos); //get the local gravity vector at this location in the universe bool enableNetworkThread = true;pthread_t networkReceiveThread;bool stopNetworkReceiveThread = false; unsigned char incomingPacket[MAX_PACKET_SIZE];int packetCount = 0;int packetsPerSecond = 0; int bytesPerSecond = 0;int bytesCount = 0; int screenWidth = 1200; //  Window sizeint screenHeight = 800;int fullscreen = 0;float aspectRatio = 1.0f; bool  USING_MOUSE_VIEW_SHIFT = false;float MOUSE_VIEW_SHIFT_RATE         = 40.0f;float MOUSE_VIEW_SHIFT_YAW_MARGIN   = (float)(WIDTH  * 0.2f);float MOUSE_VIEW_SHIFT_PITCH_MARGIN = (float)(HEIGHT * 0.2f);float MOUSE_VIEW_SHIFT_YAW_LIMIT    = 45.0;float MOUSE_VIEW_SHIFT_PITCH_LIMIT  = 30.0; //CameraMode defaultCameraMode = CAMERA_MODE_FIRST_PERSON;CameraMode defaultCameraMode = CAMERA_MODE_THIRD_PERSON; bool wantColorRandomizer = true;    // for addSphere and load file Oscilloscope audioScope(256,200,true); ViewFrustum viewFrustum;            // current state of view frustum, perspective, orientation, etc. Avatar myAvatar(true);            // The rendered avatar of oneselfCamera myCamera;                  // My view onto the world Camera viewFrustumOffsetCamera;   // The camera we use to sometimes show the view frustum from an offset mode AvatarRenderer avatarRenderer; //  Starfield informationchar starFile[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";char starCacheFile[] = "cachedStars.txt";Stars stars; glm::vec3 box(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE); VoxelSystem voxels; Environment environment;  #ifndef _WIN32Audio audio(&audioScope, &myAvatar);#endif #define IDLE_SIMULATE_MSECS 16           //  How often should call simulate and other stuff                                          //  in the idle loop?  (60 FPS is default)  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) bool renderWarningsOn = false;      //  Whether to show render pipeline warningsbool renderStatsOn = false;         //  Whether to show onscreen text overlay with statsbool renderVoxels = true;           //  Whether to render voxelsbool renderStarsOn = true;          //  Whether to display the starsbool renderAtmosphereOn = true;     //  Whether to display the atmospherebool renderAvatarsOn = true;        //  Whether to render avatarsbool renderFirstPersonOn = false;   //  Whether to render in first person bool paintOn = false;               //  Whether to paint voxels as you fly aroundVoxelDetail paintingVoxel;          //    The voxel we're painting if we're paintingunsigned char dominantColor = 0;    //    The dominant color of the voxel we're paintingbool perfStatsOn = false;           //  Do we want to display perfStats? bool logOn = true;                  //  Whether to show on-screen log bool wantToKillLocalVoxels = false; int noiseOn = 0;                    //  Whether to add random noise float noise = 1.0;                  //  Overall magnitude scaling for random noise levels  bool gyroLook = true;               //  Whether to allow the gyro data from head to move your view int displayLevels = 0;bool lookingInMirror = 0;           //  Are we currently rendering one's own head as if in mirror? int displayHeadMouse = 1;         //  Display sample mouse pointer controlled by head movementint headMouseX, headMouseY;  HandControl handControl; int mouseX = 0;int mouseY = 0; //  Mouse location at start of last down clickint mousePressed = 0; //  true if mouse has been pressed (clear when finished) // The current mode for mouse interactionenum MouseMode { ADD_VOXEL_MODE, DELETE_VOXEL_MODE, COLOR_VOXEL_MODE };MouseMode mouseMode = ADD_VOXEL_MODE;VoxelDetail mouseVoxel; // details of the voxel under the mouse cursorfloat mouseVoxelScale = 1.0f / 1024.0f; // the scale for adding/removing voxels Menu menu;       // main menuint menuOn = 1;  //  Whether to show onscreen menu ChatEntry chatEntry;       // chat entry fieldbool chatEntryOn = false;  //  Whether to show the chat entry bool oculusOn = false;              //  Whether to configure the display for the Oculus RiftGLuint oculusTextureID = 0;         //  The texture to which we render for Oculus distortionProgramObject* oculusProgram = 0;   //  The GLSL program containing the distortion shaderfloat oculusDistortionScale = 1.25; //  Controls the Oculus field of viewa SerialInterface serialPort; glm::vec3 gravity; //  Frame Rate Measurement int frameCount = 0;                  float FPS = 120.f;timeval timerStart, timerEnd;timeval lastTimeIdle;double elapsedTime;timeval applicationStartupTime;bool justStarted = true; //  Every second, check the frame rates and other stuffvoid Timer(int extra) {    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;        glutTimerFunc(1000,Timer,0);    gettimeofday(&timerStart, NULL);        // if we haven't detected gyros, check for them now    if (!serialPort.active) {        serialPort.pair();    }} void displayStats(void) {    int statsVerticalOffset = 50;    if (::menuOn == 0) {        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    }} void initDisplay(void) {    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    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);        if (fullscreen) glutFullScreen();} void init(void) {    voxels.init();    voxels.setViewerAvatar(&myAvatar);    voxels.setCamera(&myCamera);        environment.init();        handControl.setScreenDimensions(::screenWidth, ::screenHeight);     headMouseX = ::screenWidth / 2;    headMouseY = ::screenHeight / 2;     stars.readInput(starFile, starCacheFile, 0);      if (noiseOn) {           myAvatar.setNoise(noise);    }        myAvatar.setPosition(start_location);    myCamera.setPosition(start_location);    myCamera.setMode(defaultCameraMode);        OculusManager::connect();        gettimeofday(&timerStart, NULL);    gettimeofday(&lastTimeIdle, NULL);} void terminate () {    // Close serial port    // close(serial_fd);        myAvatar.writeAvatarDataToFile();         #ifndef _WIN32    audio.terminate();    #endif        if (enableNetworkThread) {        stopNetworkReceiveThread = true;        pthread_join(networkReceiveThread, NULL);     }        exit(EXIT_SUCCESS);} void reset_sensors() {        myAvatar.setPosition(start_location);    headMouseX = ::screenWidth / 2;    headMouseY = ::screenHeight / 2;        myAvatar.reset();} 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;    }} //  Using gyro data, update both view frustum and avatar head positionvoid 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, ::screenWidth);    headMouseY = max(headMouseY, 0);    headMouseY = min(headMouseY, ::screenHeight);        //  Update head and body pitch and yaw based on measured gyro rates    if (::gyroLook) {        // Render Yaw        float renderYawSpring = fabs(headMouseX - ::screenWidth / 2.f) / (::screenWidth / 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 - ::screenHeight / 2.f) / (::screenHeight / 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)WIDTH  - MOUSE_VIEW_SHIFT_YAW_MARGIN;        float bottomBoundary = (float)HEIGHT - MOUSE_VIEW_SHIFT_PITCH_MARGIN;                if (mouseX > rightBoundary) {            float f = (mouseX - rightBoundary) / ( (float)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)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;        

Lines 1–500 of 2141.