diff --git a/animation-server/src/main.cpp b/animation-server/src/main.cpp index 4d0577b960..dec8f2ea5d 100644 --- a/animation-server/src/main.cpp +++ b/animation-server/src/main.cpp @@ -36,6 +36,7 @@ bool includeBorderTracer = true; bool includeMovingBug = true; bool includeBlinkingVoxel = false; bool includeDanceFloor = true; +bool buildStreet = false; const int ANIMATION_LISTEN_PORT = 40107; @@ -616,6 +617,61 @@ static void sendBillboard() { } } +bool roadInitialized = false; +const int ROAD_WIDTH_METERS = 3.0f; +const int BRICKS_ACROSS_ROAD = 32; +const float ROAD_BRICK_SIZE = 0.125f/TREE_SCALE; //(ROAD_WIDTH_METERS / TREE_SCALE) / BRICKS_ACROSS_ROAD; // in voxel units +const int ROAD_LENGTH = 1.0f / ROAD_BRICK_SIZE; // in bricks +const int ROAD_WIDTH = BRICKS_ACROSS_ROAD; // in bricks +glm::vec3 roadPosition(0.5f - (ROAD_BRICK_SIZE * BRICKS_ACROSS_ROAD), 0.0f, 0.0f); +const int BRICKS_PER_PACKET = 32; // guessing +const int PACKETS_PER_ROAD = VOXELS_PER_PACKET / (ROAD_LENGTH * ROAD_WIDTH); + +void doBuildStreet() { + if (roadInitialized) { + return; + } + + PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully! + static VoxelDetail details[BRICKS_PER_PACKET]; + unsigned char* bufferOut; + int sizeOut; + + for (int z = 0; z < ROAD_LENGTH; z++) { + for (int x = 0; x < ROAD_WIDTH; x++) { + + int nthVoxel = ((z * ROAD_WIDTH) + x); + int item = nthVoxel % BRICKS_PER_PACKET; + + glm::vec3 brick = roadPosition + glm::vec3(x * ROAD_BRICK_SIZE, 0, z * ROAD_BRICK_SIZE); + + details[item].s = ROAD_BRICK_SIZE; + details[item].x = brick.x; + details[item].y = brick.y; + details[item].z = brick.z; + + unsigned char randomTone = randIntInRange(118,138); + details[item].red = randomTone; + details[item].green = randomTone; + details[item].blue = randomTone; + + if (item == BRICKS_PER_PACKET - 1) { + if (createVoxelEditMessage(message, 0, BRICKS_PER_PACKET, (VoxelDetail*)&details, bufferOut, sizeOut)){ + ::packetsSent++; + ::bytesSent += sizeOut; + if (true || ::shouldShowPacketsPerSecond) { + printf("building road sending packet of size=%d\n", sizeOut); + } + NodeList::getInstance()->broadcastToNodes(bufferOut, sizeOut, &NODE_TYPE_VOXEL_SERVER, 1); + delete[] bufferOut; + } + } + } + } + roadInitialized = true; +} + + double start = 0; @@ -645,6 +701,10 @@ void* animateVoxels(void* args) { sendDanceFloor(); } + if (::buildStreet) { + doBuildStreet(); + } + uint64_t end = usecTimestampNow(); uint64_t elapsedSeconds = (end - ::start) / 1000000; if (::shouldShowPacketsPerSecond) { @@ -688,6 +748,9 @@ int main(int argc, const char * argv[]) const char* NO_DANCE_FLOOR = "--NoDanceFloor"; ::includeDanceFloor = !cmdOptionExists(argc, argv, NO_DANCE_FLOOR); + const char* BUILD_STREET = "--BuildStreet"; + ::buildStreet = cmdOptionExists(argc, argv, BUILD_STREET); + // Handle Local Domain testing with the --local command line const char* showPPS = "--showPPS"; ::shouldShowPacketsPerSecond = cmdOptionExists(argc, argv, showPPS); @@ -700,6 +763,11 @@ int main(int argc, const char * argv[]) nodeList->setDomainIPToLocalhost(); } + const char* domainIP = getCmdOption(argc, argv, "--domain"); + if (domainIP) { + NodeList::getInstance()->setDomainIP(domainIP); + } + nodeList->linkedDataCreateCallback = NULL; // do we need a callback? nodeList->startSilentNodeRemovalThread(); diff --git a/interface/resources/shaders/face_textured.frag b/interface/resources/shaders/face_textured.frag new file mode 100644 index 0000000000..2170074e25 --- /dev/null +++ b/interface/resources/shaders/face_textured.frag @@ -0,0 +1,83 @@ +#version 120 + +// +// face_textured.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the texture coordinate vector from left to right +uniform vec2 texCoordRight; + +// the texture coordinate vector from bottom to the top +uniform vec2 texCoordUp; + +// the permutation/normal texture +uniform sampler2D permutationNormalTexture; + +// the depth texture +uniform sampler2D depthTexture; + +// the position in model space +varying vec3 position; + +// returns the gradient at a single corner of our sampling cube +vec3 grad(vec3 location) { + float p1 = texture2D(permutationNormalTexture, vec2(location.x / 256.0, 0.25)).r; + float p2 = texture2D(permutationNormalTexture, vec2(p1 + location.y / 256.0, 0.25)).r; + return texture2D(permutationNormalTexture, vec2(p2 + location.z / 256.0, 0.75)).xyz * 2.0 - vec3(1.0, 1.0, 1.0); +} + +// returns the perlin noise value for the specified location +float perlin(vec3 location) { + vec3 floors = floor(location); + vec3 ceils = ceil(location); + vec3 fff = grad(floors); + vec3 ffc = grad(vec3(floors.x, floors.y, ceils.z)); + vec3 fcf = grad(vec3(floors.x, ceils.y, floors.z)); + vec3 fcc = grad(vec3(floors.x, ceils.y, ceils.z)); + vec3 cff = grad(vec3(ceils.x, floors.y, floors.z)); + vec3 cfc = grad(vec3(ceils.x, floors.y, ceils.z)); + vec3 ccf = grad(vec3(ceils.x, ceils.y, floors.z)); + vec3 ccc = grad(ceils); + vec3 ffracts = fract(location); + vec3 cfracts = ffracts - vec3(1.0, 1.0, 1.0); + vec3 params = ffracts*ffracts*(3.0 - 2.0*ffracts); + + float fffv = dot(fff, ffracts); + float ffcv = dot(ffc, vec3(ffracts.x, ffracts.y, cfracts.z)); + float fcfv = dot(fcf, vec3(ffracts.x, cfracts.y, ffracts.z)); + float fccv = dot(fcc, vec3(ffracts.x, cfracts.y, cfracts.z)); + float cffv = dot(cff, vec3(cfracts.x, ffracts.y, ffracts.z)); + float cfcv = dot(cfc, vec3(cfracts.x, ffracts.y, cfracts.z)); + float ccfv = dot(ccf, vec3(cfracts.x, cfracts.y, ffracts.z)); + float cccv = dot(ccc, cfracts); + + return mix( + mix(mix(fffv, cffv, params.x), mix(fcfv, ccfv, params.x), params.y), + mix(mix(ffcv, cfcv, params.x), mix(fccv, cccv, params.x), params.y), + params.z); +} + +void main(void) { + // compute normal from adjacent depth values + float left = texture2D(depthTexture, gl_TexCoord[0].st - texCoordRight * 0.01).r; + float right = texture2D(depthTexture, gl_TexCoord[0].st + texCoordRight * 0.01).r; + float bottom = texture2D(depthTexture, gl_TexCoord[0].st - texCoordUp * 0.01).r; + float top = texture2D(depthTexture, gl_TexCoord[0].st + texCoordUp * 0.01).r; + vec3 normal = normalize(gl_NormalMatrix * vec3(left - right, top - bottom, -0.05)); + + // compute the specular component (sans exponent) based on the normal OpenGL lighting model + float specular = max(0.0, dot(normalize(gl_LightSource[0].position.xyz + vec3(0.0, 0.0, 1.0)), normal)); + + // the base color is a subtle marble texture produced by modulating the phase of a sine wave by perlin noise + vec3 color = mix(vec3(1.0, 1.0, 1.0), vec3(0.75, 0.75, 0.75), + sin(dot(position, vec3(25.0, 25.0, 25.0)) + 2.0 * perlin(position * 10.0))); + + // standard lighting + gl_FragColor = vec4(color * ( gl_LightModel.ambient.rgb + /* gl_LightSource[0].ambient.rgb + */ + gl_LightSource[0].diffuse.rgb * max(0.0, dot(normal, gl_LightSource[0].position.xyz))) + + pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, gl_Color.a); +} diff --git a/interface/resources/shaders/face_textured.vert b/interface/resources/shaders/face_textured.vert new file mode 100644 index 0000000000..28bda9a84d --- /dev/null +++ b/interface/resources/shaders/face_textured.vert @@ -0,0 +1,38 @@ +#version 120 + +// +// face_textured.vert +// vertex shader +// +// Created by Andrzej Kapolka on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the lower left texture coordinate +uniform vec2 texCoordCorner; + +// the texture coordinate vector from left to right +uniform vec2 texCoordRight; + +// the texture coordinate vector from bottom to the top +uniform vec2 texCoordUp; + +// the depth texture +uniform sampler2D depthTexture; + +// the position in model space +varying vec3 position; + +void main(void) { + gl_TexCoord[0] = vec4(texCoordCorner + gl_Vertex.x * texCoordRight + gl_Vertex.y * texCoordUp, 0.0, 1.0); + float depth = texture2D(depthTexture, gl_TexCoord[0].st).r; + + // store the model space vertex + position = gl_Vertex.xyz; + + // set alpha to zero for invalid depth values + const float MIN_VISIBLE_DEPTH = 1.0 / 255.0; + const float MAX_VISIBLE_DEPTH = 254.0 / 255.0; + gl_FrontColor = vec4(1.0, 1.0, 1.0, step(MIN_VISIBLE_DEPTH, depth) * (1.0 - step(MAX_VISIBLE_DEPTH, depth))); + gl_Position = gl_ModelViewProjectionMatrix * vec4(0.5 - gl_Vertex.x, gl_Vertex.y - 0.5, depth - 0.5, 1.0); +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 772908abe5..5aac781d3d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -77,8 +77,10 @@ static char STAR_CACHE_FILE[] = "cachedStars.txt"; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored -const glm::vec3 START_LOCATION(4.f, 0.f, 5.f); // Where one's own node begins in the world - // (will be overwritten if avatar data file is found) +// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found) +// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by +// Grayson as he's building a street around here for demo dinner 2 +const glm::vec3 START_LOCATION(0.485f * TREE_SCALE, 0.f, 0.5f * TREE_SCALE); const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) @@ -234,6 +236,10 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : NodeList::createInstance(NODE_TYPE_AGENT, listenPort); + NodeList::getInstance()->addHook(&_voxels); + NodeList::getInstance()->addHook(this); + + _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); if (!_enableNetworkThread) { NodeList::getInstance()->getNodeSocket()->setBlocking(false); @@ -317,6 +323,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // initialization continues in initializeGL when OpenGL context is ready } +Application::~Application() { + NodeList::getInstance()->removeHook(&_voxels); + NodeList::getInstance()->removeHook(this); +} + void Application::initializeGL() { qDebug( "Created Display Window.\n" ); @@ -1290,9 +1301,9 @@ void Application::editPreferences() { horizontalFieldOfView->setValue(_horizontalFieldOfView); form->addRow("Horizontal field of view (degrees):", horizontalFieldOfView); - QDoubleSpinBox* headCameraPitchYawScale = new QDoubleSpinBox(); - headCameraPitchYawScale->setValue(_headCameraPitchYawScale); - form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); + QDoubleSpinBox* gyroCameraSensitivity = new QDoubleSpinBox(); + gyroCameraSensitivity->setValue(_gyroCameraSensitivity); + form->addRow("Gyro Camera Sensitivity (0 - 1):", gyroCameraSensitivity); QDoubleSpinBox* leanScale = new QDoubleSpinBox(); leanScale->setValue(_myAvatar.getLeanScale()); @@ -1342,7 +1353,7 @@ void Application::editPreferences() { _myAvatar.getVoxels()->setVoxelURL(url); sendAvatarVoxelURLMessage(url); - _headCameraPitchYawScale = headCameraPitchYawScale->value(); + _gyroCameraSensitivity = gyroCameraSensitivity->value(); _myAvatar.setLeanScale(leanScale->value()); _audioJitterBufferSamples = audioJitterBufferSamples->value(); if (!shouldDynamicallySetJitterBuffer()) { @@ -1943,8 +1954,11 @@ void Application::initMenu() { _testPing->setChecked(true); (_fullScreenMode = optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F))->setCheckable(true); optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true); - optionsMenu->addAction("Toggle Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true); + optionsMenu->addAction("Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true); + (_wantCollisionsOn = optionsMenu->addAction("Turn collisions On", this, SLOT(toggleWantCollisionsOn())))->setCheckable(true); + _wantCollisionsOn->setChecked(true); optionsMenu->addAction("Cycle Webcam Send Mode", _webcam.getGrabber(), SLOT(cycleVideoSendMode())); + optionsMenu->addAction("Webcam Texture", _webcam.getGrabber(), SLOT(setDepthOnly(bool)))->setCheckable(true); optionsMenu->addAction("Go Home", this, SLOT(goHome()), Qt::CTRL | Qt::Key_G); QMenu* audioMenu = menuBar->addMenu("Audio"); @@ -2137,6 +2151,10 @@ void Application::toggleMixedSong() { } } +void Application::toggleWantCollisionsOn() { + _myAvatar.setWantCollisionsOn(_wantCollisionsOn->isChecked()); +} + void Application::resetSongMixMenuItem() { if (_audio.getSongFileBytes() == 0) { _rawAudioMicrophoneMix->setText("Mix RAW Song"); @@ -2287,6 +2305,56 @@ void Application::renderLookatIndicator(glm::vec3 pointOfInterest, Camera& which renderCircle(haloOrigin, INDICATOR_RADIUS, IDENTITY_UP, NUM_SEGMENTS); } +void Application::renderFollowIndicator() { + NodeList* nodeList = NodeList::getInstance(); + + glLineWidth(5); + glBegin(GL_LINES); + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); ++node) { + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { + Avatar* avatar = (Avatar *) node->getLinkedData(); + Avatar* leader = NULL; + + if (avatar->getLeaderID() != UNKNOWN_NODE_ID) { + if (avatar->getLeaderID() == NodeList::getInstance()->getOwnerID()) { + leader = &_myAvatar; + } else { + for (NodeList::iterator it = nodeList->begin(); it != nodeList->end(); ++it) { + if(it->getNodeID() == avatar->getLeaderID() + && it->getType() == NODE_TYPE_AGENT) { + leader = (Avatar*) it->getLinkedData(); + } + } + } + + if (leader != NULL) { + glColor3f(1.f, 0.f, 0.f); + glVertex3f(avatar->getPosition().x, + avatar->getPosition().y, + avatar->getPosition().z); + glColor3f(0.f, 1.f, 0.f); + glVertex3f(leader->getPosition().x, + leader->getPosition().y, + leader->getPosition().z); + } + } + } + } + + if (_myAvatar.getLeadingAvatar() != NULL) { + glColor3f(1.f, 0.f, 0.f); + glVertex3f(_myAvatar.getPosition().x, + _myAvatar.getPosition().y, + _myAvatar.getPosition().z); + glColor3f(0.f, 1.f, 0.f); + glVertex3f(_myAvatar.getLeadingAvatar()->getPosition().x, + _myAvatar.getLeadingAvatar()->getPosition().y, + _myAvatar.getLeadingAvatar()->getPosition().z); + } + + glEnd(); +} + void Application::update(float deltaTime) { // Use Transmitter Hand to move hand if connected, else use mouse @@ -2500,7 +2568,7 @@ void Application::update(float deltaTime) { if (!avatar->isInitialized()) { avatar->init(); } - avatar->simulate(deltaTime, NULL); + avatar->simulate(deltaTime, NULL, 0.f); avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); } node->unlock(); @@ -2515,13 +2583,11 @@ void Application::update(float deltaTime) { } if (_transmitterDrives->isChecked() && _myTransmitter.isConnected()) { - _myAvatar.simulate(deltaTime, &_myTransmitter); + _myAvatar.simulate(deltaTime, &_myTransmitter, _gyroCameraSensitivity); } else { - _myAvatar.simulate(deltaTime, NULL); + _myAvatar.simulate(deltaTime, NULL, _gyroCameraSensitivity); } - _myAvatar.getHand().simulate(deltaTime, true); - if (!OculusManager::isConnected()) { if (_lookingInMirror->isChecked()) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { @@ -2574,7 +2640,7 @@ void Application::update(float deltaTime) { if (_renderParticleSystemOn->isChecked()) { updateParticleSystem(deltaTime); - } + } } void Application::updateAvatar(float deltaTime) { @@ -2586,10 +2652,6 @@ void Application::updateAvatar(float deltaTime) { // Update my avatar's state from gyros and/or webcam _myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(), - glm::vec3(_headCameraPitchYawScale, - _headCameraPitchYawScale, - _headCameraPitchYawScale), - 0.f, _pitchFromTouch); if (_serialHeadSensor.isActive()) { @@ -2876,6 +2938,27 @@ void Application::displayOculus(Camera& whichCamera) { glPopMatrix(); } +void Application::setupWorldLight(Camera& whichCamera) { + + // 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 relativeSunLoc = glm::normalize(_environment.getClosestData(whichCamera.getPosition()).getSunLocation() - + whichCamera.getPosition()); + GLfloat light_position0[] = { relativeSunLoc.x, relativeSunLoc.y, relativeSunLoc.z, 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); +} + void Application::displaySide(Camera& whichCamera) { // transform by eye offset @@ -2905,22 +2988,7 @@ void Application::displaySide(Camera& whichCamera) { 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); - - glm::vec3 relativeSunLoc = glm::normalize(_environment.getClosestData(whichCamera.getPosition()).getSunLocation() - - whichCamera.getPosition()); - GLfloat light_position0[] = { relativeSunLoc.x, relativeSunLoc.y, relativeSunLoc.z, 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); + setupWorldLight(whichCamera); if (_renderStarsOn->isChecked()) { if (!_stars.getFileLoaded()) { @@ -2986,7 +3054,7 @@ void Application::displaySide(Camera& whichCamera) { glDisable(GL_FOG); glDisable(GL_NORMALIZE); - renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude()); + //renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude()); } // Draw voxels if (_renderVoxels->isChecked()) { @@ -3015,6 +3083,8 @@ void Application::displaySide(Camera& whichCamera) { glEnable(GL_LIGHTING); } + _myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS, whichCamera); + if (_renderAvatarsOn->isChecked()) { // Render avatars of other nodes NodeList* nodeList = NodeList::getInstance(); @@ -3049,6 +3119,8 @@ void Application::displaySide(Camera& whichCamera) { } } + _myAvatar.renderScreenTint(SCREEN_TINT_AFTER_AVATARS, whichCamera); + if (_renderParticleSystemOn->isChecked()) { if (_particleSystemInitialized) { _particleSystem.render(); @@ -3060,6 +3132,20 @@ void Application::displaySide(Camera& whichCamera) { // brad's frustum for debugging if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum); + + // render voxel fades if they exist + if (_voxelFades.size() > 0) { + for(std::vector::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { + fade->render(); + if(fade->isDone()) { + fade = _voxelFades.erase(fade); + } else { + ++fade; + } + } + } + + renderFollowIndicator(); } void Application::displayOverlay() { @@ -3627,6 +3713,62 @@ void Application::shiftPaintingColor() { } +void Application::injectVoxelAddedSoundEffect() { + AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025); + + if (voxelInjector) { + voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); + //voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw()); + voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value + + /* for (int i = 0; i + < 22050; i++) { + if (i % 4 == 0) { + voxelInjector->addSample(4000); + } else if (i % 4 == 1) { + voxelInjector->addSample(0); + } else if (i % 4 == 2) { + voxelInjector->addSample(-4000); + } else { + voxelInjector->addSample(0); + } + */ + + const float BIG_VOXEL_MIN_SIZE = .01f; + + for (int i = 0; i < 11025; i++) { + + /* + A440 square wave + if (sin(i * 2 * PIE / 50)>=0) { + voxelInjector->addSample(4000); + } else { + voxelInjector->addSample(-4000); + } + */ + + if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) { + voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200)))); + } else { + voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808 + } + } + + //voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80 + //voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808 + //voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808 + //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave + //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation + + //FM library + //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep + //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep + //voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse + + AudioInjectionManager::threadInjector(voxelInjector); + } +} + bool Application::maybeEditVoxelUnderCursor() { if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) { if (_mouseVoxel.s != 0) { @@ -3637,65 +3779,35 @@ bool Application::maybeEditVoxelUnderCursor() { // create the voxel locally so it appears immediately _voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s, _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, _destructiveAddVoxel->isChecked()); + + // Implement voxel fade effect + VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f); + const float VOXEL_BOUNDS_ADJUST = 0.01f; + float slightlyBigger = _mouseVoxel.s * VOXEL_BOUNDS_ADJUST; + fade.voxelDetails.x = _mouseVoxel.x - slightlyBigger; + fade.voxelDetails.y = _mouseVoxel.y - slightlyBigger; + fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger; + fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; + _voxelFades.push_back(fade); + + // inject a sound effect + injectVoxelAddedSoundEffect(); // remember the position for drag detection _justEditedVoxel = true; - AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(11025); - voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); - //_myAvatar.getPosition() -// voxelInjector->setBearing(-1 * _myAvatar.getAbsoluteHeadYaw()); - voxelInjector->setVolume (16 * pow (_mouseVoxel.s, 2) / .0000001); //255 is max, and also default value - - /* for (int i = 0; i - < 22050; i++) { - if (i % 4 == 0) { - voxelInjector->addSample(4000); - } else if (i % 4 == 1) { - voxelInjector->addSample(0); - } else if (i % 4 == 2) { - voxelInjector->addSample(-4000); - } else { - voxelInjector->addSample(0); - } - */ - - - const float BIG_VOXEL_MIN_SIZE = .01f; - - for (int i = 0; i < 11025; i++) { - - /* - A440 square wave - if (sin(i * 2 * PIE / 50)>=0) { - voxelInjector->addSample(4000); - } else { - voxelInjector->addSample(-4000); - } - */ - - if (_mouseVoxel.s > BIG_VOXEL_MIN_SIZE) { - voxelInjector->addSample(20000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 200)))); - } else { - voxelInjector->addSample(16000 * sin(i / (1.5 * log (_mouseVoxel.s / .0001) * ((i + 11025) / 5512.5)))); //808 - } - } - - //voxelInjector->addSample(32500 * sin(i/(2 * 1 * ((i+5000)/5512.5)))); //80 - //voxelInjector->addSample(20000 * sin(i/(6 * (_mouseVoxel.s/.001) *((i+5512.5)/5512.5)))); //808 - //voxelInjector->addSample(20000 * sin(i/(6 * ((i+5512.5)/5512.5)))); //808 - //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50)); //A440 sine wave - //voxelInjector->addSample(4000 * sin(i * 2 * PIE /50) * sin (i/500)); //A440 sine wave with amplitude modulation - - //FM library - //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(500*sin((i+1)/200)))); //FM 1 dubstep - //voxelInjector->addSample(20000 * sin((i * 2 * PIE) /(300*sin((i+1)/5.0)))); //FM 2 flange sweep - //voxelInjector->addSample(10000 * sin((i * 2 * PIE) /(500*sin((i+1)/500.0)))); //FM 3 resonant pulse - - AudioInjectionManager::threadInjector(voxelInjector); } } else if (_deleteVoxelMode->isChecked()) { deleteVoxelUnderCursor(); + VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); + const float VOXEL_BOUNDS_ADJUST = 0.01f; + float slightlyBigger = _mouseVoxel.s * VOXEL_BOUNDS_ADJUST; + fade.voxelDetails.x = _mouseVoxel.x - slightlyBigger; + fade.voxelDetails.y = _mouseVoxel.y - slightlyBigger; + fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger; + fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; + _voxelFades.push_back(fade); + } else if (_eyedropperMode->isChecked()) { eyedropperVoxelUnderCursor(); } else { @@ -3710,17 +3822,20 @@ void Application::deleteVoxelUnderCursor() { // sending delete to the server is sufficient, server will send new version so we see updates soon enough sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel); AudioInjector* voxelInjector = AudioInjectionManager::injectorWithCapacity(5000); - voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); -// voxelInjector->setBearing(0); //straight down the z axis - voxelInjector->setVolume (255); //255 is max, and also default value - - for (int i = 0; i < 5000; i++) { - voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse - //voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter + if (voxelInjector) { + voxelInjector->setPosition(glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z)); + //voxelInjector->setBearing(0); //straight down the z axis + voxelInjector->setVolume (255); //255 is max, and also default value + + + for (int i = 0; i < 5000; i++) { + voxelInjector->addSample(10000 * sin((i * 2 * PIE) / (500 * sin((i + 1) / 500.0)))); //FM 3 resonant pulse + //voxelInjector->addSample(20000 * sin((i) /((4 / _mouseVoxel.s) * sin((i)/(20 * _mouseVoxel.s / .001))))); //FM 2 comb filter + } + + AudioInjectionManager::threadInjector(voxelInjector); } - - AudioInjectionManager::threadInjector(voxelInjector); } // remember the position for drag detection _justEditedVoxel = true; @@ -3817,6 +3932,57 @@ void Application::attachNewHeadToNode(Node* newNode) { } } +void Application::nodeAdded(Node* node) { +} + +void Application::nodeKilled(Node* node) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + uint16_t nodeID = node->getNodeID(); + // see if this is the first we've heard of this node... + if (_voxelServerJurisdictions.find(nodeID) != _voxelServerJurisdictions.end()) { + VoxelPositionSize jurisditionDetails; + jurisditionDetails = _voxelServerJurisdictions[nodeID]; + + printf("voxel server going away...... v[%f, %f, %f, %f]\n", + jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s); + + // Add the jurisditionDetails object to the list of "fade outs" + VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); + fade.voxelDetails = jurisditionDetails; + _voxelFades.push_back(fade); + } + } +} + +int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress) { + + // parse the incoming stats data, and stick it into our averaging stats object for now... even though this + // means mixing in stats from potentially multiple servers. + int statsMessageLength = _voxelSceneStats.unpackFromMessage(messageData, messageLength); + + // But, also identify the sender, and keep track of the contained jurisdiction root for this server + Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); + uint16_t nodeID = voxelServer->getNodeID(); + + VoxelPositionSize jurisditionDetails; + voxelDetailsForCode(_voxelSceneStats.getJurisdictionRoot(), jurisditionDetails); + + // see if this is the first we've heard of this node... + if (_voxelServerJurisdictions.find(nodeID) == _voxelServerJurisdictions.end()) { + printf("stats from new voxel server... v[%f, %f, %f, %f]\n", + jurisditionDetails.x, jurisditionDetails.y, jurisditionDetails.z, jurisditionDetails.s); + + // Add the jurisditionDetails object to the list of "fade outs" + VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE); + fade.voxelDetails = jurisditionDetails; + _voxelFades.push_back(fade); + } + // store jurisdiction details for later use + _voxelServerJurisdictions[nodeID] = jurisditionDetails; + + return statsMessageLength; +} + // Receive packets from other nodes/servers and decide what to do with them! void* Application::networkReceive(void* args) { sockaddr senderAddress; @@ -3860,7 +4026,8 @@ void* Application::networkReceive(void* args) { // immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first // then process any remaining bytes as if it was another packet if (messageData[0] == PACKET_TYPE_VOXEL_STATS) { - int statsMessageLength = app->_voxelSceneStats.unpackFromMessage(messageData, messageLength); + + int statsMessageLength = app->parseVoxelStats(messageData, messageLength, senderAddress); if (messageLength > statsMessageLength) { messageData += statsMessageLength; messageLength -= statsMessageLength; @@ -3954,11 +4121,11 @@ void Application::saveAction(QSettings* set, QAction* action) { } void Application::loadSettings(QSettings* settings) { - if (!settings) { + if (!settings) { settings = getSettings(); } - _headCameraPitchYawScale = loadSetting(settings, "headCameraPitchYawScale", 0.0f); + _gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f); _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); _horizontalFieldOfView = loadSetting(settings, "horizontalFieldOfView", HORIZONTAL_FIELD_OF_VIEW_DEGREES); @@ -3982,7 +4149,7 @@ void Application::saveSettings(QSettings* settings) { settings = getSettings(); } - settings->setValue("headCameraPitchYawScale", _headCameraPitchYawScale); + settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity); settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); settings->setValue("horizontalFieldOfView", _horizontalFieldOfView); settings->beginGroup("View Frustum Offset Camera"); diff --git a/interface/src/Application.h b/interface/src/Application.h index cb851c1dfc..d9c945f34e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -31,17 +31,19 @@ #include "Environment.h" #include "PacketHeaders.h" #include "ParticleSystem.h" -#include "renderer/GeometryCache.h" #include "SerialInterface.h" #include "Stars.h" #include "Swatch.h" #include "ToolsPalette.h" #include "ViewFrustum.h" +#include "VoxelFade.h" #include "VoxelSystem.h" #include "Webcam.h" #include "PieMenu.h" #include "avatar/Avatar.h" #include "avatar/HandControl.h" +#include "renderer/GeometryCache.h" +#include "renderer/TextureCache.h" #include "ui/BandwidthDialog.h" #include "ui/ChatEntry.h" #include "ui/VoxelStatsDialog.h" @@ -59,13 +61,23 @@ class QWheelEvent; class Node; class ProgramObject; -class Application : public QApplication { +static const float NODE_ADDED_RED = 0.0f; +static const float NODE_ADDED_GREEN = 1.0f; +static const float NODE_ADDED_BLUE = 0.0f; +static const float NODE_KILLED_RED = 1.0f; +static const float NODE_KILLED_GREEN = 0.0f; +static const float NODE_KILLED_BLUE = 0.0f; + + + +class Application : public QApplication, public NodeListHook { Q_OBJECT public: static Application* getInstance() { return static_cast(QCoreApplication::instance()); } Application(int& argc, char** argv, timeval &startup_time); + ~Application(); void initializeGL(); void paintGL(); @@ -105,8 +117,13 @@ public: QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } + TextureCache* getTextureCache() { return &_textureCache; } void resetSongMixMenuItem(); + void setupWorldLight(Camera& whichCamera); + + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -179,6 +196,7 @@ private slots: void setListenModePoint(); void setListenModeSingleSource(); void toggleMixedSong(); + void toggleWantCollisionsOn(); void renderCoverageMap(); @@ -216,6 +234,7 @@ private: bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera); + void renderFollowIndicator(); void updateAvatar(float deltaTime); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -233,6 +252,7 @@ private: void deleteVoxelUnderCursor(); void eyedropperVoxelUnderCursor(); void resetSensors(); + void injectVoxelAddedSoundEffect(); void setMenuShortcutsEnabled(bool enabled); @@ -294,6 +314,7 @@ private: QAction* _rawAudioMicrophoneMix; // Mixing of a RAW audio file with microphone stream for rave gloves QAction* _noise; QAction* _occlusionCulling; + QAction* _wantCollisionsOn; QAction* _renderCoverageMapV2; QAction* _renderCoverageMap; @@ -358,7 +379,7 @@ private: Environment _environment; int _headMouseX, _headMouseY; - float _headCameraPitchYawScale; + float _gyroCameraSensitivity; int _audioJitterBufferSamples; // Number of extra samples to wait before starting audio playback @@ -419,6 +440,7 @@ private: int _hmdWarpParamLocation; GeometryCache _geometryCache; + TextureCache _textureCache; ParticleSystem _particleSystem; @@ -445,6 +467,11 @@ private: PieMenu _pieMenu; VoxelSceneStats _voxelSceneStats; + int parseVoxelStats(unsigned char* messageData, ssize_t messageLength, sockaddr senderAddress); + + std::map _voxelServerJurisdictions; + + std::vector _voxelFades; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/VoxelFade.cpp b/interface/src/VoxelFade.cpp new file mode 100644 index 0000000000..0bbcbeedc1 --- /dev/null +++ b/interface/src/VoxelFade.cpp @@ -0,0 +1,58 @@ +// +// VoxelFade.cpp +// interface +// +// Created by Brad Hefta-Gaub on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include "InterfaceConfig.h" + +#include + +#include "VoxelFade.h" + +const float VoxelFade::FADE_OUT_START = 0.5f; +const float VoxelFade::FADE_OUT_END = 0.0f; +const float VoxelFade::FADE_OUT_STEP = -0.005f; +const float VoxelFade::FADE_IN_START = 0.0f; +const float VoxelFade::FADE_IN_END = 0.5f; +const float VoxelFade::FADE_IN_STEP = 0.005f; +const float VoxelFade::DEFAULT_RED = 0.5f; +const float VoxelFade::DEFAULT_GREEN = 0.5f; +const float VoxelFade::DEFAULT_BLUE = 0.5f; + +VoxelFade::VoxelFade(FadeDirection direction, float red, float green, float blue) : + direction(direction), + red(red), + green(green), + blue(blue) +{ + opacity = (direction == FADE_OUT) ? FADE_OUT_START : FADE_IN_START; +} + +void VoxelFade::render() { + glDisable(GL_LIGHTING); + glPushMatrix(); + glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); + glColor4f(red, green, blue, opacity); + glTranslatef(voxelDetails.x + voxelDetails.s * 0.5f, + voxelDetails.y + voxelDetails.s * 0.5f, + voxelDetails.z + voxelDetails.s * 0.5f); + glLineWidth(1.0f); + glutSolidCube(voxelDetails.s); + glLineWidth(1.0f); + glPopMatrix(); + glEnable(GL_LIGHTING); + + opacity += (direction == FADE_OUT) ? FADE_OUT_STEP : FADE_IN_STEP; +} + +bool VoxelFade::isDone() const { + if (direction == FADE_OUT) { + return opacity <= FADE_OUT_END; + } else { + return opacity >= FADE_IN_END; + } + return true; // unexpected case, assume we're done +} \ No newline at end of file diff --git a/interface/src/VoxelFade.h b/interface/src/VoxelFade.h new file mode 100644 index 0000000000..fd7a73135b --- /dev/null +++ b/interface/src/VoxelFade.h @@ -0,0 +1,43 @@ +// +// VoxelFade.h +// interface +// +// Created by Brad Hefta-Gaub on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__VoxelFade__ +#define __interface__VoxelFade__ + +#include // for VoxelPositionSize + +class VoxelFade { +public: + + enum FadeDirection { FADE_OUT, FADE_IN}; + static const float FADE_OUT_START; + static const float FADE_OUT_END; + static const float FADE_OUT_STEP; + static const float FADE_IN_START; + static const float FADE_IN_END; + static const float FADE_IN_STEP; + static const float DEFAULT_RED; + static const float DEFAULT_GREEN; + static const float DEFAULT_BLUE; + + VoxelPositionSize voxelDetails; + FadeDirection direction; + float opacity; + + float red; + float green; + float blue; + + VoxelFade(FadeDirection direction = FADE_OUT, float red = DEFAULT_RED, + float green = DEFAULT_GREEN, float blue = DEFAULT_BLUE); + + void render(); + bool isDone() const; +}; + +#endif // __interface__VoxelFade__ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 65d819eddc..bc9502b0ee 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -17,8 +17,6 @@ #include // to load voxels from file #include -#include - #include #include #include @@ -493,7 +491,6 @@ glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float vo } ProgramObject* VoxelSystem::_perlinModulateProgram = 0; -GLuint VoxelSystem::_permutationNormalTextureID = 0; void VoxelSystem::init() { @@ -585,29 +582,9 @@ void VoxelSystem::init() { _perlinModulateProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); _perlinModulateProgram->link(); + _perlinModulateProgram->bind(); _perlinModulateProgram->setUniformValue("permutationNormalTexture", 0); - - // create the permutation/normal texture - glGenTextures(1, &_permutationNormalTextureID); - glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID); - - // the first line consists of random permutation offsets - unsigned char data[256 * 2 * 3]; - for (int i = 0; i < 256 * 3; i++) { - data[i] = rand() % 256; - } - // the next, random unit normals - for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { - glm::vec3 randvec = glm::sphericalRand(1.0f); - data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f; - data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f; - data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; - } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_2D, 0); + _perlinModulateProgram->release(); } void VoxelSystem::updateFullVBOs() { @@ -734,7 +711,7 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { if (texture) { _perlinModulateProgram->bind(); - glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID); + glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); } } @@ -1553,10 +1530,38 @@ void VoxelSystem::falseColorizeOccludedV2() { VoxelProjectedPolygon::intersects_calls ); //myCoverageMapV2.erase(); - - _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } +void VoxelSystem::nodeAdded(Node* node) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + uint16_t nodeID = node->getNodeID(); + printf("VoxelSystem... voxel server %u added...\n", nodeID); + } +} + +bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) { + uint16_t killedNodeID = *(uint16_t*)extraData; + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + VoxelNode* childNode = node->getChildAtIndex(i); + if (childNode && childNode->getSourceID()== killedNodeID) { + node->safeDeepDeleteChildAtIndex(i); + } + } + return true; +} + +void VoxelSystem::nodeKilled(Node* node) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + uint16_t nodeID = node->getNodeID(); + printf("VoxelSystem... voxel server %u removed...\n", nodeID); + + // Kill any voxels from the local tree + _tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeID); + _tree->setDirtyBit(); + setupNewVoxelsForDrawing(); + } +} + diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 6c6bf261f9..60396adcb0 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -28,7 +28,7 @@ class ProgramObject; const int NUM_CHILDREN = 8; -class VoxelSystem : public NodeData, public VoxelNodeDeleteHook { +class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook { public: VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); @@ -99,6 +99,8 @@ public: CoverageMap myCoverageMap; virtual void nodeDeleted(VoxelNode* node); + virtual void nodeAdded(Node* node); + virtual void nodeKilled(Node* node); protected: float _treeScale; @@ -140,6 +142,7 @@ private: static bool falseColorizeSubTreeOperation(VoxelNode* node, void* extraData); static bool falseColorizeOccludedV2Operation(VoxelNode* node, void* extraData); static bool falseColorizeBySourceOperation(VoxelNode* node, void* extraData); + static bool killSourceVoxelsOperation(VoxelNode* node, void* extraData); int updateNodeInArraysAsFullVBO(VoxelNode* node); int updateNodeInArraysAsPartialVBO(VoxelNode* node); @@ -193,7 +196,6 @@ private: bool _voxelsDirty; static ProgramObject* _perlinModulateProgram; - static GLuint _permutationNormalTextureID; int _hookID; std::vector _freeIndexes; diff --git a/interface/src/Webcam.cpp b/interface/src/Webcam.cpp index 4d2edcac37..0deae62e46 100644 --- a/interface/src/Webcam.cpp +++ b/interface/src/Webcam.cpp @@ -71,26 +71,28 @@ void Webcam::reset() { } void Webcam::renderPreview(int screenWidth, int screenHeight) { - if (_enabled && _colorTextureID != 0) { - glBindTexture(GL_TEXTURE_2D, _colorTextureID); + if (_enabled) { glEnable(GL_TEXTURE_2D); glColor3f(1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - const int PREVIEW_HEIGHT = 200; - int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height; - int top = screenHeight - 600; - int left = screenWidth - previewWidth - 10; - - glTexCoord2f(0, 0); - glVertex2f(left, top); - glTexCoord2f(1, 0); - glVertex2f(left + previewWidth, top); - glTexCoord2f(1, 1); - glVertex2f(left + previewWidth, top + PREVIEW_HEIGHT); - glTexCoord2f(0, 1); - glVertex2f(left, top + PREVIEW_HEIGHT); - glEnd(); - + + const int PREVIEW_HEIGHT = 200; + int previewWidth = _textureSize.width * PREVIEW_HEIGHT / _textureSize.height; + int top = screenHeight - 600; + int left = screenWidth - previewWidth - 10; + if (_colorTextureID != 0) { + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(left, top); + glTexCoord2f(1, 0); + glVertex2f(left + previewWidth, top); + glTexCoord2f(1, 1); + glVertex2f(left + previewWidth, top + PREVIEW_HEIGHT); + glTexCoord2f(0, 1); + glVertex2f(left, top + PREVIEW_HEIGHT); + glEnd(); + } + if (_depthTextureID != 0) { glBindTexture(GL_TEXTURE_2D, _depthTextureID); glBegin(GL_QUADS); @@ -157,22 +159,26 @@ const float METERS_PER_MM = 1.0f / 1000.0f; void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midFaceDepth, float aspectRatio, const RotatedRect& faceRect, bool sending, const JointVector& joints) { - IplImage colorImage = color; - glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3); - if (_colorTextureID == 0) { - glGenTextures(1, &_colorTextureID); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureSize.width = colorImage.width, _textureSize.height = colorImage.height, - 0, format, GL_UNSIGNED_BYTE, colorImage.imageData); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - qDebug("Capturing video at %gx%g.\n", _textureSize.width, _textureSize.height); - - } else { - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format, - GL_UNSIGNED_BYTE, colorImage.imageData); + if (!color.empty()) { + IplImage colorImage = color; + glPixelStorei(GL_UNPACK_ROW_LENGTH, colorImage.widthStep / 3); + if (_colorTextureID == 0) { + glGenTextures(1, &_colorTextureID); + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textureSize.width = colorImage.width, _textureSize.height = colorImage.height, + 0, format, GL_UNSIGNED_BYTE, colorImage.imageData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + } else { + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, format, + GL_UNSIGNED_BYTE, colorImage.imageData); + } + } else if (_colorTextureID != 0) { + glDeleteTextures(1, &_colorTextureID); + _colorTextureID = 0; } - + if (!depth.empty()) { IplImage depthImage = depth; glPixelStorei(GL_UNPACK_ROW_LENGTH, depthImage.widthStep); @@ -189,6 +195,9 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midF glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _textureSize.width, _textureSize.height, GL_LUMINANCE, GL_UNSIGNED_BYTE, depthImage.imageData); } + } else if (_depthTextureID != 0) { + glDeleteTextures(1, &_depthTextureID); + _depthTextureID = 0; } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glBindTexture(GL_TEXTURE_2D, 0); @@ -273,8 +282,8 @@ void Webcam::setFrame(const Mat& color, int format, const Mat& depth, float midF QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame())); } -FrameGrabber::FrameGrabber() : _initialized(false), _videoSendMode(FULL_FRAME_VIDEO), _capture(0), _searchWindow(0, 0, 0, 0), - _smoothedMidFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) { +FrameGrabber::FrameGrabber() : _initialized(false), _videoSendMode(FULL_FRAME_VIDEO), _depthOnly(false), _capture(0), + _searchWindow(0, 0, 0, 0), _smoothedMidFaceDepth(UNINITIALIZED_FACE_DEPTH), _colorCodec(), _depthCodec(), _frameCount(0) { } FrameGrabber::~FrameGrabber() { @@ -374,6 +383,11 @@ void FrameGrabber::cycleVideoSendMode() { destroyCodecs(); } +void FrameGrabber::setDepthOnly(bool depthOnly) { + _depthOnly = depthOnly; + destroyCodecs(); +} + void FrameGrabber::reset() { _searchWindow = cv::Rect(0, 0, 0, 0); @@ -479,7 +493,7 @@ void FrameGrabber::grabFrame() { encodedWidth = color.cols; encodedHeight = color.rows; aspectRatio = FULL_FRAME_ASPECT; - colorBitrateMultiplier = 4.0f; + colorBitrateMultiplier = depthBitrateMultiplier = 4.0f; } else { // if we don't have a search window (yet), try using the face cascade @@ -591,108 +605,129 @@ void FrameGrabber::grabFrame() { depth.convertTo(_grayDepthFrame, CV_8UC1, 1.0, depthOffset); } - QByteArray payload; + // increment the frame count that identifies frames + _frameCount++; + + QByteArray payload; if (_videoSendMode != NO_VIDEO) { - if (_colorCodec.name == 0) { - // initialize encoder context(s) - vpx_codec_enc_cfg_t codecConfig; - vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0); - codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * colorBitrateMultiplier * - codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; - codecConfig.g_w = encodedWidth; - codecConfig.g_h = encodedHeight; - vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0); - - if (!depth.empty()) { - codecConfig.rc_target_bitrate *= depthBitrateMultiplier; - vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0); - } - } - - Mat transform; - if (_videoSendMode == FACE_VIDEO) { - // resize/rotate face into encoding rectangle - _faceColor.create(encodedHeight, encodedWidth, CV_8UC3); - warpAffine(color, _faceColor, faceTransform, _faceColor.size()); - - } else { - _faceColor = color; - } - - // convert from RGB to YV12: see http://www.fourcc.org/yuv.php and - // http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor + // start the payload off with the aspect ratio (zero for full frame) + payload.append((const char*)&aspectRatio, sizeof(float)); + + // prepare the image in which we'll store the data const int ENCODED_BITS_PER_Y = 8; const int ENCODED_BITS_PER_VU = 2; const int ENCODED_BITS_PER_PIXEL = ENCODED_BITS_PER_Y + 2 * ENCODED_BITS_PER_VU; const int BITS_PER_BYTE = 8; _encodedFace.resize(encodedWidth * encodedHeight * ENCODED_BITS_PER_PIXEL / BITS_PER_BYTE); vpx_image_t vpxImage; - vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, encodedWidth, encodedHeight, 1, - (unsigned char*)_encodedFace.data()); - uchar* yline = vpxImage.planes[0]; - uchar* vline = vpxImage.planes[1]; - uchar* uline = vpxImage.planes[2]; - const int Y_RED_WEIGHT = (int)(0.299 * 256); - const int Y_GREEN_WEIGHT = (int)(0.587 * 256); - const int Y_BLUE_WEIGHT = (int)(0.114 * 256); - const int V_RED_WEIGHT = (int)(0.713 * 256); - const int U_BLUE_WEIGHT = (int)(0.564 * 256); - int redIndex = 0; - int greenIndex = 1; - int blueIndex = 2; - if (format == GL_BGR) { - redIndex = 2; - blueIndex = 0; - } - for (int i = 0; i < encodedHeight; i += 2) { - uchar* ydest = yline; - uchar* vdest = vline; - uchar* udest = uline; - for (int j = 0; j < encodedWidth; j += 2) { - uchar* tl = _faceColor.ptr(i, j); - uchar* tr = _faceColor.ptr(i, j + 1); - uchar* bl = _faceColor.ptr(i + 1, j); - uchar* br = _faceColor.ptr(i + 1, j + 1); - - ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] * - Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] * - Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8; - ydest += 2; - - int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex]; - int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex]; - int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex]; - int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8; - - *vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128; - *udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128; + vpx_img_wrap(&vpxImage, VPX_IMG_FMT_YV12, encodedWidth, encodedHeight, 1, (unsigned char*)_encodedFace.data()); + + if (!_depthOnly || depth.empty()) { + if (_colorCodec.name == 0) { + // initialize encoder context + vpx_codec_enc_cfg_t codecConfig; + vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0); + codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * colorBitrateMultiplier * + codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; + codecConfig.g_w = encodedWidth; + codecConfig.g_h = encodedHeight; + vpx_codec_enc_init(&_colorCodec, vpx_codec_vp8_cx(), &codecConfig, 0); } - yline += vpxImage.stride[0] * 2; - vline += vpxImage.stride[1]; - uline += vpxImage.stride[2]; - } - // encode the frame - vpx_codec_encode(&_colorCodec, &vpxImage, ++_frameCount, 1, 0, VPX_DL_REALTIME); + if (_videoSendMode == FACE_VIDEO) { + // resize/rotate face into encoding rectangle + _faceColor.create(encodedHeight, encodedWidth, CV_8UC3); + warpAffine(color, _faceColor, faceTransform, _faceColor.size()); - // start the payload off with the aspect ratio (zero for full frame) - payload.append((const char*)&aspectRatio, sizeof(float)); - - // extract the encoded frame - vpx_codec_iter_t iterator = 0; - const vpx_codec_cx_pkt_t* packet; - while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) { - if (packet->kind == VPX_CODEC_CX_FRAME_PKT) { - // prepend the length, which will indicate whether there's a depth frame too - payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz)); - payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz); + } else { + _faceColor = color; } - } + // convert from RGB to YV12: see http://www.fourcc.org/yuv.php and + // http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor + uchar* yline = vpxImage.planes[0]; + uchar* vline = vpxImage.planes[1]; + uchar* uline = vpxImage.planes[2]; + const int Y_RED_WEIGHT = (int)(0.299 * 256); + const int Y_GREEN_WEIGHT = (int)(0.587 * 256); + const int Y_BLUE_WEIGHT = (int)(0.114 * 256); + const int V_RED_WEIGHT = (int)(0.713 * 256); + const int U_BLUE_WEIGHT = (int)(0.564 * 256); + int redIndex = 0; + int greenIndex = 1; + int blueIndex = 2; + if (format == GL_BGR) { + redIndex = 2; + blueIndex = 0; + } + for (int i = 0; i < encodedHeight; i += 2) { + uchar* ydest = yline; + uchar* vdest = vline; + uchar* udest = uline; + for (int j = 0; j < encodedWidth; j += 2) { + uchar* tl = _faceColor.ptr(i, j); + uchar* tr = _faceColor.ptr(i, j + 1); + uchar* bl = _faceColor.ptr(i + 1, j); + uchar* br = _faceColor.ptr(i + 1, j + 1); + + ydest[0] = (tl[redIndex] * Y_RED_WEIGHT + tl[1] * Y_GREEN_WEIGHT + tl[blueIndex] * Y_BLUE_WEIGHT) >> 8; + ydest[1] = (tr[redIndex] * Y_RED_WEIGHT + tr[1] * Y_GREEN_WEIGHT + tr[blueIndex] * Y_BLUE_WEIGHT) >> 8; + ydest[vpxImage.stride[0]] = (bl[redIndex] * Y_RED_WEIGHT + bl[greenIndex] * + Y_GREEN_WEIGHT + bl[blueIndex] * Y_BLUE_WEIGHT) >> 8; + ydest[vpxImage.stride[0] + 1] = (br[redIndex] * Y_RED_WEIGHT + br[greenIndex] * + Y_GREEN_WEIGHT + br[blueIndex] * Y_BLUE_WEIGHT) >> 8; + ydest += 2; + + int totalRed = tl[redIndex] + tr[redIndex] + bl[redIndex] + br[redIndex]; + int totalGreen = tl[greenIndex] + tr[greenIndex] + bl[greenIndex] + br[greenIndex]; + int totalBlue = tl[blueIndex] + tr[blueIndex] + bl[blueIndex] + br[blueIndex]; + int totalY = (totalRed * Y_RED_WEIGHT + totalGreen * Y_GREEN_WEIGHT + totalBlue * Y_BLUE_WEIGHT) >> 8; + + *vdest++ = (((totalRed - totalY) * V_RED_WEIGHT) >> 10) + 128; + *udest++ = (((totalBlue - totalY) * U_BLUE_WEIGHT) >> 10) + 128; + } + yline += vpxImage.stride[0] * 2; + vline += vpxImage.stride[1]; + uline += vpxImage.stride[2]; + } + + // encode the frame + vpx_codec_encode(&_colorCodec, &vpxImage, _frameCount, 1, 0, VPX_DL_REALTIME); + + // extract the encoded frame + vpx_codec_iter_t iterator = 0; + const vpx_codec_cx_pkt_t* packet; + while ((packet = vpx_codec_get_cx_data(&_colorCodec, &iterator)) != 0) { + if (packet->kind == VPX_CODEC_CX_FRAME_PKT) { + // prepend the length, which will indicate whether there's a depth frame too + payload.append((const char*)&packet->data.frame.sz, sizeof(packet->data.frame.sz)); + payload.append((const char*)packet->data.frame.buf, packet->data.frame.sz); + } + } + } else { + // zero length indicates no color info + const size_t ZERO_SIZE = 0; + payload.append((const char*)&ZERO_SIZE, sizeof(size_t)); + + // we can use more bits for depth + depthBitrateMultiplier *= 2.0f; + + // don't bother reporting the color + color = Mat(); + } + if (!depth.empty()) { + if (_depthCodec.name == 0) { + // initialize encoder context + vpx_codec_enc_cfg_t codecConfig; + vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &codecConfig, 0); + codecConfig.rc_target_bitrate = ENCODED_FACE_WIDTH * ENCODED_FACE_HEIGHT * depthBitrateMultiplier * + codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; + codecConfig.g_w = encodedWidth; + codecConfig.g_h = encodedHeight; + vpx_codec_enc_init(&_depthCodec, vpx_codec_vp8_cx(), &codecConfig, 0); + } + // convert with mask uchar* yline = vpxImage.planes[0]; uchar* vline = vpxImage.planes[1]; diff --git a/interface/src/Webcam.h b/interface/src/Webcam.h index aee87e1bc7..16b9339eb0 100644 --- a/interface/src/Webcam.h +++ b/interface/src/Webcam.h @@ -112,6 +112,7 @@ public: public slots: void cycleVideoSendMode(); + void setDepthOnly(bool depthOnly); void reset(); void shutdown(); void grabFrame(); @@ -126,6 +127,7 @@ private: bool _initialized; VideoSendMode _videoSendMode; + bool _depthOnly; CvCapture* _capture; cv::CascadeClassifier _faceCascade; cv::Mat _hsvFrame; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 26192ffe5a..1f1609d401 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -101,6 +101,7 @@ Avatar::Avatar(Node* owningNode) : _lastCollisionPosition(0, 0, 0), _speedBrakes(false), _isThrustOn(false), + _isCollisionsOn(true), _leadingAvatar(NULL), _voxels(this) { @@ -294,9 +295,8 @@ void Avatar::reset() { // Update avatar head rotation with sensor data void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, - const glm::vec3& amplifyAngle, - float yawFromTouch, float pitchFromTouch) { + _head.setMousePitch(pitchFromTouch); SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor(); Webcam* webcam = Application::getInstance()->getWebcam(); glm::vec3 estimatedPosition, estimatedRotation; @@ -308,7 +308,6 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, } else { _head.setPitch(pitchFromTouch); - _head.setYaw(yawFromTouch); return; } if (webcam->isActive()) { @@ -334,9 +333,15 @@ void Avatar::updateFromGyrosAndOrWebcam(bool gyroLook, } else { _head.getFace().clearFrame(); } - _head.setPitch(estimatedRotation.x * amplifyAngle.x + pitchFromTouch); - _head.setYaw(estimatedRotation.y * amplifyAngle.y + yawFromTouch); - _head.setRoll(estimatedRotation.z * amplifyAngle.z); + + // Set the rotation of the avatar's head (as seen by others, not affecting view frustum) + // to be scaled. Pitch is greater to emphasize nodding behavior / synchrony. + const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f; + const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f; + const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f; + _head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); + _head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); + _head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); _head.setCameraFollowsHead(gyroLook); // Update torso lean distance based on accelerometer data @@ -391,7 +396,7 @@ void Avatar::updateThrust(float deltaTime, Transmitter * transmitter) { // // Gather thrust information from keyboard and sensors to apply to avatar motion // - glm::quat orientation = getHead().getOrientation(); + glm::quat orientation = getHead().getCameraOrientation(); glm::vec3 front = orientation * IDENTITY_FRONT; glm::vec3 right = orientation * IDENTITY_RIGHT; glm::vec3 up = orientation * IDENTITY_UP; @@ -493,14 +498,17 @@ void Avatar::follow(Avatar* leadingAvatar) { _leadingAvatar = leadingAvatar; if (_leadingAvatar != NULL) { + _leaderID = leadingAvatar->getOwningNode()->getNodeID(); _stringLength = glm::length(_position - _leadingAvatar->getPosition()) / _scale; if (_stringLength > MAX_STRING_LENGTH) { _stringLength = MAX_STRING_LENGTH; } + } else { + _leaderID = UNKNOWN_NODE_ID; } } -void Avatar::simulate(float deltaTime, Transmitter* transmitter) { +void Avatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity) { glm::quat orientation = getOrientation(); glm::vec3 front = orientation * IDENTITY_FRONT; @@ -623,9 +631,12 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); } } - updateCollisionWithEnvironment(deltaTime); - updateCollisionWithVoxels(deltaTime); - updateAvatarCollisions(deltaTime); + + if (_isCollisionsOn) { + updateCollisionWithEnvironment(deltaTime); + updateCollisionWithVoxels(deltaTime); + updateAvatarCollisions(deltaTime); + } } // update body balls @@ -633,7 +644,7 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { // test for avatar collision response with the big sphere - if (usingBigSphereCollisionTest) { + if (usingBigSphereCollisionTest && _isCollisionsOn) { updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime); } @@ -747,7 +758,9 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) { _head.setPosition(_bodyBall[ BODY_BALL_HEAD_BASE ].position); _head.setScale(_scale); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); - _head.simulate(deltaTime, isMyAvatar()); + _head.simulate(deltaTime, isMyAvatar(), gyroCameraSensitivity); + _hand.simulate(deltaTime, isMyAvatar()); + @@ -1146,6 +1159,10 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) { glPopMatrix(); } + if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) { + _hand.setRaveLights(RAVE_LIGHTS_AVATAR); + } + // render a simple round on the ground projected down from the avatar's position renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f); @@ -1207,6 +1224,21 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) { } } +void Avatar::renderScreenTint(ScreenTintLayer layer, Camera& whichCamera) { + + if (layer == SCREEN_TINT_BEFORE_AVATARS) { + if (_hand.isRaveGloveActive()) { + _hand.renderRaveGloveStage(); + } + } + else if (layer == SCREEN_TINT_BEFORE_AVATARS) { + if (_hand.isRaveGloveActive()) { + // Restore the world lighting + Application::getInstance()->setupWorldLight(whichCamera); + } + } +} + void Avatar::resetBodyBalls() { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) { @@ -1372,6 +1404,17 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) { float alpha = getBallRenderAlpha(b, lookingInMirror); + // When in rave glove mode, don't show the arms at all. + if (_hand.isRaveGloveActive()) { + if (b == BODY_BALL_LEFT_ELBOW + || b == BODY_BALL_LEFT_WRIST + || b == BODY_BALL_LEFT_FINGERTIPS + || b == BODY_BALL_RIGHT_ELBOW + || b == BODY_BALL_RIGHT_WRIST + || b == BODY_BALL_RIGHT_FINGERTIPS) { + continue; + } + } // Always render other people, and render myself when beyond threshold distance if (b == BODY_BALL_HEAD_BASE) { // the head is rendered as a special if (alpha > 0.0f) { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b490429ec9..ab122ee81b 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -27,7 +27,7 @@ #include "world.h" -static const float MAX_SCALE = 5.f; +static const float MAX_SCALE = 10.f; static const float MIN_SCALE = .5f; static const float SCALING_RATIO = .05f; static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1 @@ -112,6 +112,15 @@ enum AvatarMode NUM_AVATAR_MODES }; +enum ScreenTintLayer +{ + SCREEN_TINT_BEFORE_LANDSCAPE = 0, + SCREEN_TINT_BEFORE_AVATARS, + SCREEN_TINT_BEFORE_MY_AVATAR, + SCREEN_TINT_AFTER_AVATARS, + NUM_SCREEN_TINT_LAYERS +}; + class Avatar : public AvatarData { public: Avatar(Node* owningNode = NULL); @@ -119,16 +128,15 @@ public: void init(); void reset(); - void simulate(float deltaTime, Transmitter* transmitter); + void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity); void updateThrust(float deltaTime, Transmitter * transmitter); void follow(Avatar* leadingAvatar); void updateFromGyrosAndOrWebcam(bool gyroLook, - const glm::vec3& amplifyAngle, - float yawFromTouch, float pitchFromTouch); void addBodyYaw(float bodyYaw) {_bodyYaw += bodyYaw;}; void addBodyYawDelta(float bodyYawDelta) {_bodyYawDelta += bodyYawDelta;} void render(bool lookingInMirror, bool renderAvatarBalls); + void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera); //setters void setMousePressed (bool mousePressed ) { _mousePressed = mousePressed;} @@ -142,6 +150,7 @@ public: void setMouseRay (const glm::vec3 &origin, const glm::vec3 &direction); void setOrientation (const glm::quat& orientation); void setNewScale (const float scale); + void setWantCollisionsOn (bool wantCollisionsOn ) { _isCollisionsOn = wantCollisionsOn; } //getters bool isInitialized () const { return _initialized;} @@ -263,6 +272,7 @@ private: glm::vec3 _lastCollisionPosition; bool _speedBrakes; bool _isThrustOn; + bool _isCollisionsOn; Avatar* _leadingAvatar; float _stringLength; diff --git a/interface/src/avatar/Face.cpp b/interface/src/avatar/Face.cpp index ffc88d4b5b..22ce3c0727 100644 --- a/interface/src/avatar/Face.cpp +++ b/interface/src/avatar/Face.cpp @@ -22,10 +22,10 @@ using namespace cv; -ProgramObject* Face::_program = 0; -int Face::_texCoordCornerLocation; -int Face::_texCoordRightLocation; -int Face::_texCoordUpLocation; +ProgramObject* Face::_videoProgram = 0; +Face::Locations Face::_videoProgramLocations; +ProgramObject* Face::_texturedProgram = 0; +Face::Locations Face::_texturedProgramLocations; GLuint Face::_vboID; GLuint Face::_iboID; @@ -71,6 +71,7 @@ void Face::setFrameFromWebcam() { void Face::clearFrame() { _colorTextureID = 0; + _depthTextureID = 0; } int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { @@ -111,128 +112,140 @@ int Face::processVideoMessage(unsigned char* packetData, size_t dataBytes) { return dataBytes; } - // the switch from full frame to not (or vice versa) requires us to reinit the codecs + // the switch between full frame or depth only modes requires us to reinit the codecs float aspectRatio = *(const float*)_arrivingFrame.constData(); + size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float)); bool fullFrame = (aspectRatio == FULL_FRAME_ASPECT); - if (fullFrame != _lastFullFrame) { + bool depthOnly = (colorSize == 0); + if (fullFrame != _lastFullFrame || depthOnly != _lastDepthOnly) { destroyCodecs(); _lastFullFrame = fullFrame; + _lastDepthOnly = depthOnly; } - if (_colorCodec.name == 0) { - // initialize decoder context - vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0); - } - - size_t colorSize = *(const size_t*)(_arrivingFrame.constData() + sizeof(float)); + // read the color data, if non-empty + Mat color; const uint8_t* colorData = (const uint8_t*)(_arrivingFrame.constData() + sizeof(float) + sizeof(size_t)); - vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0); - vpx_codec_iter_t iterator = 0; - vpx_image_t* image; - while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) { - // convert from YV12 to RGB: see http://www.fourcc.org/yuv.php and - // http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor - Mat color(image->d_h, image->d_w, CV_8UC3); - uchar* yline = image->planes[0]; - uchar* vline = image->planes[1]; - uchar* uline = image->planes[2]; - const int RED_V_WEIGHT = (int)(1.403 * 256); - const int GREEN_V_WEIGHT = (int)(0.714 * 256); - const int GREEN_U_WEIGHT = (int)(0.344 * 256); - const int BLUE_U_WEIGHT = (int)(1.773 * 256); - for (int i = 0; i < image->d_h; i += 2) { - uchar* ysrc = yline; - uchar* vsrc = vline; - uchar* usrc = uline; - for (int j = 0; j < image->d_w; j += 2) { - uchar* tl = color.ptr(i, j); - uchar* tr = color.ptr(i, j + 1); - uchar* bl = color.ptr(i + 1, j); - uchar* br = color.ptr(i + 1, j + 1); - - int v = *vsrc++ - 128; - int u = *usrc++ - 128; - - int redOffset = (RED_V_WEIGHT * v) >> 8; - int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8; - int blueOffset = (BLUE_U_WEIGHT * u) >> 8; - - int ytl = ysrc[0]; - int ytr = ysrc[1]; - int ybl = ysrc[image->w]; - int ybr = ysrc[image->w + 1]; - ysrc += 2; - - tl[0] = saturate_cast(ytl + redOffset); - tl[1] = saturate_cast(ytl - greenOffset); - tl[2] = saturate_cast(ytl + blueOffset); - - tr[0] = saturate_cast(ytr + redOffset); - tr[1] = saturate_cast(ytr - greenOffset); - tr[2] = saturate_cast(ytr + blueOffset); - - bl[0] = saturate_cast(ybl + redOffset); - bl[1] = saturate_cast(ybl - greenOffset); - bl[2] = saturate_cast(ybl + blueOffset); - - br[0] = saturate_cast(ybr + redOffset); - br[1] = saturate_cast(ybr - greenOffset); - br[2] = saturate_cast(ybr + blueOffset); - } - yline += image->stride[0] * 2; - vline += image->stride[1]; - uline += image->stride[2]; + if (colorSize > 0) { + if (_colorCodec.name == 0) { + // initialize decoder context + vpx_codec_dec_init(&_colorCodec, vpx_codec_vp8_dx(), 0, 0); } - Mat depth; - - const uint8_t* depthData = colorData + colorSize; - int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData()); - if (depthSize > 0) { - if (_depthCodec.name == 0) { - // initialize decoder context - vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0); - } - vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0); - vpx_codec_iter_t iterator = 0; - vpx_image_t* image; - while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) { - depth.create(image->d_h, image->d_w, CV_8UC1); - uchar* yline = image->planes[0]; - uchar* vline = image->planes[1]; - const uchar EIGHT_BIT_MAXIMUM = 255; - const uchar MASK_THRESHOLD = 192; - for (int i = 0; i < image->d_h; i += 2) { - uchar* ysrc = yline; - uchar* vsrc = vline; - for (int j = 0; j < image->d_w; j += 2) { - if (*vsrc++ < MASK_THRESHOLD) { - *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM; - *depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM; - - } else { - *depth.ptr(i, j) = ysrc[0]; - *depth.ptr(i, j + 1) = ysrc[1]; - *depth.ptr(i + 1, j) = ysrc[image->stride[0]]; - *depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1]; - } - ysrc += 2; - } - yline += image->stride[0] * 2; - vline += image->stride[1]; + vpx_codec_decode(&_colorCodec, colorData, colorSize, 0, 0); + vpx_codec_iter_t iterator = 0; + vpx_image_t* image; + while ((image = vpx_codec_get_frame(&_colorCodec, &iterator)) != 0) { + // convert from YV12 to RGB: see http://www.fourcc.org/yuv.php and + // http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#cvtcolor + color.create(image->d_h, image->d_w, CV_8UC3); + uchar* yline = image->planes[0]; + uchar* vline = image->planes[1]; + uchar* uline = image->planes[2]; + const int RED_V_WEIGHT = (int)(1.403 * 256); + const int GREEN_V_WEIGHT = (int)(0.714 * 256); + const int GREEN_U_WEIGHT = (int)(0.344 * 256); + const int BLUE_U_WEIGHT = (int)(1.773 * 256); + for (int i = 0; i < image->d_h; i += 2) { + uchar* ysrc = yline; + uchar* vsrc = vline; + uchar* usrc = uline; + for (int j = 0; j < image->d_w; j += 2) { + uchar* tl = color.ptr(i, j); + uchar* tr = color.ptr(i, j + 1); + uchar* bl = color.ptr(i + 1, j); + uchar* br = color.ptr(i + 1, j + 1); + + int v = *vsrc++ - 128; + int u = *usrc++ - 128; + + int redOffset = (RED_V_WEIGHT * v) >> 8; + int greenOffset = (GREEN_V_WEIGHT * v + GREEN_U_WEIGHT * u) >> 8; + int blueOffset = (BLUE_U_WEIGHT * u) >> 8; + + int ytl = ysrc[0]; + int ytr = ysrc[1]; + int ybl = ysrc[image->w]; + int ybr = ysrc[image->w + 1]; + ysrc += 2; + + tl[0] = saturate_cast(ytl + redOffset); + tl[1] = saturate_cast(ytl - greenOffset); + tl[2] = saturate_cast(ytl + blueOffset); + + tr[0] = saturate_cast(ytr + redOffset); + tr[1] = saturate_cast(ytr - greenOffset); + tr[2] = saturate_cast(ytr + blueOffset); + + bl[0] = saturate_cast(ybl + redOffset); + bl[1] = saturate_cast(ybl - greenOffset); + bl[2] = saturate_cast(ybl + blueOffset); + + br[0] = saturate_cast(ybr + redOffset); + br[1] = saturate_cast(ybr - greenOffset); + br[2] = saturate_cast(ybr + blueOffset); } + yline += image->stride[0] * 2; + vline += image->stride[1]; + uline += image->stride[2]; } } - QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color), - Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio)); + } else if (_colorCodec.name != 0) { + vpx_codec_destroy(&_colorCodec); + _colorCodec.name = 0; } + // read the depth data, if non-empty + Mat depth; + const uint8_t* depthData = colorData + colorSize; + int depthSize = _arrivingFrame.size() - ((const char*)depthData - _arrivingFrame.constData()); + if (depthSize > 0) { + if (_depthCodec.name == 0) { + // initialize decoder context + vpx_codec_dec_init(&_depthCodec, vpx_codec_vp8_dx(), 0, 0); + } + vpx_codec_decode(&_depthCodec, depthData, depthSize, 0, 0); + vpx_codec_iter_t iterator = 0; + vpx_image_t* image; + while ((image = vpx_codec_get_frame(&_depthCodec, &iterator)) != 0) { + depth.create(image->d_h, image->d_w, CV_8UC1); + uchar* yline = image->planes[0]; + uchar* vline = image->planes[1]; + const uchar EIGHT_BIT_MAXIMUM = 255; + const uchar MASK_THRESHOLD = 192; + for (int i = 0; i < image->d_h; i += 2) { + uchar* ysrc = yline; + uchar* vsrc = vline; + for (int j = 0; j < image->d_w; j += 2) { + if (*vsrc++ < MASK_THRESHOLD) { + *depth.ptr(i, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i, j + 1) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j) = EIGHT_BIT_MAXIMUM; + *depth.ptr(i + 1, j + 1) = EIGHT_BIT_MAXIMUM; + + } else { + *depth.ptr(i, j) = ysrc[0]; + *depth.ptr(i, j + 1) = ysrc[1]; + *depth.ptr(i + 1, j) = ysrc[image->stride[0]]; + *depth.ptr(i + 1, j + 1) = ysrc[image->stride[0] + 1]; + } + ysrc += 2; + } + yline += image->stride[0] * 2; + vline += image->stride[1]; + } + } + } else if (_depthCodec.name != 0) { + vpx_codec_destroy(&_depthCodec); + _depthCodec.name = 0; + } + QMetaObject::invokeMethod(this, "setFrame", Q_ARG(cv::Mat, color), + Q_ARG(cv::Mat, depth), Q_ARG(float, aspectRatio)); + return dataBytes; } bool Face::render(float alpha) { - if (_colorTextureID == 0 || _textureRect.size.area() == 0) { + if (!isActive()) { return false; } glPushMatrix(); @@ -275,20 +288,9 @@ bool Face::render(float alpha) { const int INDICES_PER_TRIANGLE = 3; const int INDEX_COUNT = QUAD_COUNT * TRIANGLES_PER_QUAD * INDICES_PER_TRIANGLE; - if (_program == 0) { - _program = new ProgramObject(); - _program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/face.vert"); - _program->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/face.frag"); - _program->link(); - - _program->bind(); - _program->setUniformValue("depthTexture", 0); - _program->setUniformValue("colorTexture", 1); - _program->release(); - - _texCoordCornerLocation = _program->uniformLocation("texCoordCorner"); - _texCoordRightLocation = _program->uniformLocation("texCoordRight"); - _texCoordUpLocation = _program->uniformLocation("texCoordUp"); + if (_videoProgram == 0) { + _videoProgram = loadProgram(QString(), "colorTexture", _videoProgramLocations); + _texturedProgram = loadProgram("_textured", "permutationNormalTexture", _texturedProgramLocations); glGenBuffers(1, &_vboID); glBindBuffer(GL_ARRAY_BUFFER, _vboID); @@ -328,14 +330,23 @@ bool Face::render(float alpha) { glBindTexture(GL_TEXTURE_2D, _depthTextureID); glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - _program->bind(); - _program->setUniformValue(_texCoordCornerLocation, + ProgramObject* program = _videoProgram; + Locations* locations = &_videoProgramLocations; + if (_colorTextureID != 0) { + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); + program = _texturedProgram; + locations = &_texturedProgramLocations; + } + program->bind(); + program->setUniformValue(locations->texCoordCorner, points[0].x / _textureSize.width, points[0].y / _textureSize.height); - _program->setUniformValue(_texCoordRightLocation, + program->setUniformValue(locations->texCoordRight, (points[3].x - points[0].x) / _textureSize.width, (points[3].y - points[0].y) / _textureSize.height); - _program->setUniformValue(_texCoordUpLocation, + program->setUniformValue(locations->texCoordUp, (points[1].x - points[0].x) / _textureSize.width, (points[1].y - points[0].y) / _textureSize.height); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, 0); @@ -357,7 +368,7 @@ bool Face::render(float alpha) { glDisableClientState(GL_VERTEX_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, 0); - _program->release(); + program->release(); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); @@ -392,53 +403,52 @@ void Face::cycleRenderMode() { } void Face::setFrame(const cv::Mat& color, const cv::Mat& depth, float aspectRatio) { - if (color.empty()) { - // release our textures, if any; there's no more video - if (_colorTextureID != 0) { - glDeleteTextures(1, &_colorTextureID); - _colorTextureID = 0; + Size2f textureSize = _textureSize; + if (!color.empty()) { + bool generate = (_colorTextureID == 0); + if (generate) { + glGenTextures(1, &_colorTextureID); } - if (_depthTextureID != 0) { - glDeleteTextures(1, &_depthTextureID); - _depthTextureID = 0; + glBindTexture(GL_TEXTURE_2D, _colorTextureID); + if (_textureSize.width != color.cols || _textureSize.height != color.rows || generate) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, color.cols, color.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, color.ptr()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + textureSize = color.size(); + _textureRect = RotatedRect(Point2f(color.cols * 0.5f, color.rows * 0.5f), textureSize, 0.0f); + + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, color.cols, color.rows, GL_RGB, GL_UNSIGNED_BYTE, color.ptr()); } - return; - } - - if (_colorTextureID == 0) { - glGenTextures(1, &_colorTextureID); - } - glBindTexture(GL_TEXTURE_2D, _colorTextureID); - bool recreateTextures = (_textureSize.width != color.cols || _textureSize.height != color.rows); - if (recreateTextures) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, color.cols, color.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, color.ptr()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - _textureSize = color.size(); - _textureRect = RotatedRect(Point2f(color.cols * 0.5f, color.rows * 0.5f), _textureSize, 0.0f); - - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, color.cols, color.rows, GL_RGB, GL_UNSIGNED_BYTE, color.ptr()); + } else if (_colorTextureID != 0) { + glDeleteTextures(1, &_colorTextureID); + _colorTextureID = 0; } if (!depth.empty()) { - if (_depthTextureID == 0) { + bool generate = (_depthTextureID == 0); + if (generate) { glGenTextures(1, &_depthTextureID); } glBindTexture(GL_TEXTURE_2D, _depthTextureID); - if (recreateTextures) { + if (_textureSize.width != depth.cols || _textureSize.height != depth.rows || generate) { glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, depth.cols, depth.rows, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, depth.ptr()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + textureSize = depth.size(); + _textureRect = RotatedRect(Point2f(depth.cols * 0.5f, depth.rows * 0.5f), textureSize, 0.0f); } else { - glBindTexture(GL_TEXTURE_2D, _depthTextureID); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, depth.cols, depth.rows, GL_LUMINANCE, GL_UNSIGNED_BYTE, depth.ptr()); } + } else if (_depthTextureID != 0) { + glDeleteTextures(1, &_depthTextureID); + _depthTextureID = 0; } glBindTexture(GL_TEXTURE_2D, 0); _aspectRatio = aspectRatio; + _textureSize = textureSize; } void Face::destroyCodecs() { @@ -451,3 +461,21 @@ void Face::destroyCodecs() { _depthCodec.name = 0; } } + +ProgramObject* Face::loadProgram(const QString& suffix, const char* secondTextureUniform, Locations& locations) { + ProgramObject* program = new ProgramObject(); + program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/face" + suffix + ".vert"); + program->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/face" + suffix + ".frag"); + program->link(); + + program->bind(); + program->setUniformValue("depthTexture", 0); + program->setUniformValue(secondTextureUniform, 1); + program->release(); + + locations.texCoordCorner = program->uniformLocation("texCoordCorner"); + locations.texCoordRight = program->uniformLocation("texCoordRight"); + locations.texCoordUp = program->uniformLocation("texCoordUp"); + + return program; +} diff --git a/interface/src/avatar/Face.h b/interface/src/avatar/Face.h index 893318f186..f3e681ff72 100644 --- a/interface/src/avatar/Face.h +++ b/interface/src/avatar/Face.h @@ -30,7 +30,8 @@ public: Face(Head* owningHead); ~Face(); - bool isFullFrame() const { return _colorTextureID != 0 && _aspectRatio == FULL_FRAME_ASPECT; } + bool isActive() const { return _colorTextureID != 0 || _depthTextureID != 0; } + bool isFullFrame() const { return isActive() && _aspectRatio == FULL_FRAME_ASPECT; } void setFrameFromWebcam(); void clearFrame(); @@ -64,15 +65,26 @@ private: vpx_codec_ctx_t _colorCodec; vpx_codec_ctx_t _depthCodec; bool _lastFullFrame; + bool _lastDepthOnly; QByteArray _arrivingFrame; int _frameCount; int _frameBytesRemaining; - static ProgramObject* _program; - static int _texCoordCornerLocation; - static int _texCoordRightLocation; - static int _texCoordUpLocation; + struct Locations { + int texCoordCorner; + int texCoordRight; + int texCoordUp; + }; + + static ProgramObject* loadProgram(const QString& suffix, const char* secondTextureUniform, Locations& locations); + + static ProgramObject* _videoProgram; + static Locations _videoProgramLocations; + + static ProgramObject* _texturedProgram; + static Locations _texturedProgramLocations; + static GLuint _vboID; static GLuint _iboID; }; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index bf09096d7f..e274e64e1a 100755 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -42,6 +42,9 @@ void Hand::init() { else { _ballColor = glm::vec3(0.0, 0.0, 0.4); } + + _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE; + _raveGloveEffectsModeChanged = false; } void Hand::reset() { @@ -51,15 +54,20 @@ void Hand::reset() { void Hand::simulate(float deltaTime, bool isMine) { if (_isRaveGloveActive) { + if (_raveGloveEffectsModeChanged && _raveGloveInitialized) { + activateNewRaveGloveMode(); + _raveGloveEffectsModeChanged = false; + } + updateRaveGloveParticles(deltaTime); } } void Hand::calculateGeometry() { - glm::vec3 offset(0.2, -0.2, -0.3); // place the hand in front of the face where we can see it + const glm::vec3 leapHandsOffsetFromFace(0.0, -0.2, -0.3); // place the hand in front of the face where we can see it Head& head = _owningAvatar->getHead(); - _basePosition = head.getPosition() + head.getOrientation() * offset; + _basePosition = head.getPosition() + head.getOrientation() * leapHandsOffsetFromFace; _baseOrientation = head.getOrientation(); // generate finger tip balls.... @@ -106,18 +114,21 @@ void Hand::calculateGeometry() { } void Hand::setRaveGloveEffectsMode(QKeyEvent* event) { + + _raveGloveEffectsModeChanged = true; + switch (event->key()) { - case Qt::Key_0: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR); break; - case Qt::Key_1: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_TRAILS ); break; - case Qt::Key_2: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE ); break; - case Qt::Key_3: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_WATER ); break; - case Qt::Key_4: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FLASHY ); break; - case Qt::Key_5: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER ); break; - case Qt::Key_6: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER ); break; - case Qt::Key_7: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_SNAKE ); break; - case Qt::Key_8: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_PULSE ); break; - case Qt::Key_9: setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROB ); break; + case Qt::Key_0: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR; break; + case Qt::Key_1: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_TRAILS; break; + case Qt::Key_2: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FIRE; break; + case Qt::Key_3: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_WATER; break; + case Qt::Key_4: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_FLASHY; break; + case Qt::Key_5: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_BOZO_SPARKLER; break; + case Qt::Key_6: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_LONG_SPARKLER; break; + case Qt::Key_7: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_SNAKE; break; + case Qt::Key_8: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_PULSE; break; + case Qt::Key_9: _raveGloveEffectsMode = RAVE_GLOVE_EFFECTS_MODE_THROB; break; }; } @@ -129,10 +140,14 @@ void Hand::render(bool lookingInMirror) { calculateGeometry(); if (_isRaveGloveActive) { - renderRaveGloveStage(); + // Disable raveGloveStage while we work on the network glove features + // renderRaveGloveStage(); if (_raveGloveInitialized) { updateRaveGloveEmitters(); // do this after calculateGeometry + + // Use normal lighting for the particles + setRaveLights(RAVE_LIGHTS_PARTICLES); _raveGloveParticleSystem.render(); } } @@ -142,8 +157,35 @@ void Hand::render(bool lookingInMirror) { if ( SHOW_LEAP_HAND ) { //renderLeapHands(); - renderLeapFingerTrails(); - renderLeapHandSpheres(); + if (!isRaveGloveActive()) { + renderLeapFingerTrails(); + renderLeapHandSpheres(); + } + } +} + +void Hand::setRaveLights(RaveLightsSetting setting) { + if (setting == RAVE_LIGHTS_AVATAR) { + // Set some mood lighting + GLfloat ambient_color[] = { 0.0, 0.0, 0.0 }; + glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color); + GLfloat diffuse_color[] = { 0.4, 0.0, 0.0 }; + glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color); + GLfloat specular_color[] = { 0.0, 0.0, 0.0, 0.0}; + glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color); + glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color); + glMateriali(GL_FRONT, GL_SHININESS, 0); + } + else if (setting == RAVE_LIGHTS_PARTICLES) { + // particles use a brighter light setting + 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); } } @@ -160,6 +202,7 @@ void Hand::renderRaveGloveStage() { glm::vec3 v3 = headOrientation * (glm::vec3(-1.0f, 1.0f, 0.0f) * scale) + vc; glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBegin(GL_TRIANGLE_FAN); glColor4f(0.0f, 0.0f, 0.0f, 1.0f); @@ -171,6 +214,7 @@ void Hand::renderRaveGloveStage() { glVertex3fv((float*)&v3); glVertex3fv((float*)&v0); glEnd(); + glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); } } @@ -340,6 +384,7 @@ void Hand::updateRaveGloveParticles(float deltaTime) { } setRaveGloveMode(RAVE_GLOVE_EFFECTS_MODE_FIRE); + activateNewRaveGloveMode(); _raveGloveParticleSystem.setUpDirection(glm::vec3(0.0f, 1.0f, 0.0f)); _raveGloveInitialized = true; } else { @@ -347,12 +392,14 @@ void Hand::updateRaveGloveParticles(float deltaTime) { } } +// The rave glove mode has changed, so activate the effects. +void Hand::activateNewRaveGloveMode() { - -void Hand::setRaveGloveMode(int mode) { - - _raveGloveMode = mode; - + if (!_raveGloveInitialized) { + return; + } + + int mode = _raveGloveEffectsMode; _raveGloveParticleSystem.killAllParticles(); for ( int f = 0; f< NUM_FINGERS; f ++ ) { diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 39845141aa..54a2b2e8a3 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -20,6 +20,11 @@ #include #include +enum RaveLightsSetting { + RAVE_LIGHTS_AVATAR = 0, + RAVE_LIGHTS_PARTICLES +}; + class Avatar; class ProgramObject; @@ -41,6 +46,8 @@ public: void reset(); void simulate(float deltaTime, bool isMine); void render(bool lookingInMirror); + void renderRaveGloveStage(); + void setRaveLights(RaveLightsSetting setting); void setBallColor (glm::vec3 ballColor ) { _ballColor = ballColor; } void updateRaveGloveParticles(float deltaTime); @@ -72,8 +79,8 @@ private: void setLeapHands(const std::vector& handPositions, const std::vector& handNormals); - void renderRaveGloveStage(); - virtual void setRaveGloveMode(int mode); + void activateNewRaveGloveMode(); + void renderLeapHandSpheres(); void renderLeapHands(); void renderLeapHand(PalmData& hand); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b88802b7f1..8f1d511b02 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -84,6 +84,7 @@ Head::Head(Avatar* owningAvatar) : _rightEyeBlinkVelocity(0.0f), _timeWithoutTalking(0.0f), _cameraPitch(_pitch), + _mousePitch(0.f), _cameraYaw(_yaw), _isCameraMoving(false), _cameraFollowsHead(false), @@ -145,7 +146,7 @@ void Head::resetHairPhysics() { } -void Head::simulate(float deltaTime, bool isMine) { +void Head::simulate(float deltaTime, bool isMine, float gyroCameraSensitivity) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 0.50f; @@ -227,15 +228,18 @@ void Head::simulate(float deltaTime, bool isMine) { } // Update camera pitch and yaw independently from motion of head (for gyro-based interface) - if (isMine && _cameraFollowsHead) { + if (isMine && _cameraFollowsHead && (gyroCameraSensitivity > 0.f)) { // If we are using gyros and using gyroLook, have the camera follow head but with a null region // to create stable rendering view with small head movements. - const float CAMERA_FOLLOW_HEAD_RATE_START = 0.01f; - const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.5f; + const float CAMERA_FOLLOW_HEAD_RATE_START = 0.1f; + const float CAMERA_FOLLOW_HEAD_RATE_MAX = 1.0f; const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.05f; const float CAMERA_STOP_TOLERANCE_DEGREES = 0.5f; - const float CAMERA_PITCH_START_TOLERANCE_DEGREES = 20.0f; - const float CAMERA_YAW_START_TOLERANCE_DEGREES = 10.0f; + const float PITCH_START_RANGE = 20.f; + const float YAW_START_RANGE = 10.f; + float pitchStartTolerance = PITCH_START_RANGE * (1.f - gyroCameraSensitivity); + float yawStartTolerance = YAW_START_RANGE * (1.f - gyroCameraSensitivity); + float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw)); if (_isCameraMoving) { _cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE, @@ -248,17 +252,13 @@ void Head::simulate(float deltaTime, bool isMine) { _isCameraMoving = false; } } else { - if ((fabs(_pitch - _cameraPitch) > CAMERA_PITCH_START_TOLERANCE_DEGREES) || - (fabs(_yaw - _cameraYaw) > CAMERA_YAW_START_TOLERANCE_DEGREES)) { + if ((fabs(_pitch - _cameraPitch) > pitchStartTolerance) || + (fabs(_yaw - _cameraYaw) > yawStartTolerance)) { _isCameraMoving = true; _cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START; } } - } else { - // Camera always locked to head - _cameraPitch = _pitch; - _cameraYaw = _yaw; - } + } } void Head::calculateGeometry() { @@ -338,7 +338,7 @@ void Head::setScale (float scale) { } void Head::createMohawk() { - uint16_t nodeId = 0; + uint16_t nodeId = UNKNOWN_NODE_ID; if (_owningAvatar->getOwningNode()) { nodeId = _owningAvatar->getOwningNode()->getNodeID(); } else { @@ -428,7 +428,7 @@ glm::quat Head::getOrientation() const { glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); return owningAvatar->getWorldAlignedOrientation() - * glm::quat(glm::radians(glm::vec3(_cameraPitch, _cameraYaw, 0.0f))); + * glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f))); } void Head::renderHeadSphere() { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 1256b05bb7..ea9e7fab0e 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -41,7 +41,7 @@ public: void init(); void reset(); - void simulate(float deltaTime, bool isMine); + void simulate(float deltaTime, bool isMine, float gyroCameraSensitivity); void render(float alpha); void renderMohawk(); @@ -57,6 +57,8 @@ public: void setCameraFollowsHead(bool cameraFollowsHead) { _cameraFollowsHead = cameraFollowsHead; } + void setMousePitch(float mousePitch) { _mousePitch = mousePitch; } + glm::quat getOrientation() const; glm::quat getCameraOrientation () const; @@ -123,6 +125,7 @@ private: float _rightEyeBlinkVelocity; float _timeWithoutTalking; float _cameraPitch; // Used to position the camera differently from the head + float _mousePitch; float _cameraYaw; bool _isCameraMoving; bool _cameraFollowsHead; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp new file mode 100644 index 0000000000..1c832a648e --- /dev/null +++ b/interface/src/renderer/TextureCache.cpp @@ -0,0 +1,45 @@ +// +// TextureCache.cpp +// interface +// +// Created by Andrzej Kapolka on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. + +#include + +#include "TextureCache.h" + +TextureCache::TextureCache() : _permutationNormalTextureID(0) { +} + +TextureCache::~TextureCache() { + if (_permutationNormalTextureID != 0) { + glDeleteTextures(1, &_permutationNormalTextureID); + } +} + +GLuint TextureCache::getPermutationNormalTextureID() { + if (_permutationNormalTextureID == 0) { + glGenTextures(1, &_permutationNormalTextureID); + glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID); + + // the first line consists of random permutation offsets + unsigned char data[256 * 2 * 3]; + for (int i = 0; i < 256 * 3; i++) { + data[i] = rand() % 256; + } + // the next, random unit normals + for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { + glm::vec3 randvec = glm::sphericalRand(1.0f); + data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f; + data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f; + data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + } + return _permutationNormalTextureID; +} diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h new file mode 100644 index 0000000000..9804f038ba --- /dev/null +++ b/interface/src/renderer/TextureCache.h @@ -0,0 +1,27 @@ +// +// TextureCache.h +// interface +// +// Created by Andrzej Kapolka on 8/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__TextureCache__ +#define __interface__TextureCache__ + +#include "InterfaceConfig.h" + +class TextureCache { +public: + + TextureCache(); + ~TextureCache(); + + GLuint getPermutationNormalTextureID(); + +private: + + GLuint _permutationNormalTextureID; +}; + +#endif /* defined(__interface__TextureCache__) */ diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f4a3ab8067..821cedb507 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -28,6 +28,7 @@ AvatarData::AvatarData(Node* owningNode) : _bodyPitch(0.0), _bodyRoll(0.0), _newScale(1.0f), + _leaderID(UNKNOWN_NODE_ID), _handState(0), _cameraPosition(0,0,0), _cameraOrientation(), @@ -107,8 +108,14 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) { destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyYaw); destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyPitch); destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyRoll); + + // Body scale destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _newScale); + // Follow mode info + memcpy(destinationBuffer, &_leaderID, sizeof(uint16_t)); + destinationBuffer += sizeof(uint16_t); + // Head rotation (NOTE: This needs to become a quaternion to save two bytes) destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_yaw); destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->_pitch); @@ -204,8 +211,14 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) { sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyYaw); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyPitch); sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_bodyRoll); + + // Body scale sourceBuffer += unpackFloatRatioFromTwoByte( sourceBuffer, _newScale); + // Follow mode info + memcpy(&_leaderID, sourceBuffer, sizeof(uint16_t)); + sourceBuffer += sizeof(uint16_t); + // Head rotation (NOTE: This needs to become a quaternion to save two bytes) float headYaw, headPitch, headRoll; sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 5d8b022e34..2510260147 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -106,10 +106,11 @@ public: QString getQStringChatMessage() { return QString(_chatMessage.data()); } // related to Voxel Sending strategies - bool getWantColor() const { return _wantColor; } - bool getWantDelta() const { return _wantDelta; } - bool getWantLowResMoving() const { return _wantLowResMoving; } + bool getWantColor() const { return _wantColor; } + bool getWantDelta() const { return _wantDelta; } + bool getWantLowResMoving() const { return _wantLowResMoving; } bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } + uint16_t getLeaderID() const { return _leaderID; } void setWantColor(bool wantColor) { _wantColor = wantColor; } void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } @@ -131,8 +132,13 @@ protected: float _bodyYaw; float _bodyPitch; float _bodyRoll; + + // Body scale float _newScale; + // Following mode infos + uint16_t _leaderID; + // Hand state (are we grabbing something or not) char _handState; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 644a0764b7..2f6e8aa560 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -20,7 +20,8 @@ HandData::HandData(AvatarData* owningAvatar) : _baseOrientation(0.0f, 0.0f, 0.0f, 1.0f), _owningAvatarData(owningAvatar), _isRaveGloveActive(false), - _raveGloveMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR) + _raveGloveEffectsMode(RAVE_GLOVE_EFFECTS_MODE_THROBBING_COLOR), + _raveGloveEffectsModeChanged(false) { // Start with two palms addNewPalm(); @@ -160,8 +161,9 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { } setRaveGloveActive((gloveFlags & GLOVE_FLAG_RAVE) != 0); -// This is disabled for crash tracing. -// setRaveGloveMode(effectsMode); + if (numHands > 0) { + setRaveGloveMode(effectsMode); + } // One byte for error checking safety. unsigned char requiredLength = (unsigned char)(sourceBuffer - startPosition); @@ -171,6 +173,13 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { return sourceBuffer - startPosition; } +void HandData::setRaveGloveMode(int effectsMode) { + if (effectsMode != _raveGloveEffectsMode) { + _raveGloveEffectsModeChanged = true; + } + _raveGloveEffectsMode = effectsMode; +} + void HandData::setFingerTrailLength(unsigned int length) { for (size_t i = 0; i < getNumPalms(); ++i) { PalmData& palm = getPalms()[i]; diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index fd820bccc0..c871c568bb 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -70,9 +70,9 @@ public: int decodeRemoteData(unsigned char* sourceBuffer); void setRaveGloveActive(bool active) { _isRaveGloveActive = active; } - virtual void setRaveGloveMode(int effectsMode) { _raveGloveMode = effectsMode; } + void setRaveGloveMode(int effectsMode); bool isRaveGloveActive() const { return _isRaveGloveActive; } - int getRaveGloveMode() { return _raveGloveMode; } + int getRaveGloveMode() { return _raveGloveEffectsMode; } friend class AvatarData; protected: @@ -81,7 +81,8 @@ protected: AvatarData* _owningAvatarData; std::vector _palms; bool _isRaveGloveActive; - int _raveGloveMode; + int _raveGloveEffectsMode; + bool _raveGloveEffectsModeChanged; private: // privatize copy ctor and assignment operator so copies of this object cannot be made HandData(const HandData&); diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 2dfd537d93..69b19d2e75 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -435,6 +435,8 @@ void NodeList::addNodeToList(Node* newNode) { ++_numNodes; qDebug() << "Added" << *newNode << "\n"; + + notifyHooksOfAddedNode(newNode); } unsigned NodeList::broadcastToNodes(unsigned char *broadcastData, size_t dataBytes, const char* nodeTypes, int numNodeTypes) { @@ -476,7 +478,7 @@ Node* NodeList::soloNodeOfType(char nodeType) { return NULL; } -void *removeSilentNodes(void *args) { +void* removeSilentNodes(void *args) { NodeList* nodeList = (NodeList*) args; uint64_t checkTimeUSecs; int sleepTime; @@ -490,6 +492,8 @@ void *removeSilentNodes(void *args) { qDebug() << "Killed" << *node << "\n"; + nodeList->notifyHooksOfKilledNode(&*node); + node->setAlive(false); } } @@ -619,3 +623,30 @@ void NodeListIterator::skipDeadAndStopIncrement() { } } } + +void NodeList::addHook(NodeListHook* hook) { + _hooks.push_back(hook); +} + +void NodeList::removeHook(NodeListHook* hook) { + for (int i = 0; i < _hooks.size(); i++) { + if (_hooks[i] == hook) { + _hooks.erase(_hooks.begin() + i); + return; + } + } +} + +void NodeList::notifyHooksOfAddedNode(Node* node) { + for (int i = 0; i < _hooks.size(); i++) { +printf("NodeList::notifyHooksOfAddedNode() i=%d\n", i); + _hooks[i]->nodeAdded(node); + } +} + +void NodeList::notifyHooksOfKilledNode(Node* node) { + for (int i = 0; i < _hooks.size(); i++) { +printf("NodeList::notifyHooksOfKilledNode() i=%d\n", i); + _hooks[i]->nodeKilled(node); + } +} diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 260fddf96f..c2b966b879 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -45,6 +45,14 @@ const int UNKNOWN_NODE_ID = -1; class NodeListIterator; +// Callers who want to hook add/kill callbacks should implement this class +class NodeListHook { +public: + virtual void nodeAdded(Node* node) = 0; + virtual void nodeKilled(Node* node) = 0; +}; + + class NodeList { public: static NodeList* createInstance(char ownerType, unsigned int socketListenPort = NODE_SOCKET_LISTEN_PORT); @@ -66,7 +74,7 @@ public: void setDomainIPToLocalhost(); uint16_t getLastNodeID() const { return _lastNodeID; } - void increaseNodeID() { ++_lastNodeID; } + void increaseNodeID() { (++_lastNodeID == UNKNOWN_NODE_ID) ? ++_lastNodeID : _lastNodeID; } uint16_t getOwnerID() const { return _ownerID; } void setOwnerID(uint16_t ownerID) { _ownerID = ownerID; } @@ -111,6 +119,12 @@ public: void saveData(QSettings* settings); friend class NodeListIterator; + + void addHook(NodeListHook* hook); + void removeHook(NodeListHook* hook); + void notifyHooksOfAddedNode(Node* node); + void notifyHooksOfKilledNode(Node* node); + private: static NodeList* _sharedInstance; @@ -136,6 +150,8 @@ private: void handlePingReply(sockaddr *nodeAddress); void timePingReply(sockaddr *nodeAddress, unsigned char *packetData); + + std::vector _hooks; }; class NodeListIterator : public std::iterator { diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 0a198f9832..5513d362ac 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -115,6 +115,29 @@ unsigned char * childOctalCode(unsigned char * parentOctalCode, char childNumber return newCode; } +void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize) { + float output[3]; + memset(&output[0], 0, 3 * sizeof(float)); + float currentScale = 1.0; + + if (octalCode) { + for (int i = 0; i < numberOfThreeBitSectionsInCode(octalCode); i++) { + currentScale *= 0.5; + int sectionIndex = sectionValue(octalCode + 1 + (BITS_IN_OCTAL * i / BITS_IN_BYTE), + (BITS_IN_OCTAL * i) % BITS_IN_BYTE); + + for (int j = 0; j < BITS_IN_OCTAL; j++) { + output[j] += currentScale * (int)oneAtBit(sectionIndex, (BITS_IN_BYTE - BITS_IN_OCTAL) + j); + } + + } + } + voxelPositionSize.x = output[0]; + voxelPositionSize.y = output[1]; + voxelPositionSize.z = output[2]; + voxelPositionSize.s = currentScale; +} + void copyFirstVertexForCode(unsigned char * octalCode, float* output) { memset(output, 0, 3 * sizeof(float)); diff --git a/libraries/shared/src/OctalCode.h b/libraries/shared/src/OctalCode.h index 477751ea7c..405b85164d 100644 --- a/libraries/shared/src/OctalCode.h +++ b/libraries/shared/src/OctalCode.h @@ -36,6 +36,11 @@ bool isAncestorOf(unsigned char* possibleAncestor, unsigned char* possibleDescen float * firstVertexForCode(unsigned char * octalCode); void copyFirstVertexForCode(unsigned char * octalCode, float* output); +struct VoxelPositionSize { + float x, y, z, s; +}; +void voxelDetailsForCode(unsigned char * octalCode, VoxelPositionSize& voxelPositionSize); + typedef enum { ILLEGAL_CODE = -2, LESS_THAN = -1, diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 99c887614c..e8a459d633 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -20,11 +20,13 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { return 1; case PACKET_TYPE_HEAD_DATA: - return 3; + return 4; case PACKET_TYPE_AVATAR_FACE_VIDEO: return 1; - + + case PACKET_TYPE_VOXEL_STATS: + return 1; default: return 0; } diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 93ea8a70ed..cf2374b84e 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -215,7 +215,6 @@ bool createVoxelEditMessage(unsigned char command, short int sequence, bool success = true; // assume the best int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now - int actualMessageSize = 3; unsigned char* messageBuffer = new unsigned char[messageSize]; int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command); @@ -223,6 +222,7 @@ bool createVoxelEditMessage(unsigned char command, short int sequence, *sequenceAt = sequence; unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence)]; + int actualMessageSize = numBytesPacketHeader + sizeof(sequence); for (int i = 0; i < voxelCount && success; i++) { // get the coded voxel @@ -232,7 +232,7 @@ bool createVoxelEditMessage(unsigned char command, short int sequence, int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA; // make sure we have room to copy this voxel - if (actualMessageSize+lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { + if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { success = false; } else { // add it to our message diff --git a/libraries/voxels/src/JurisdictionMap.h b/libraries/voxels/src/JurisdictionMap.h index 5caec75b39..4bb3f5595f 100644 --- a/libraries/voxels/src/JurisdictionMap.h +++ b/libraries/voxels/src/JurisdictionMap.h @@ -30,6 +30,8 @@ public: bool writeToFile(const char* filename); bool readFromFile(const char* filename); + + unsigned char* getRootOctalCode() const { return _rootOctalCode; } private: void clear(); diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index 4b971e9515..5a5815b6b3 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -14,6 +14,7 @@ #include #include +#include // this is where the coordinate system is represented const glm::vec3 IDENTITY_RIGHT = glm::vec3( 1.0f, 0.0f, 0.0f); diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp index 9dd696eaa6..d525725371 100644 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ b/libraries/voxels/src/VoxelSceneStats.cpp @@ -22,9 +22,17 @@ VoxelSceneStats::VoxelSceneStats() : reset(); _isReadyToSend = false; _isStarted = false; + _jurisdictionRoot = NULL; } -void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root) { +VoxelSceneStats::~VoxelSceneStats() { + if (_jurisdictionRoot) { + delete[] _jurisdictionRoot; + } +} + + +void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* root, JurisdictionMap* jurisdictionMap) { reset(); // resets packet and voxel stats _isStarted = true; _start = usecTimestampNow(); @@ -34,6 +42,19 @@ void VoxelSceneStats::sceneStarted(bool isFullScene, bool isMoving, VoxelNode* r _isFullScene = isFullScene; _isMoving = isMoving; + + if (_jurisdictionRoot) { + delete[] _jurisdictionRoot; + _jurisdictionRoot = NULL; + } + if (jurisdictionMap) { + unsigned char* jurisdictionRoot = jurisdictionMap->getRootOctalCode(); + if (jurisdictionRoot) { + int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(jurisdictionRoot)); + _jurisdictionRoot = new unsigned char[bytes]; + memcpy(_jurisdictionRoot, jurisdictionRoot, bytes); + } + } } void VoxelSceneStats::sceneCompleted() { @@ -273,6 +294,20 @@ int VoxelSceneStats::packIntoMessage(unsigned char* destinationBuffer, int avail destinationBuffer += sizeof(_existsInPacketBitsWritten); memcpy(destinationBuffer, &_treesRemoved, sizeof(_treesRemoved)); destinationBuffer += sizeof(_treesRemoved); + + // add the root jurisdiction + if (_jurisdictionRoot) { + // copy the + int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot)); + memcpy(destinationBuffer, &bytes, sizeof(bytes)); + destinationBuffer += sizeof(bytes); + memcpy(destinationBuffer, _jurisdictionRoot, bytes); + destinationBuffer += bytes; + } else { + int bytes = 0; + memcpy(destinationBuffer, &bytes, sizeof(bytes)); + destinationBuffer += sizeof(bytes); + } return destinationBuffer - bufferStart; // includes header! } @@ -363,6 +398,19 @@ int VoxelSceneStats::unpackFromMessage(unsigned char* sourceBuffer, int availabl sourceBuffer += sizeof(_existsInPacketBitsWritten); memcpy(&_treesRemoved, sourceBuffer, sizeof(_treesRemoved)); sourceBuffer += sizeof(_treesRemoved); + + // read the root jurisdiction + int bytes = 0; + memcpy(&bytes, sourceBuffer, sizeof(bytes)); + sourceBuffer += sizeof(bytes); + + if (bytes == 0) { + _jurisdictionRoot = NULL; + } else { + _jurisdictionRoot = new unsigned char[bytes]; + memcpy(_jurisdictionRoot, sourceBuffer, bytes); + sourceBuffer += bytes; + } // running averages _elapsedAverage.updateAverage((float)_elapsed); diff --git a/libraries/voxels/src/VoxelSceneStats.h b/libraries/voxels/src/VoxelSceneStats.h index ded2061a6e..910859ff3e 100644 --- a/libraries/voxels/src/VoxelSceneStats.h +++ b/libraries/voxels/src/VoxelSceneStats.h @@ -12,14 +12,16 @@ #include #include +#include "JurisdictionMap.h" class VoxelNode; class VoxelSceneStats { public: VoxelSceneStats(); + ~VoxelSceneStats(); void reset(); - void sceneStarted(bool fullScene, bool moving, VoxelNode* root); + void sceneStarted(bool fullScene, bool moving, VoxelNode* root, JurisdictionMap* jurisdictionMap); void sceneCompleted(); void printDebugDetails(); @@ -78,6 +80,9 @@ public: ItemInfo& getItemInfo(int item) { return _ITEMS[item]; }; char* getItemValue(int item); + unsigned char* getJurisdictionRoot() const { return _jurisdictionRoot; } + + private: bool _isReadyToSend; unsigned char _statsMessage[MAX_PACKET_SIZE]; @@ -165,6 +170,8 @@ private: static ItemInfo _ITEMS[]; static int const MAX_ITEM_VALUE_LENGTH = 128; char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH]; + + unsigned char* _jurisdictionRoot; }; #endif /* defined(__hifi__VoxelSceneStats__) */ diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 5d7489135a..c6a8a61ca1 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -49,6 +49,7 @@ const float MAX_CUBE = 0.05f; const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps int PACKETS_PER_CLIENT_PER_INTERVAL = 20; const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels +const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS; const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4; @@ -64,6 +65,7 @@ bool debugVoxelSending = false; bool shouldShowAnimationDebug = false; bool displayVoxelStats = false; bool debugVoxelReceiving = false; +bool sendEnvironments = true; EnvironmentData environmentData[3]; @@ -255,7 +257,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, // start tracking our stats bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); - nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode); + nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction); } // If we have something in our nodeBag, then turn them into packets and send them out... @@ -265,7 +267,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, int packetsSentThisInterval = 0; uint64_t start = usecTimestampNow(); - bool shouldSendEnvironments = shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); + bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS); while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) { // Check to see if we're taking too long, and if so bail early... uint64_t now = usecTimestampNow(); @@ -473,6 +475,15 @@ int main(int argc, const char * argv[]) { jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes); } } + + // should we send environments? Default is yes, but this command line suppresses sending + const char* DONT_SEND_ENVIRONMENTS = "--dontSendEnvironments"; + bool dontSendEnvironments = cmdOptionExists(argc, argv, DONT_SEND_ENVIRONMENTS); + if (dontSendEnvironments) { + printf("Sending environments suppressed...\n"); + ::sendEnvironments = false; + } + printf("Sending environments=%s\n", debug::valueOf(::sendEnvironments)); NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort); setvbuf(stdout, NULL, _IOLBF, 0); @@ -562,7 +573,7 @@ int main(int argc, const char * argv[]) { const char* PACKETS_PER_SECOND = "--packetsPerSecond"; const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND); if (packetsPerSecond) { - PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/10; + PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/INTERVALS_PER_SECOND; if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) { PACKETS_PER_CLIENT_PER_INTERVAL = 1; }