diff --git a/BUILD.md b/BUILD.md index 674f0d24cc..93aafcc3e0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -24,9 +24,12 @@ In order for CMake to find the Qt5 find modules, you will need to set an ENV var For example, a Qt5 5.2.0 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.2.0/clang_64/lib/cmake/ +The path it needs to be set to will depend on where and how Qt5 was installed. e.g. + + export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.2.0/clang_64/lib/cmake/ + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.2.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake -The path it needs to be set to will depend on where and how Qt5 was installed. ####Generating build files Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index c601478f70..14765e2ddc 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -87,13 +87,14 @@ void MetavoxelServer::sendDeltas() { int elapsed = now - _lastSend; _lastSend = now; - _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - elapsed)); + _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); } MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelServer* server) : Endpoint(node, new PacketRecord(), NULL), _server(server), - _reliableDeltaChannel(NULL) { + _reliableDeltaChannel(NULL), + _reliableDeltaID(0) { connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleMessage(const QVariant&))); connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(checkReliableDeltaReceived())); @@ -108,9 +109,7 @@ void MetavoxelSession::update() { } // if we're sending a reliable delta, wait until it's acknowledged if (_reliableDeltaChannel) { - Bitstream& out = _sequencer.startPacket(); - out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); - _sequencer.endPacket(); + sendPacketGroup(); return; } Bitstream& out = _sequencer.startPacket(); @@ -134,12 +133,16 @@ void MetavoxelSession::update() { // go back to the beginning with the current packet and note that there's a delta pending _sequencer.getOutputStream().getUnderlying().device()->seek(start); - out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID }; + out << QVariant::fromValue(msg); _sequencer.endPacket(); } else { _sequencer.endPacket(); } + + // perhaps send additional packets to fill out the group + sendPacketGroup(1); } void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { @@ -176,3 +179,17 @@ void MetavoxelSession::checkReliableDeltaReceived() { _reliableDeltaData = MetavoxelData(); _reliableDeltaChannel = NULL; } + +void MetavoxelSession::sendPacketGroup(int alreadySent) { + int additionalPackets = _sequencer.notePacketGroup() - alreadySent; + for (int i = 0; i < additionalPackets; i++) { + Bitstream& out = _sequencer.startPacket(); + if (_reliableDeltaChannel) { + MetavoxelDeltaPendingMessage msg = { _reliableDeltaID }; + out << QVariant::fromValue(msg); + } else { + out << QVariant(); + } + _sequencer.endPacket(); + } +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index f2769f26f2..6df769227c 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -74,6 +74,8 @@ private slots: private: + void sendPacketGroup(int alreadySent = 0); + MetavoxelServer* _server; MetavoxelLOD _lod; @@ -83,6 +85,7 @@ private: MetavoxelData _reliableDeltaData; MetavoxelLOD _reliableDeltaLOD; Bitstream::WriteMappings _reliableDeltaWriteMappings; + int _reliableDeltaID; }; #endif // hifi_MetavoxelServer_h diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js new file mode 100644 index 0000000000..57f9d84ffe --- /dev/null +++ b/examples/avatarLocalLight.js @@ -0,0 +1,126 @@ +// +// avatarLocalLight.js +// +// Created by Tony Peng on July 2nd, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Set the local light direction and color on the avatar +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var localLightDirections = [ {x: 1.0, y:0.0, z: 0.0}, {x: 0.0, y:1.0, z: 1.0}, {x: 0.0, y:0.0, z: 1.0}, {x: 1.0, y:1.0, z: 1.0} ]; +var localLightColors = [ {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0} ]; + +var currentSelection = 0; +var currentNumLights = 1; +var maxNumLights = 2; + +function keyPressEvent(event) { + + var choice = parseInt(event.text); + + if (event.text == "1") { + currentSelection = 0; + print("light election = " + currentSelection); + } + else if (event.text == "2" ) { + currentSelection = 1; + print("light selection = " + currentSelection); + } + else if (event.text == "3" ) { + currentSelection = 2; + print("light selection = " + currentSelection); + } + else if (event.text == "4" ) { + currentSelection = 3; + print("light selection = " + currentSelection); + } + else if (event.text == "5" ) { + localLightColors[currentSelection].x += 0.01; + if ( localLightColors[currentSelection].x > 1.0) { + localLightColors[currentSelection].x = 0.0; + } + + MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + } + else if (event.text == "6" ) { + localLightColors[currentSelection].y += 0.01; + if ( localLightColors[currentSelection].y > 1.0) { + localLightColors[currentSelection].y = 0.0; + } + + MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + } + else if (event.text == "7" ) { + localLightColors[currentSelection].z += 0.01; + if ( localLightColors[currentSelection].z > 1.0) { + localLightColors[currentSelection].z = 0.0; + } + + MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + } + else if (event.text == "8" ) { + localLightDirections[currentSelection].x += 0.01; + if (localLightDirections[currentSelection].x > 1.0) { + localLightDirections[currentSelection].x = -1.0; + } + + MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + } + else if (event.text == "9" ) { + localLightDirections[currentSelection].x -= 0.01; + if (localLightDirections[currentSelection].x < -1.0) { + localLightDirections[currentSelection].x = 1.0; + } + + MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + } + else if (event.text == "[" ) { + localLightDirections[currentSelection].y += 0.01; + if (localLightDirections[currentSelection].y > 1.0) { + localLightDirections[currentSelection].y = -1.0; + } + + MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + } + else if (event.text == "]" ) { + localLightDirections[currentSelection].y -= 0.01; + if (localLightDirections[currentSelection].y < -1.0) { + localLightDirections[currentSelection].y = 1.0; + } + + MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + } + else if (event.text == "," ) { + if (currentNumLights + 1 <= maxNumLights) { + var darkGrayColor = {x:0.3, y:0.3, z:0.3}; + + // default light + localLightColors[currentNumLights].x = darkGrayColor.x; + localLightColors[currentNumLights].y = darkGrayColor.y; + localLightColors[currentNumLights].z = darkGrayColor.z; + + MyAvatar.addLocalLight(); + MyAvatar.setLocalLightColor(localLightColors[currentNumLights], currentNumLights); + MyAvatar.setLocalLightDirection(localLightDirections[currentNumLights], currentNumLights); + + ++currentNumLights; + } + } + else if (event.text == "." ) { + if (currentNumLights - 1 >= 0 ) { + + // no light contribution + localLightColors[currentNumLights - 1].x = 0.0; + localLightColors[currentNumLights - 1].y = 0.0; + localLightColors[currentNumLights - 1].z = 0.0; + + MyAvatar.removeLocalLight(); + --currentNumLights; + } + } +} + +Controller.keyPressEvent.connect(keyPressEvent); diff --git a/examples/clap.js b/examples/clap.js index 7a2a77afc4..28835c1f00 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -1,48 +1,52 @@ // -// cameraExample.js +// clap.js // examples // // Copyright 2014 High Fidelity, Inc. // -// This sample script watches your hydra hands and makes clapping sound when they come close together fast +// This sample script watches your hydra hands and makes clapping sound when they come close together fast, +// and also watches for the 'shift' key and claps when that key is pressed. Clapping multiple times by pressing +// the shift key again makes the animation and sound match your pace of clapping. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // var clapAnimation = "https://s3-us-west-1.amazonaws.com/highfidelity-public/animations/ClapAnimations/ClapHands_Standing.fbx"; +var ANIMATION_FRAMES_PER_CLAP = 10.0; var startEndFrames = []; -startEndFrames.push({ start: 0, end: 8}); +startEndFrames.push({ start: 0, end: 10}); startEndFrames.push({ start: 10, end: 20}); -startEndFrames.push({ start: 20, end: 28}); -startEndFrames.push({ start: 30, end: 37}); -startEndFrames.push({ start: 41, end: 46}); -startEndFrames.push({ start: 53, end: 58}); +startEndFrames.push({ start: 20, end: 30}); +startEndFrames.push({ start: 30, end: 40}); +startEndFrames.push({ start: 41, end: 51}); +startEndFrames.push({ start: 53, end: 0}); var lastClapFrame = 0; var lastAnimFrame = 0; var claps = []; -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap1Reverb.wav")); -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap2Reverb.wav")); -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap3Reverb.wav")); -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap4Reverb.wav")); -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap5Reverb.wav")); -claps.push(new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/claps/Clap6Reverb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap1Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap2Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap3Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap4Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap5Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap6Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap7Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap8Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap9Rvb.wav")); +claps.push(new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/claps/BClap10Rvb.wav")); var numberOfSounds = claps.length; var clappingNow = false; var collectedClicks = 0; +var clickStartTime, clickEndTime; var clickClappingNow = false; var CLAP_START_RATE = 15.0; var clapRate = CLAP_START_RATE; var startedTimer = false; -var clapping = new Array(); -clapping[0] = false; -clapping[1] = false; - function maybePlaySound(deltaTime) { // Set the location and other info for the sound to play @@ -51,9 +55,9 @@ function maybePlaySound(deltaTime) { var frame = Math.floor(animationDetails.frameIndex); if (frame != lastAnimFrame) { - print("frame " + frame); lastAnimFrame = frame; } + for (var i = 0; i < startEndFrames.length; i++) { if (frame == startEndFrames[i].start && (frame != lastClapFrame)) { playClap(1.0, Camera.getPosition()); @@ -90,34 +94,41 @@ function playClap(volume, position) { Audio.playSound(claps[clip], options); } -function keepClapping() { - playClap(1.0, Camera.getPosition()); -} +var FASTEST_CLAP_INTERVAL = 100.0; +var SLOWEST_CLAP_INTERVAL = 2000.0; Controller.keyPressEvent.connect(function(event) { if(event.text == "SHIFT") { if (!clickClappingNow) { + clickClappingNow = true; + clickStartTime = new Date(); playClap(1.0, Camera.getPosition()); - var whichClip = Math.floor(Math.random() * startEndFrames.length); lastClapFrame = 0; MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false); - clickClappingNow = true; } else { - clapRate *= 1.25; - MyAvatar.stopAnimation(clapAnimation); - MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false); + // Adjust animation speed for measured clicking interval + clickEndTime = new Date(); + var milliseconds = clickEndTime - clickStartTime; + clickStartTime = new Date(); + if ((milliseconds < SLOWEST_CLAP_INTERVAL) && (milliseconds > FASTEST_CLAP_INTERVAL)) { + clapRate = ANIMATION_FRAMES_PER_CLAP * (1000.0 / milliseconds); + playClap(1.0, Camera.getPosition()); + MyAvatar.stopAnimation(clapAnimation); + MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false); + } collectedClicks = collectedClicks + 1; } } }); -var CLAP_END_WAIT_MSECS = 500; +var CLAP_END_WAIT_MSECS = 300; Controller.keyReleaseEvent.connect(function(event) { if (event.text == "SHIFT") { + collectedClicks = 0; if (!startedTimer) { - startedTimer = true; collectedClicks = 0; Script.setTimeout(stopClapping, CLAP_END_WAIT_MSECS); + startedTimer = true; } } }); @@ -134,5 +145,4 @@ function stopClapping() { } // Connect a call back that happens every frame -Script.update.connect(maybePlaySound); -//Controller.keyPressEvent.connect(keyPressEvent); \ No newline at end of file +Script.update.connect(maybePlaySound); \ No newline at end of file diff --git a/examples/concertCamera_kims.js b/examples/concertCamera_kims.js new file mode 100644 index 0000000000..3017d3c008 --- /dev/null +++ b/examples/concertCamera_kims.js @@ -0,0 +1,72 @@ +// +// concertCamera.js +// +// Created by Philip Rosedale on June 24, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Move a camera through a series of pre-set locations by pressing number keys +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var oldMode; +var avatarPosition; + +var cameraNumber = 0; +var freeCamera = false; + +var cameraLocations = [ {x: 8027.5, y: 237.5, z: 7305.7}, {x: 8027.5, y: 237.5, z: 7306.6}, {x: 8027.5, y: 237.5, z: 7308.0}, {x: 8027.5, y: 237.5, z: 7303.0}, {x: 8030.8, y: 238.6, z: 7311.4}, {x: 8030.9, y: 237.1, z: 7308.0} ]; +var cameraLookAts = [ {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7305.7}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0} ]; + +function saveCameraState() { + oldMode = Camera.getMode(); + avatarPosition = MyAvatar.position; + Camera.setModeShiftPeriod(0.0); + Camera.setMode("independent"); +} + +function restoreCameraState() { + Camera.stopLooking(); + Camera.setMode(oldMode); +} + +function update(deltaTime) { + if (freeCamera) { + var delta = Vec3.subtract(MyAvatar.position, avatarPosition); + if (Vec3.length(delta) > 0.05) { + cameraNumber = 0; + freeCamera = false; + restoreCameraState(); + } + } +} + +function keyPressEvent(event) { + + var choice = parseInt(event.text); + + if ((choice > 0) && (choice <= cameraLocations.length)) { + print("camera " + choice); + if (!freeCamera) { + saveCameraState(); + freeCamera = true; + } + Camera.setMode("independent"); + Camera.setPosition(cameraLocations[choice - 1]); + Camera.keepLookingAt(cameraLookAts[choice - 1]); + } + if (event.text == "ESC") { + cameraNumber = 0; + freeCamera = false; + restoreCameraState(); + } + if (event.text == "0") { + // Show camera location in log + var cameraLocation = Camera.getPosition(); + print(cameraLocation.x + ", " + cameraLocation.y + ", " + cameraLocation.z); + } +} + +Script.update.connect(update); +Controller.keyPressEvent.connect(keyPressEvent); diff --git a/examples/concertCamera_kyrs.js b/examples/concertCamera_kyrs.js new file mode 100644 index 0000000000..2b37a84f9e --- /dev/null +++ b/examples/concertCamera_kyrs.js @@ -0,0 +1,72 @@ +// +// concertCamera.js +// +// Created by Philip Rosedale on June 24, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Move a camera through a series of pre-set locations by pressing number keys +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var oldMode; +var avatarPosition; + +var cameraNumber = 0; +var freeCamera = false; + +var cameraLocations = [ {x: 2921.5, y: 251.3, z: 8254.8}, {x: 2921.5, y: 251.3, z: 8254.4}, {x: 2921.5, y: 251.3, z: 8252.2}, {x: 2921.5, y: 251.3, z: 8247.2}, {x: 2921.4, y: 251.3, z: 8255.7} ]; +var cameraLookAts = [ {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.4 , y: 251.3, z: 8255.1} ]; + +function saveCameraState() { + oldMode = Camera.getMode(); + avatarPosition = MyAvatar.position; + Camera.setModeShiftPeriod(0.0); + Camera.setMode("independent"); +} + +function restoreCameraState() { + Camera.stopLooking(); + Camera.setMode(oldMode); +} + +function update(deltaTime) { + if (freeCamera) { + var delta = Vec3.subtract(MyAvatar.position, avatarPosition); + if (Vec3.length(delta) > 0.05) { + cameraNumber = 0; + freeCamera = false; + restoreCameraState(); + } + } +} + +function keyPressEvent(event) { + + var choice = parseInt(event.text); + + if ((choice > 0) && (choice <= cameraLocations.length)) { + print("camera " + choice); + if (!freeCamera) { + saveCameraState(); + freeCamera = true; + } + Camera.setMode("independent"); + Camera.setPosition(cameraLocations[choice - 1]); + Camera.keepLookingAt(cameraLookAts[choice - 1]); + } + if (event.text == "ESC") { + cameraNumber = 0; + freeCamera = false; + restoreCameraState(); + } + if (event.text == "0") { + // Show camera location in log + var cameraLocation = Camera.getPosition(); + print(cameraLocation.x + ", " + cameraLocation.y + ", " + cameraLocation.z); + } +} + +Script.update.connect(update); +Controller.keyPressEvent.connect(keyPressEvent); diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 488736abf9..468a892686 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -11,9 +11,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + // the diffuse texture uniform sampler2D diffuseMap; +// local lights +const int MAX_LOCAL_LIGHTS = 2; // 2 lights for now, will probably need more later on +uniform int numLocalLights; +uniform vec3 localLightDirections[MAX_LOCAL_LIGHTS]; +uniform vec3 localLightColors[MAX_LOCAL_LIGHTS]; + // the interpolated position varying vec4 position; @@ -25,8 +32,19 @@ void main(void) { vec4 normalizedNormal = normalize(normal); float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse); + + // the local light that is always present + vec4 totalLocalLight = vec4(0.0, 0.0, 0.0, 1.0); + for (int i = 0; i < numLocalLights; i++) { + float localDiffuse = dot(normalizedNormal, vec4(localLightDirections[i], 1.0)); + float localLight = step(0.0, localDiffuse); + float localLightVal = localDiffuse * localLight; + + totalLocalLight += (localLightVal * vec4( localLightColors[i], 0.0)); + } + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + totalLocalLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), diff --git a/interface/resources/shaders/model.vert b/interface/resources/shaders/model.vert index da7e9640d9..13eee2fa3d 100644 --- a/interface/resources/shaders/model.vert +++ b/interface/resources/shaders/model.vert @@ -11,6 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_LOCAL_LIGHTS = 4; + // the interpolated position varying vec4 position; @@ -37,3 +39,4 @@ void main(void) { // use standard pipeline transform gl_Position = ftransform(); } + diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index 8444f2d6ea..ca0201f6ab 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -37,9 +37,14 @@ void main(void) { normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); float diffuse = dot(viewNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse); + float localDiffuse = dot(viewNormal, gl_LightSource[1].position); + float localLight = step(0.0, localDiffuse); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + gl_FrontLightProduct[1].diffuse * (localDiffuse * localLight)); + + + // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(vec3(interpolatedPosition), 0.0))), viewNormal)); diff --git a/interface/resources/shaders/model_specular_map.frag b/interface/resources/shaders/model_specular_map.frag index a07324cd1b..329da65e9e 100644 --- a/interface/resources/shaders/model_specular_map.frag +++ b/interface/resources/shaders/model_specular_map.frag @@ -10,6 +10,11 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +const int MAX_LOCAL_LIGHTS = 2; + +uniform int numLocalLights; +uniform vec3 localLightDirections[MAX_LOCAL_LIGHTS]; +uniform vec3 localLightColors[MAX_LOCAL_LIGHTS]; // the diffuse texture uniform sampler2D diffuseMap; @@ -28,8 +33,19 @@ void main(void) { vec4 normalizedNormal = normalize(normal); float diffuse = dot(normalizedNormal, gl_LightSource[0].position); float facingLight = step(0.0, diffuse); + + // the local light that is always present + vec4 totalLocalLight = vec4(0.0, 0.0, 0.0, 1.0); + for (int i = 0; i < numLocalLights; i++) { + float localDiffuse = dot(normalizedNormal, vec4(localLightDirections[i], 1.0)); + float localLight = step(0.0, localDiffuse); + float localLightVal = localDiffuse * localLight; + + totalLocalLight += (localLightVal * vec4( localLightColors[i], 0.0)); + } + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + - gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight) + totalLocalLight); // compute the specular component (sans exponent) float specular = facingLight * max(0.0, dot(normalize(gl_LightSource[0].position - normalize(vec4(position.xyz, 0.0))), @@ -38,4 +54,5 @@ void main(void) { // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0); + } diff --git a/interface/resources/shaders/skin_model.vert b/interface/resources/shaders/skin_model.vert index d68347d33d..943daf9061 100644 --- a/interface/resources/shaders/skin_model.vert +++ b/interface/resources/shaders/skin_model.vert @@ -34,6 +34,7 @@ void main(void) { position += clusterMatrix * gl_Vertex * clusterWeight; normal += clusterMatrix * vec4(gl_Normal, 0.0) * clusterWeight; } + position = gl_ModelViewMatrix * position; normal = normalize(gl_ModelViewMatrix * normal); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a038cc583e..42184bbdda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -354,6 +354,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); + + // Set hand controller velocity filtering + _sixenseManager.setLowVelocityFilter(Menu::getInstance()->isOptionChecked(MenuOption::LowVelocityFilter)); checkVersion(); @@ -601,9 +604,19 @@ void Application::paintGL() { } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness(0.0f); - _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); - _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); + //Only behave like a true mirror when in the OR + if (OculusManager::isConnected()) { + _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition() + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0)); + } else { + _myCamera.setTightness(0.0f); + glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition(); + float headHeight = eyePosition.y - _myAvatar->getPosition().y; + _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0)); + _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + } } // Update camera position @@ -682,13 +695,10 @@ void Application::paintGL() { } { - PerformanceTimer perfTimer("paintGL/renderOverlay"); - //If alpha is 1, we can render directly to the screen. - if (_applicationOverlay.getAlpha() == 1.0f) { - _applicationOverlay.renderOverlay(); - } else { - //Render to to texture so we can fade it - _applicationOverlay.renderOverlay(true); + PerformanceTimer perfTimer("renderOverlay"); + // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() + _applicationOverlay.renderOverlay(true); + if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { _applicationOverlay.displayOverlayTexture(); } } @@ -713,6 +723,7 @@ void Application::resizeGL(int width, int height) { resetCamerasOnResizeGL(_myCamera, width, height); glViewport(0, 0, width, height); // shouldn't this account for the menu??? + _applicationOverlay.resize(); updateProjectionMatrix(); glLoadIdentity(); @@ -1010,6 +1021,9 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } break; + case Qt::Key_Slash: + Menu::getInstance()->triggerOption(MenuOption::UserInterface); + break; case Qt::Key_F: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::DisplayFrustum); @@ -1029,7 +1043,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; break; - case Qt::Key_Slash: + case Qt::Key_Percent: Menu::getInstance()->triggerOption(MenuOption::Stats); break; case Qt::Key_Plus: @@ -1355,18 +1369,18 @@ void Application::idle() { if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) { _lastTimeUpdated.start(); { - PerformanceTimer perfTimer("idle/update"); + PerformanceTimer perfTimer("update"); PerformanceWarning warn(showWarnings, "Application::idle()... update()"); const float BIGGEST_DELTA_TIME_SECS = 0.25f; update(glm::clamp((float)timeSinceLastUpdate / 1000.f, 0.f, BIGGEST_DELTA_TIME_SECS)); } { - PerformanceTimer perfTimer("idle/updateGL"); + PerformanceTimer perfTimer("updateGL"); PerformanceWarning warn(showWarnings, "Application::idle()... updateGL()"); _glWidget->updateGL(); } { - PerformanceTimer perfTimer("idle/rest"); + PerformanceTimer perfTimer("rest"); PerformanceWarning warn(showWarnings, "Application::idle()... rest of it"); _idleLoopStdev.addValue(timeSinceLastUpdate); @@ -1378,7 +1392,7 @@ void Application::idle() { } if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { - PerformanceTimer perfTimer("idle/rest/_buckyBalls"); + PerformanceTimer perfTimer("buckyBalls"); _buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData()); } @@ -1426,6 +1440,10 @@ void Application::setRenderVoxels(bool voxelRender) { } } +void Application::setLowVelocityFilter(bool lowVelocityFilter) { + getSixenseManager()->setLowVelocityFilter(lowVelocityFilter); +} + void Application::doKillLocalVoxels() { _wantToKillLocalVoxels = true; } @@ -1792,7 +1810,7 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) { } void Application::updateLOD() { - PerformanceTimer perfTimer("idle/update/updateLOD"); + PerformanceTimer perfTimer("LOD"); // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { Menu::getInstance()->autoAdjustLOD(_fps); @@ -1802,7 +1820,7 @@ void Application::updateLOD() { } void Application::updateMouseRay() { - PerformanceTimer perfTimer("idle/update/updateMouseRay"); + PerformanceTimer perfTimer("mouseRay"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); @@ -1835,8 +1853,6 @@ void Application::updateMouseRay() { } void Application::updateFaceshift() { - PerformanceTimer perfTimer("idle/update/updateFaceshift"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateFaceshift()"); @@ -1850,8 +1866,6 @@ void Application::updateFaceshift() { } void Application::updateVisage() { - PerformanceTimer perfTimer("idle/update/updateVisage"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateVisage()"); @@ -1860,11 +1874,11 @@ void Application::updateVisage() { } void Application::updateMyAvatarLookAtPosition() { - PerformanceTimer perfTimer("idle/update/updateMyAvatarLookAtPosition"); - + PerformanceTimer perfTimer("lookAt"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); + _myAvatar->updateLookAtTargetAvatar(); FaceTracker* tracker = getActiveFaceTracker(); bool isLookingAtSomeone = false; @@ -1927,7 +1941,7 @@ void Application::updateMyAvatarLookAtPosition() { } void Application::updateThreads(float deltaTime) { - PerformanceTimer perfTimer("idle/update/updateThreads"); + PerformanceTimer perfTimer("updateThreads"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateThreads()"); @@ -1942,7 +1956,7 @@ void Application::updateThreads(float deltaTime) { } void Application::updateMetavoxels(float deltaTime) { - PerformanceTimer perfTimer("idle/update/updateMetavoxels"); + PerformanceTimer perfTimer("updateMetavoxels"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()"); @@ -1972,7 +1986,7 @@ void Application::cameraMenuChanged() { } void Application::updateCamera(float deltaTime) { - PerformanceTimer perfTimer("idle/update/updateCamera"); + PerformanceTimer perfTimer("updateCamera"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); @@ -1990,7 +2004,7 @@ void Application::updateCamera(float deltaTime) { } void Application::updateDialogs(float deltaTime) { - PerformanceTimer perfTimer("idle/update/updateDialogs"); + PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateDialogs()"); @@ -2007,7 +2021,7 @@ void Application::updateDialogs(float deltaTime) { } void Application::updateCursor(float deltaTime) { - PerformanceTimer perfTimer("idle/update/updateCursor"); + PerformanceTimer perfTimer("updateCursor"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCursor()"); @@ -2032,83 +2046,69 @@ void Application::updateCursor(float deltaTime) { } void Application::update(float deltaTime) { - //PerformanceTimer perfTimer("idle/update"); // NOTE: we track this above in Application::idle() - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); updateLOD(); updateMouseRay(); // check what's under the mouse and update the mouse voxel - updateFaceshift(); - updateVisage(); - { - PerformanceTimer perfTimer("idle/update/updateLookAtTargetAvatar"); - _myAvatar->updateLookAtTargetAvatar(); - } - updateMyAvatarLookAtPosition(); - { - PerformanceTimer perfTimer("idle/update/sixense,joystick,prioVR"); + PerformanceTimer perfTimer("devices"); + updateFaceshift(); + updateVisage(); _sixenseManager.update(deltaTime); _joystickManager.update(); _prioVR.update(deltaTime); } - { - PerformanceTimer perfTimer("idle/update/updateMyAvatar"); + PerformanceTimer perfTimer("myAvatar"); + updateMyAvatarLookAtPosition(); updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes } updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - { - PerformanceTimer perfTimer("idle/update/_avatarManager"); - _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... - } + _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... + updateMetavoxels(deltaTime); // update metavoxels updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present updateCursor(deltaTime); // Handle cursor updates { - PerformanceTimer perfTimer("idle/update/_particles"); + PerformanceTimer perfTimer("particles"); _particles.update(); // update the particles... - } - { - PerformanceTimer perfTimer("idle/update/_particleCollisionSystem"); - _particleCollisionSystem.update(); // collide the particles... + { + PerformanceTimer perfTimer("collisions"); + _particleCollisionSystem.update(); // collide the particles... + } } { - PerformanceTimer perfTimer("idle/update/_models"); + PerformanceTimer perfTimer("models"); _models.update(); // update the models... } { - PerformanceTimer perfTimer("idle/update/_overlays"); + PerformanceTimer perfTimer("overlays"); _overlays.update(deltaTime); } { - PerformanceTimer perfTimer("idle/update/emit simulating"); + PerformanceTimer perfTimer("emitSimulating"); // let external parties know we're updating emit simulating(deltaTime); } } void Application::updateMyAvatar(float deltaTime) { - PerformanceTimer perfTimer("updateMyAvatar"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatar()"); - { - PerformanceTimer perfTimer("updateMyAvatar/_myAvatar->update()"); - _myAvatar->update(deltaTime); - } + _myAvatar->update(deltaTime); { // send head/hand data to the avatar mixer and voxel server - PerformanceTimer perfTimer("updateMyAvatar/sendToAvatarMixer"); + PerformanceTimer perfTimer("send"); QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData); packet.append(_myAvatar->toByteArray()); controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer); @@ -2121,13 +2121,13 @@ void Application::updateMyAvatar(float deltaTime) { // actually need to calculate the view frustum planes to send these details // to the server. { - PerformanceTimer perfTimer("updateMyAvatar/loadViewFrustum"); + PerformanceTimer perfTimer("loadViewFrustum"); loadViewFrustum(_myCamera, _viewFrustum); } // Update my voxel servers with my current voxel query... { - PerformanceTimer perfTimer("updateMyAvatar/queryOctree"); + PerformanceTimer perfTimer("queryOctree"); quint64 now = usecTimestampNow(); quint64 sinceLastQuery = now - _lastQueriedTime; const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; @@ -2460,7 +2460,7 @@ glm::vec3 Application::getSunDirection() { } void Application::updateShadowMap() { - PerformanceTimer perfTimer("paintGL/updateShadowMap"); + PerformanceTimer perfTimer("shadowMap"); QOpenGLFramebufferObject* fbo = _textureCache.getShadowFramebufferObject(); fbo->bind(); glEnable(GL_DEPTH_TEST); @@ -2622,7 +2622,7 @@ QImage Application::renderAvatarBillboard() { } void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { - PerformanceTimer perfTimer("paintGL/displaySide"); + PerformanceTimer perfTimer("display"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); // transform by eye offset @@ -2656,7 +2656,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // Setup 3D lights (after the camera transform, so that they are positioned in world space) { - PerformanceTimer perfTimer("paintGL/displaySide/setupWorldLight"); + PerformanceTimer perfTimer("lights"); setupWorldLight(); } @@ -2675,7 +2675,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("paintGL/displaySide/stars"); + PerformanceTimer perfTimer("stars"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... stars..."); if (!_stars.isStarsLoaded()) { @@ -2704,7 +2704,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // draw the sky dome if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { - PerformanceTimer perfTimer("paintGL/displaySide/atmosphere"); + PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); _environment.renderAtmospheres(whichCamera); @@ -2725,13 +2725,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // draw the audio reflector overlay { - PerformanceTimer perfTimer("paintGL/displaySide/audioReflector"); + PerformanceTimer perfTimer("audio"); _audioReflector.render(); } // Draw voxels if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { - PerformanceTimer perfTimer("paintGL/displaySide/voxels"); + PerformanceTimer perfTimer("voxels"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); _voxels.render(); @@ -2739,14 +2739,14 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // also, metavoxels if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { - PerformanceTimer perfTimer("paintGL/displaySide/metavoxels"); + PerformanceTimer perfTimer("metavoxels"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... metavoxels..."); _metavoxels.render(); } if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) { - PerformanceTimer perfTimer("paintGL/displaySide/buckyBalls"); + PerformanceTimer perfTimer("buckyBalls"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... bucky balls..."); _buckyBalls.render(); @@ -2754,7 +2754,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // render particles... if (Menu::getInstance()->isOptionChecked(MenuOption::Particles)) { - PerformanceTimer perfTimer("paintGL/displaySide/particles"); + PerformanceTimer perfTimer("particles"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... particles..."); _particles.render(); @@ -2762,7 +2762,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // render models... if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) { - PerformanceTimer perfTimer("paintGL/displaySide/models"); + PerformanceTimer perfTimer("models"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... models..."); _models.render(); @@ -2770,7 +2770,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // render the ambient occlusion effect if enabled if (Menu::getInstance()->isOptionChecked(MenuOption::AmbientOcclusion)) { - PerformanceTimer perfTimer("paintGL/displaySide/AmbientOcclusion"); + PerformanceTimer perfTimer("ambientOcclusion"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... AmbientOcclusion..."); _ambientOcclusionEffect.render(); @@ -2785,9 +2785,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR); { - PerformanceTimer perfTimer("paintGL/displaySide/renderAvatars"); + PerformanceTimer perfTimer("avatars"); - if (1 || OculusManager::isConnected()) { + if (OculusManager::isConnected()) { OculusManager::renderLaserPointer(); } @@ -2796,14 +2796,15 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (!selfAvatarOnly) { // Render the world box - if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - PerformanceTimer perfTimer("paintGL/displaySide/renderWorldBox"); + if (whichCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats) && + Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { + PerformanceTimer perfTimer("worldBox"); renderWorldBox(); } // view frustum for debugging if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum) && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceTimer perfTimer("paintGL/displaySide/ViewFrustum"); + PerformanceTimer perfTimer("viewFrustum"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... renderViewFrustum..."); renderViewFrustum(_viewFrustum); @@ -2811,7 +2812,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // render voxel fades if they exist if (_voxelFades.size() > 0) { - PerformanceTimer perfTimer("paintGL/displaySide/voxel fades"); + PerformanceTimer perfTimer("voxelFades"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxel fades..."); _voxelFadesLock.lockForWrite(); @@ -2828,13 +2829,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // give external parties a change to hook in { - PerformanceTimer perfTimer("paintGL/displaySide/inWorldInterface"); + PerformanceTimer perfTimer("inWorldInterface"); emit renderingInWorldInterface(); } // render JS/scriptable overlays { - PerformanceTimer perfTimer("paintGL/displaySide/3dOverlays"); + PerformanceTimer perfTimer("3dOverlays"); _overlays.render3D(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 11f406abf0..321a43d548 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -317,6 +317,7 @@ public slots: void nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec); void setRenderVoxels(bool renderVoxels); + void setLowVelocityFilter(bool lowVelocityFilter); void doKillLocalVoxels(); void loadDialog(); void loadScriptURLDialog(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c0c25bb00d..2c9f7b9c28 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -276,6 +276,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, appInstance, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::UserInterface, Qt::Key_Slash, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, false, @@ -326,7 +327,7 @@ Menu::Menu() : addDisabledActionAndSeparator(viewMenu, "Stats"); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Percent); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); @@ -407,9 +408,6 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); - QMenu* oculusOptionsMenu = developerMenu->addMenu("Oculus Options"); - addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true); - QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); @@ -421,6 +419,13 @@ Menu::Menu() : true, appInstance->getSixenseManager(), SLOT(setFilter(bool))); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, + MenuOption::LowVelocityFilter, + 0, + true, + appInstance, + SLOT(setLowVelocityFilter(bool))); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e8146f8038..a2e1dd0c20 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -350,7 +350,6 @@ namespace MenuOption { const QString DisplayModelBounds = "Display Model Bounds"; const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; - const QString DisplayOculusOverlays = "Display Oculus Overlays"; const QString DisplayTimingDetails = "Display Timing Details"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString EchoLocalAudio = "Echo Local Audio"; @@ -368,6 +367,7 @@ namespace MenuOption { const QString Faceplus = "Faceplus"; const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; + const QString LowVelocityFilter = "Low Velocity Filter"; const QString FirstPerson = "First Person"; const QString FrameTimer = "Show Timer"; const QString FrustumRenderMode = "Render Mode"; @@ -439,6 +439,7 @@ namespace MenuOption { const QString UploadAttachment = "Upload Attachment Model"; const QString UploadHead = "Upload Head Model"; const QString UploadSkeleton = "Upload Skeleton Model"; + const QString UserInterface = "User Interface"; const QString Visage = "Visage"; const QString VoxelMode = "Cycle Voxel Mode"; const QString Voxels = "Voxels"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9bb54efe20..3221e75e56 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -14,12 +14,13 @@ #include #include #include - -#include -#include -#include +#include #include +#include +#include +#include +#include #include "Application.h" #include "Avatar.h" @@ -60,7 +61,8 @@ Avatar::Avatar() : _moving(false), _collisionGroups(0), _initialized(false), - _shouldRenderBillboard(true) + _shouldRenderBillboard(true), + _numLocalLights(1) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -81,6 +83,23 @@ void Avatar::init() { _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); initializeHair(); + + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + _localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f); + _localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f); + } + + glm::vec3 darkGrayColor(0.3f, 0.3f, 0.3f); + glm::vec3 greenColor(0.0f, 1.0f, 0.0f); + glm::vec3 directionX(1.0f, 0.0f, 0.0f); + glm::vec3 directionY(0.0f, 1.0f, 0.0f); + + // initialize local lights + _localLightColors[0] = darkGrayColor; + _localLightColors[1] = greenColor; + + _localLightDirections[0] = directionX; + _localLightDirections[1] = directionY; } glm::vec3 Avatar::getChestPosition() const { @@ -99,6 +118,7 @@ float Avatar::getLODDistance() const { } void Avatar::simulate(float deltaTime) { + PerformanceTimer perfTimer("simulate"); if (_scale != _targetScale) { setScale(_targetScale); } @@ -118,29 +138,36 @@ void Avatar::simulate(float deltaTime) { bool inViewFrustum = Application::getInstance()->getViewFrustum()->sphereInFrustum(_position, boundingRadius) != ViewFrustum::OUTSIDE; - getHand()->simulate(deltaTime, false); + { + PerformanceTimer perfTimer("hand"); + getHand()->simulate(deltaTime, false); + } _skeletonModel.setLODDistance(getLODDistance()); if (!_shouldRenderBillboard && inViewFrustum) { if (_hasNewJointRotations) { + PerformanceTimer perfTimer("skeleton"); for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData.at(i); _skeletonModel.setJointState(i, data.valid, data.rotation); } _skeletonModel.simulate(deltaTime); } - _skeletonModel.simulate(deltaTime, _hasNewJointRotations); - simulateAttachments(deltaTime); - _hasNewJointRotations = false; + { + PerformanceTimer perfTimer("head"); + _skeletonModel.simulate(deltaTime, _hasNewJointRotations); + simulateAttachments(deltaTime); + _hasNewJointRotations = false; - glm::vec3 headPosition = _position; - _skeletonModel.getHeadPosition(headPosition); - Head* head = getHead(); - head->setPosition(headPosition); - head->setScale(_scale); - head->simulate(deltaTime, false, _shouldRenderBillboard); - + glm::vec3 headPosition = _position; + _skeletonModel.getHeadPosition(headPosition); + Head* head = getHead(); + head->setPosition(headPosition); + head->setScale(_scale); + head->simulate(deltaTime, false, _shouldRenderBillboard); + } if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { + PerformanceTimer perfTimer("hair"); simulateHair(deltaTime); } @@ -224,7 +251,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { const float GLOW_DISTANCE = 20.0f; const float GLOW_MAX_LOUDNESS = 2500.0f; const float MAX_GLOW = 0.5f; - + float GLOW_FROM_AVERAGE_LOUDNESS = ((this == Application::getInstance()->getAvatar()) ? 0.0f : MAX_GLOW * getHeadData()->getAudioLoudness() / GLOW_MAX_LOUDNESS); @@ -235,7 +262,23 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderMode == NORMAL_RENDER_MODE ? 1.0f : GLOW_FROM_AVERAGE_LOUDNESS; - + + + // local lights directions and colors + getSkeletonModel().setNumLocalLights(_numLocalLights); + getHead()->getFaceModel().setNumLocalLights(_numLocalLights); + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { + glm::vec3 normalized = glm::normalize(_localLightDirections[i]); + + // body + getSkeletonModel().setLocalLightColor(_localLightColors[i], i); + getSkeletonModel().setLocalLightDirection(normalized, i); + + // head + getHead()->getFaceModel().setLocalLightColor(_localLightColors[i], i); + getHead()->getFaceModel().setLocalLightDirection(_localLightDirections[i], i); + } + // render body if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { renderBody(renderMode, glowLevel); @@ -611,7 +654,6 @@ void Avatar::initializeHair() { } } - qDebug() << "Initialize Hair"; } bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const { @@ -1115,3 +1157,29 @@ void Avatar::setShowDisplayName(bool showDisplayName) { } +void Avatar::setLocalLightDirection(const glm::vec3& direction, int lightIndex) { + _localLightDirections[lightIndex] = direction; + qDebug( "set light %d direction ( %f, %f, %f )\n", lightIndex, direction.x, direction.y, direction.z ); +} + +void Avatar::setLocalLightColor(const glm::vec3& color, int lightIndex) { + _localLightColors[lightIndex] = color; + qDebug( "set light %d color ( %f, %f, %f )\n", lightIndex, color.x, color.y, color.z ); +} + +void Avatar::addLocalLight() { + if (_numLocalLights + 1 <= MAX_LOCAL_LIGHTS) { + ++_numLocalLights; + } + + qDebug("ADD LOCAL LIGHT (numLocalLights = %d)\n", _numLocalLights); +} + +void Avatar::removeLocalLight() { + if (_numLocalLights - 1 >= 0) { + --_numLocalLights; + } + + qDebug("REMOVE LOCAL LIGHT (numLocalLights = %d)\n", _numLocalLights); +} + diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 369cd7e688..8c14fc9ed0 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -155,7 +155,11 @@ public: public slots: void updateCollisionGroups(); - + void setLocalLightDirection(const glm::vec3& direction, int lightIndex); + void setLocalLightColor(const glm::vec3& color, int lightIndex); + void addLocalLight(); + void removeLocalLight(); + signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); @@ -176,9 +180,14 @@ protected: glm::vec3 _mouseRayDirection; float _stringLength; bool _moving; ///< set when position is changing - + quint32 _collisionGroups; - + + // always-present local lighting for the avatar + glm::vec3 _localLightDirections[MAX_LOCAL_LIGHTS]; + glm::vec3 _localLightColors[MAX_LOCAL_LIGHTS]; + int _numLocalLights; + // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 59f31388f8..86ec7c2680 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -41,9 +41,13 @@ void AvatarManager::init() { } void AvatarManager::updateOtherAvatars(float deltaTime) { + if (_avatarHash.size() > 1) { + return; + } bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); + PerformanceTimer perfTimer("otherAvatars"); Application* applicationInstance = Application::getInstance(); glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 044e1a8a8d..04e7b628c9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -108,15 +108,10 @@ void MyAvatar::reset() { } void MyAvatar::update(float deltaTime) { - PerformanceTimer perfTimer("MyAvatar::update/"); Head* head = getHead(); head->relaxLean(deltaTime); - { - PerformanceTimer perfTimer("MyAvatar::update/updateFromTrackers"); - updateFromTrackers(deltaTime); - } + updateFromTrackers(deltaTime); if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { - PerformanceTimer perfTimer("MyAvatar::update/moveWithLean"); // Faceshift drive is enabled, set the avatar drive based on the head position moveWithLean(); } @@ -127,19 +122,14 @@ void MyAvatar::update(float deltaTime) { head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) { - PerformanceTimer perfTimer("MyAvatar::update/gravityWork"); setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); } - { - PerformanceTimer perfTimer("MyAvatar::update/simulate"); - simulate(deltaTime); - } + simulate(deltaTime); } void MyAvatar::simulate(float deltaTime) { - PerformanceTimer perfTimer("MyAvatar::simulate"); - + PerformanceTimer perfTimer("simulate"); if (_scale != _targetScale) { float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; setScale(scale); @@ -150,31 +140,28 @@ void MyAvatar::simulate(float deltaTime) { _handState = HAND_STATE_NULL; { - PerformanceTimer perfTimer("MyAvatar::simulate/updateOrientation"); + PerformanceTimer perfTimer("transform"); updateOrientation(deltaTime); - } - { - PerformanceTimer perfTimer("MyAvatar::simulate/updatePosition"); updatePosition(deltaTime); } { - PerformanceTimer perfTimer("MyAvatar::simulate/hand Collision,simulate"); + PerformanceTimer perfTimer("hand"); // update avatar skeleton and simulate hand and head getHand()->simulate(deltaTime, true); } { - PerformanceTimer perfTimer("MyAvatar::simulate/_skeletonModel.simulate()"); + PerformanceTimer perfTimer("skeleton"); _skeletonModel.simulate(deltaTime); } { - PerformanceTimer perfTimer("MyAvatar::simulate/simulateAttachments"); + PerformanceTimer perfTimer("attachments"); simulateAttachments(deltaTime); } { - PerformanceTimer perfTimer("MyAvatar::simulate/copy joints"); + PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _jointData.resize(_skeletonModel.getJointStateCount()); for (int i = 0; i < _jointData.size(); i++) { @@ -184,7 +171,7 @@ void MyAvatar::simulate(float deltaTime) { } { - PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); + PerformanceTimer perfTimer("head"); Head* head = getHead(); glm::vec3 headPosition; if (!_skeletonModel.getHeadPosition(headPosition)) { @@ -196,7 +183,7 @@ void MyAvatar::simulate(float deltaTime) { } { - PerformanceTimer perfTimer("MyAvatar::simulate/hair Simulate"); + PerformanceTimer perfTimer("hair"); if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { simulateHair(deltaTime); foreach (Hair* hair, _hairs) { @@ -206,7 +193,7 @@ void MyAvatar::simulate(float deltaTime) { } { - PerformanceTimer perfTimer("MyAvatar::simulate/ragdoll"); + PerformanceTimer perfTimer("ragdoll"); if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { const int minError = 0.01f; const float maxIterations = 10; @@ -219,7 +206,7 @@ void MyAvatar::simulate(float deltaTime) { // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionGroups != 0) { - PerformanceTimer perfTimer("MyAvatar::simulate/_collisionGroups"); + PerformanceTimer perfTimer("collisions"); Camera* myCamera = Application::getInstance()->getCamera(); float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; @@ -228,18 +215,18 @@ void MyAvatar::simulate(float deltaTime) { radius *= COLLISION_RADIUS_SCALAR; } if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) { - PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithEnvironment"); + PerformanceTimer perfTimer("environment"); updateCollisionWithEnvironment(deltaTime, radius); } if (_collisionGroups & COLLISION_GROUP_VOXELS) { - PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithVoxels"); + PerformanceTimer perfTimer("voxels"); updateCollisionWithVoxels(deltaTime, radius); } else { _trapDuration = 0.0f; } /* TODO: Andrew to make this work if (_collisionGroups & COLLISION_GROUP_AVATARS) { - PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithAvatars"); + PerformanceTimer perfTimer("avatars"); updateCollisionWithAvatars(deltaTime); } */ @@ -916,7 +903,6 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend } float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) { - PerformanceTimer perfTimer("MyAvatar::computeDistanceToFloor()"); glm::vec3 direction = -_worldUpDirection; OctreeElement* elementHit; // output from findRayIntersection float distance = FLT_MAX; // output from findRayIntersection @@ -982,7 +968,6 @@ void MyAvatar::updateOrientation(float deltaTime) { const float NEARBY_FLOOR_THRESHOLD = 5.0f; void MyAvatar::updatePosition(float deltaTime) { - PerformanceTimer perfTimer("MyAvatar::updatePosition"); float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 76d0d45efa..7a88d890ac 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -109,6 +109,7 @@ public: void resetShapePositionsToDefaultPose(); // DEBUG method void renderRagdoll(); + protected: // virtual overrrides from Ragdoll diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 59f2c245df..a7d50814e2 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -11,6 +11,7 @@ #include +#include #include #include "Application.h" @@ -75,6 +76,7 @@ void Faceshift::update() { if (!isActive()) { return; } + PerformanceTimer perfTimer("faceshift"); // get the euler angles relative to the window glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))))); diff --git a/interface/src/devices/JoystickManager.cpp b/interface/src/devices/JoystickManager.cpp index 005505441c..8169c6d06e 100644 --- a/interface/src/devices/JoystickManager.cpp +++ b/interface/src/devices/JoystickManager.cpp @@ -12,9 +12,10 @@ #include #include - #include +#include + #include "JoystickManager.h" using namespace std; @@ -46,6 +47,7 @@ JoystickManager::~JoystickManager() { void JoystickManager::update() { #ifdef HAVE_SDL + PerformanceTimer perfTimer("joystick"); SDL_JoystickUpdate(); for (int i = 0; i < _joystickStates.size(); i++) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 804ebd1873..2e6d4cb9a2 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -269,7 +269,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p // We only need to render the overlays to a texture once, then we just render the texture on the hemisphere // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() applicationOverlay.renderOverlay(true); - const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::DisplayOculusOverlays); + const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface); //Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) { diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index e96f4f04d5..195de5705d 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Application.h" #include "PrioVR.h" @@ -166,6 +167,7 @@ void PrioVR::update(float deltaTime) { if (!_skeletalDevice) { return; } + PerformanceTimer perfTimer("PrioVR"); unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 33d485ea84..b9a736887e 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -11,6 +11,8 @@ #include +#include + #include "Application.h" #include "SixenseManager.h" #include "devices/OculusManager.h" @@ -33,6 +35,7 @@ SixenseManager::SixenseManager() { #ifdef HAVE_SIXENSE _lastMovement = 0; _amountMoved = glm::vec3(0.0f); + _lowVelocityFilter = false; _calibrationState = CALIBRATION_STATE_IDLE; // By default we assume the _neckBase (in orb frame) is as high above the orb @@ -61,10 +64,8 @@ SixenseManager::~SixenseManager() { void SixenseManager::setFilter(bool filter) { #ifdef HAVE_SIXENSE if (filter) { - qDebug("Sixense Filter ON"); sixenseSetFilterEnabled(1); } else { - qDebug("Sixense Filter OFF"); sixenseSetFilterEnabled(0); } #endif @@ -85,7 +86,10 @@ void SixenseManager::update(float deltaTime) { if (sixenseGetNumActiveControllers() == 0) { _hydrasConnected = false; return; - } else if (!_hydrasConnected) { + } + + PerformanceTimer perfTimer("sixense"); + if (!_hydrasConnected) { _hydrasConnected = true; UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } @@ -161,17 +165,21 @@ void SixenseManager::update(float deltaTime) { } palm->setRawVelocity(rawVelocity); // meters/sec - // Use a velocity sensitive filter to damp small motions and preserve large ones with - // no latency. - float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); - palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter)); - // adjustment for hydra controllers fit into hands float sign = (i == 0) ? -1.0f : 1.0f; rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f)); - palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter)); - + if (_lowVelocityFilter) { + // Use a velocity sensitive filter to damp small motions and preserve large ones with + // no latency. + float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); + palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter)); + palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter)); + } else { + palm->setRawPosition(position); + palm->setRawRotation(rotation); + } + // use the velocity to determine whether there's any movement (if the hand isn't new) const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f; _amountMoved += rawVelocity * deltaTime; diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 8ca27ef77c..bae9e1c6d6 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -47,6 +47,7 @@ public: public slots: void setFilter(bool filter); + void setLowVelocityFilter(bool lowVelocityFilter) { _lowVelocityFilter = lowVelocityFilter; }; private: #ifdef HAVE_SIXENSE @@ -80,6 +81,8 @@ private: bool _bumperPressed[2]; int _oldX[2]; int _oldY[2]; + + bool _lowVelocityFilter; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 25d3ff892a..3b42c03f2d 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -100,6 +100,7 @@ void TV3DManager::display(Camera& whichCamera) { // We only need to render the overlays to a texture once, then we just render the texture as a quad // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() applicationOverlay.renderOverlay(true); + const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface); if (glowEnabled) { Application::getInstance()->getGlowEffect()->prepare(); @@ -128,7 +129,9 @@ void TV3DManager::display(Camera& whichCamera) { glLoadIdentity(); Application::getInstance()->displaySide(whichCamera); - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + if (displayOverlays) { + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + } } glPopMatrix(); glDisable(GL_SCISSOR_TEST); @@ -154,7 +157,9 @@ void TV3DManager::display(Camera& whichCamera) { glLoadIdentity(); Application::getInstance()->displaySide(whichCamera); - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + if (displayOverlays) { + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); + } } glPopMatrix(); glDisable(GL_SCISSOR_TEST); diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 119d89654a..5de2746b07 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -11,6 +11,7 @@ #include +#include #include #include @@ -128,6 +129,7 @@ void Visage::update() { if (!_active) { return; } + PerformanceTimer perfTimer("visage"); _headRotation = glm::quat(glm::vec3(-_data->faceRotation[0], -_data->faceRotation[1], _data->faceRotation[2])); _headTranslation = (glm::vec3(_data->faceTranslation[0], _data->faceTranslation[1], _data->faceTranslation[2]) - _headOrigin) * TRANSLATION_SCALE; diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index c163136956..1fdebb66d7 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -121,7 +121,7 @@ static void maybeRelease(QOpenGLFramebufferObject* fbo) { } QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) { - PerformanceTimer perfTimer("paintGL/glowEffect"); + PerformanceTimer perfTimer("glowEffect"); QOpenGLFramebufferObject* primaryFBO = Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject(); primaryFBO->release(); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 723297f6b4..8b8557709c 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1488,14 +1488,19 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re if (cascadedShadows) { program->setUniform(skinLocations->shadowDistances, Application::getInstance()->getShadowDistances()); } - } else { + + // local light uniforms + skinProgram->setUniformValue("numLocalLights", _numLocalLights); + skinProgram->setUniformArray("localLightDirections", _localLightDirections, MAX_LOCAL_LIGHTS); + skinProgram->setUniformArray("localLightColors", _localLightColors, MAX_LOCAL_LIGHTS); + } else { glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); program->bind(); if (cascadedShadows) { program->setUniform(shadowDistancesLocation, Application::getInstance()->getShadowDistances()); } } - + if (mesh.blendshapes.isEmpty()) { if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); @@ -1622,6 +1627,20 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } } +void Model::setLocalLightDirection(const glm::vec3& direction, int lightIndex) { + assert(lightIndex >= 0 && lightIndex < MAX_LOCAL_LIGHTS); + _localLightDirections[lightIndex] = direction; +} + +void Model::setLocalLightColor(const glm::vec3& color, int lightIndex) { + assert(lightIndex >= 0 && lightIndex < MAX_LOCAL_LIGHTS); + _localLightColors[lightIndex] = color; +} + +void Model::setNumLocalLights(int numLocalLights) { + _numLocalLights = numLocalLights; +} + void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 5e29b869e0..9e2e0d8348 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -32,6 +32,8 @@ class Shape; typedef QSharedPointer AnimationHandlePointer; typedef QWeakPointer WeakAnimationHandlePointer; +const int MAX_LOCAL_LIGHTS = 2; + /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public PhysicsEntity { Q_OBJECT @@ -143,6 +145,10 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); + void setLocalLightDirection(const glm::vec3& direction, int lightIndex); + void setLocalLightColor(const glm::vec3& color, int lightIndex); + void setNumLocalLights(int numLocalLights); + protected: QSharedPointer _geometry; @@ -158,6 +164,10 @@ protected: bool _showTrueJointTransforms; int _rootIndex; + glm::vec3 _localLightDirections[MAX_LOCAL_LIGHTS]; + glm::vec3 _localLightColors[MAX_LOCAL_LIGHTS]; + int _numLocalLights; + QVector _jointStates; class MeshState { diff --git a/interface/src/renderer/ProgramObject.cpp b/interface/src/renderer/ProgramObject.cpp index b88be69f07..16b3461ad0 100644 --- a/interface/src/renderer/ProgramObject.cpp +++ b/interface/src/renderer/ProgramObject.cpp @@ -10,6 +10,7 @@ // #include "ProgramObject.h" +#include ProgramObject::ProgramObject(QObject* parent) : QGLShaderProgram(parent) { } @@ -22,3 +23,17 @@ void ProgramObject::setUniform(const char* name, const glm::vec3& value) { setUniformValue(name, value.x, value.y, value.z); } +void ProgramObject::setUniformArray(const char* name, const glm::vec3* values, int count) { + GLfloat* floatVal = new GLfloat[count*3]; + int index = 0; + for (int i = 0; i < count; i++) { + assert(index < count*3); + const float* valPtr = glm::value_ptr(values[i]); + floatVal[index++] = valPtr[0]; + floatVal[index++] = valPtr[1]; + floatVal[index++] = valPtr[2]; + } + + setUniformValueArray(name, floatVal, count, 3); + delete[] floatVal; +} diff --git a/interface/src/renderer/ProgramObject.h b/interface/src/renderer/ProgramObject.h index 21e01ac8b3..8e66ce9bc9 100644 --- a/interface/src/renderer/ProgramObject.h +++ b/interface/src/renderer/ProgramObject.h @@ -23,6 +23,7 @@ public: void setUniform(int location, const glm::vec3& value); void setUniform(const char* name, const glm::vec3& value); + void setUniformArray(const char* name, const glm::vec3* values, int count); }; #endif // hifi_ProgramObject_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index c064bb040a..caf103c00c 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -40,7 +40,6 @@ ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _alpha(1.0f), - _active(true), _crosshairTexture(0) { memset(_reticleActive, 0, sizeof(_reticleActive)); @@ -70,8 +69,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); - //Handle fadeing and deactivation/activation of UI - if (_active) { + //Handle fading and deactivation/activation of UI + if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { _alpha += FADE_SPEED; if (_alpha > 1.0f) { _alpha = 1.0f; @@ -581,7 +580,8 @@ void ApplicationOverlay::renderControllerPointers() { if (palmData->getTrigger() == 1.0f) { if (!triggerPressed[index]) { if (bumperPressed[index]) { - _active = !_active; + Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, + !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); } triggerPressed[index] = true; } @@ -591,7 +591,8 @@ void ApplicationOverlay::renderControllerPointers() { if ((controllerButtons & BUTTON_FWD)) { if (!bumperPressed[index]) { if (triggerPressed[index]) { - _active = !_active; + Menu::getInstance()->setIsOptionChecked(MenuOption::UserInterface, + !Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)); } bumperPressed[index] = true; } @@ -1188,6 +1189,14 @@ void ApplicationOverlay::renderTexturedHemisphere() { } +void ApplicationOverlay::resize() { + if (_framebufferObject != NULL) { + delete _framebufferObject; + _framebufferObject = NULL; + } + // _framebufferObject is recreated at the correct size the next time it is accessed via getFramebufferObject(). +} + QOpenGLFramebufferObject* ApplicationOverlay::getFramebufferObject() { QSize size = Application::getInstance()->getGLWidget()->size(); if (!_framebufferObject || _framebufferObject->size() != size) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index fc282c2903..a956858051 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -32,7 +32,11 @@ public: void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; void getClickLocation(int &x, int &y) const; +<<<<<<< HEAD QPoint getOculusPalmClickLocation(const PalmData *palm) const; +======= + void resize(); +>>>>>>> af6704a83cec1d69b3dc3e1145238919fcb82933 // Getters QOpenGLFramebufferObject* getFramebufferObject(); @@ -69,7 +73,6 @@ private: float _magSizeMult[NUMBER_OF_MAGNIFIERS]; float _alpha; - bool _active; GLuint _crosshairTexture; }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 379dd35df7..3de21b449b 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -601,6 +601,8 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color); } + PerformanceTimer::tallyAllTimerRecords(); + // TODO: the display of these timing details should all be moved to JavaScript if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { // Timing details... diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index ca79967109..6c19cfd4c2 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include "Application.h" #include "InterfaceConfig.h" #include "Menu.h" @@ -57,6 +59,8 @@ GLubyte identityIndicesRight[] = { 1, 2, 6, 1, 6, 5 }; GLubyte identityIndicesFront[] = { 0, 2, 1, 0, 3, 2 }; GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 }; +static glm::vec3 grayColor = glm::vec3(0.3f, 0.3f, 0.3f); + VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree) : NodeData(), _treeScale(treeScale), @@ -67,7 +71,10 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree) _inOcclusions(false), _showCulledSharedFaces(false), _usePrimitiveRenderer(false), - _renderer(0) + _renderer(0), + _drawHaze(false), + _farHazeDistance(300.0f), + _hazeColor(grayColor) { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; @@ -373,6 +380,7 @@ void VoxelSystem::cleanupVoxelMemory() { delete[] _readVoxelDirtyArray; _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL; _readArraysLock.unlock(); + } } @@ -454,6 +462,7 @@ void VoxelSystem::initVoxelMemory() { _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels]; _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels); + } else { // Global Normals mode uses a technique of not including normals on any voxel vertices, and instead @@ -521,13 +530,23 @@ void VoxelSystem::initVoxelMemory() { _shadowDistancesLocation = _cascadedShadowMapProgram.uniformLocation("shadowDistances"); _cascadedShadowMapProgram.release(); } + } _renderer = new PrimitiveRenderer(_maxVoxels); _initialized = true; - + _writeArraysLock.unlock(); _readArraysLock.unlock(); + + // fog for haze + if (_drawHaze) { + GLfloat fogColor[] = {_hazeColor.x, _hazeColor.y, _hazeColor.z, 1.0f}; + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogfv(GL_FOG_COLOR, fogColor); + glFogf(GL_FOG_START, 0.0f); + glFogf(GL_FOG_END, _farHazeDistance); + } } int VoxelSystem::parseData(const QByteArray& packet) { @@ -1114,6 +1133,7 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo node->setBufferIndex(nodeIndex); node->setVoxelSystem(this); } + // populate the array with points for the 8 vertices and RGB color for each added vertex updateArraysDetails(nodeIndex, startVertex, voxelScale, node->getColor()); } @@ -1131,11 +1151,13 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo void VoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color) { - + if (_initialized && nodeIndex <= _maxVoxels) { _writeVoxelDirtyArray[nodeIndex] = true; - + if (_useVoxelShader) { + // write in position, scale, and color for the voxel + if (_writeVoxelShaderData) { VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex]; writeVerticesAt->x = startVertex.x * TREE_SCALE; @@ -1157,6 +1179,7 @@ void VoxelSystem::updateArraysDetails(glBufferIndex nodeIndex, const glm::vec3& } } } + } } @@ -1407,6 +1430,10 @@ void VoxelSystem::render() { } } else if (!_usePrimitiveRenderer) { + if (_drawHaze) { + glEnable(GL_FOG); + } + PerformanceWarning warn(showWarnings, "render().. TRIANGLES..."); { @@ -1478,6 +1505,10 @@ void VoxelSystem::render() { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } + + if (_drawHaze) { + glDisable(GL_FOG); + } } else { applyScaleAndBindProgram(texture); diff --git a/interface/src/voxels/VoxelSystem.h b/interface/src/voxels/VoxelSystem.h index ae8752605a..9f61e0579c 100644 --- a/interface/src/voxels/VoxelSystem.h +++ b/interface/src/voxels/VoxelSystem.h @@ -273,7 +273,11 @@ private: static unsigned short _sSwizzledOcclusionBits[64]; ///< Swizzle value of bit pairs of the value of index static unsigned char _sOctantIndexToBitMask[8]; ///< Map octant index to partition mask static unsigned char _sOctantIndexToSharedBitMask[8][8]; ///< Map octant indices to shared partition mask - + + // haze + bool _drawHaze; + float _farHazeDistance; + glm::vec3 _hazeColor; }; #endif // hifi_VoxelSystem_h diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 2c594fc1ca..536cfc9dfb 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -79,7 +79,7 @@ ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { return channel; } -int DatagramSequencer::startPacketGroup(int desiredPackets) { +int DatagramSequencer::notePacketGroup(int desiredPackets) { // figure out how much data we have enqueued and increase the number of packets desired int totalAvailable = 0; foreach (ReliableChannel* channel, _reliableOutputChannels) { diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index b85916b561..b6dce464f7 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -108,10 +108,10 @@ public: /// Returns the intput channel at the specified index, creating it if necessary. ReliableChannel* getReliableInputChannel(int index = 0); - /// Starts a packet group. + /// Notes that we're sending a group of packets. /// \param desiredPackets the number of packets we'd like to write in the group /// \return the number of packets to write in the group - int startPacketGroup(int desiredPackets = 1); + int notePacketGroup(int desiredPackets = 1); /// Starts a new packet for transmission. /// \return a reference to the Bitstream to use for writing to the packet diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp index 666ffe52d9..420a52ef95 100644 --- a/libraries/metavoxels/src/Endpoint.cpp +++ b/libraries/metavoxels/src/Endpoint.cpp @@ -39,9 +39,12 @@ Endpoint::~Endpoint() { } void Endpoint::update() { - Bitstream& out = _sequencer.startPacket(); - writeUpdateMessage(out); - _sequencer.endPacket(); + int packetsToSend = _sequencer.notePacketGroup(); + for (int i = 0; i < packetsToSend; i++) { + Bitstream& out = _sequencer.startPacket(); + writeUpdateMessage(out); + _sequencer.endPacket(); + } } int Endpoint::parseData(const QByteArray& packet) { diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index e69794917f..f3ea1ae8c5 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -87,7 +87,8 @@ void MetavoxelClientManager::updateClient(MetavoxelClient* client) { MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) : Endpoint(node, new PacketRecord(), new PacketRecord()), _manager(manager), - _reliableDeltaChannel(NULL) { + _reliableDeltaChannel(NULL), + _reliableDeltaID(0) { connect(_sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX), SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); @@ -139,10 +140,16 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } } else if (userType == MetavoxelDeltaPendingMessage::Type) { - if (!_reliableDeltaChannel) { + // check the id to make sure this is not a delta we've already processed + int id = message.value().id; + if (id > _reliableDeltaID) { + _reliableDeltaID = id; _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); + PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + _remoteDataLOD = receiveRecord->getLOD(); + _remoteData = receiveRecord->getData(); } } else { Endpoint::handleMessage(message, in); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 1f37b15c18..ad6c86c8fc 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -74,6 +74,7 @@ private: ReliableChannel* _reliableDeltaChannel; MetavoxelLOD _reliableDeltaLOD; + int _reliableDeltaID; }; #endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index b822f1c561..91d73c08a9 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -64,6 +64,10 @@ DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaMessage) /// A message indicating that metavoxel delta information is being sent on a reliable channel. class MetavoxelDeltaPendingMessage { STREAMABLE + +public: + + STREAM int id; }; DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) diff --git a/libraries/shared/src/PerfStat.cpp b/libraries/shared/src/PerfStat.cpp index 4dca3f3d49..b811a719bc 100644 --- a/libraries/shared/src/PerfStat.cpp +++ b/libraries/shared/src/PerfStat.cpp @@ -17,6 +17,12 @@ #include "PerfStat.h" +#include "SharedUtil.h" + +// ---------------------------------------------------------------------------- +// PerformanceWarning +// ---------------------------------------------------------------------------- + // Static class members initialization here! bool PerformanceWarning::_suppressShortTimings = false; @@ -52,14 +58,50 @@ PerformanceWarning::~PerformanceWarning() { } }; +// ---------------------------------------------------------------------------- +// PerformanceTimerRecord +// ---------------------------------------------------------------------------- +const quint64 STALE_STAT_PERIOD = 4 * USECS_PER_SECOND; + +void PerformanceTimerRecord::tallyResult(const quint64& now) { + if (_numAccumulations > 0) { + _numTallies++; + _movingAverage.updateAverage(_runningTotal - _lastTotal); + _lastTotal = _runningTotal; + _numAccumulations = 0; + _expiry = now + STALE_STAT_PERIOD; + } +} + +// ---------------------------------------------------------------------------- +// PerformanceTimer +// ---------------------------------------------------------------------------- + +QString PerformanceTimer::_fullName; QMap PerformanceTimer::_records; PerformanceTimer::~PerformanceTimer() { - quint64 end = usecTimestampNow(); - quint64 elapsedusec = (end - _start); - PerformanceTimerRecord& namedRecord = _records[_name]; - namedRecord.recordResult(elapsedusec); + quint64 elapsedusec = (usecTimestampNow() - _start); + PerformanceTimerRecord& namedRecord = _records[_fullName]; + namedRecord.accumulateResult(elapsedusec); + _fullName.resize(_fullName.size() - (_name.size() + 1)); +} + +// static +void PerformanceTimer::tallyAllTimerRecords() { + QMap::iterator recordsItr = _records.begin(); + QMap::const_iterator recordsEnd = _records.end(); + quint64 now = usecTimestampNow(); + while (recordsItr != recordsEnd) { + recordsItr.value().tallyResult(now); + if (recordsItr.value().isStale(now)) { + // purge stale records + recordsItr = _records.erase(recordsItr); + } else { + ++recordsItr; + } + } } void PerformanceTimer::dumpAllTimerRecords() { diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index f849fb844c..4f94be73b1 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -25,13 +25,13 @@ class PerformanceWarning { private: - quint64 _start; - const char* _message; - bool _renderWarningsOn; - bool _alwaysDisplay; - quint64* _runningTotal; - quint64* _totalCalls; - static bool _suppressShortTimings; + quint64 _start; + const char* _message; + bool _renderWarningsOn; + bool _alwaysDisplay; + quint64* _runningTotal; + quint64* _totalCalls; + static bool _suppressShortTimings; public: PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, @@ -52,38 +52,47 @@ public: class PerformanceTimerRecord { public: - PerformanceTimerRecord() : _runningTotal(0), _totalCalls(0) {} + PerformanceTimerRecord() : _runningTotal(0), _lastTotal(0), _numAccumulations(0), _numTallies(0), _expiry(0) {} - void recordResult(quint64 elapsed) { _runningTotal += elapsed; _totalCalls++; _movingAverage.updateAverage(elapsed); } - quint64 getAverage() const { return (_totalCalls == 0) ? 0 : _runningTotal / _totalCalls; } - quint64 getMovingAverage() const { return (_totalCalls == 0) ? 0 : _movingAverage.getAverage(); } - quint64 getCount() const { return _totalCalls; } + void accumulateResult(const quint64& elapsed) { _runningTotal += elapsed; ++_numAccumulations; } + void tallyResult(const quint64& now); + bool isStale(const quint64& now) const { return now > _expiry; } + quint64 getAverage() const { return (_numTallies == 0) ? 0 : _runningTotal / _numTallies; } + quint64 getMovingAverage() const { return (_numTallies == 0) ? 0 : _movingAverage.getAverage(); } + quint64 getCount() const { return _numTallies; } private: - quint64 _runningTotal; - quint64 _totalCalls; - SimpleMovingAverage _movingAverage; + quint64 _runningTotal; + quint64 _lastTotal; + quint64 _numAccumulations; + quint64 _numTallies; + quint64 _expiry; + SimpleMovingAverage _movingAverage; }; class PerformanceTimer { public: PerformanceTimer(const QString& name) : - _start(usecTimestampNow()), - _name(name) { } + _start(0), + _name(name) { + _fullName.append("/"); + _fullName.append(_name); + _start = usecTimestampNow(); + } - quint64 elapsed() const { return (usecTimestampNow() - _start); }; - ~PerformanceTimer(); static const PerformanceTimerRecord& getTimerRecord(const QString& name) { return _records[name]; }; static const QMap& getAllTimerRecords() { return _records; }; + static void tallyAllTimerRecords(); static void dumpAllTimerRecords(); private: - quint64 _start; - QString _name; - static QMap _records; + quint64 _start; + QString _name; + static QString _fullName; + static QMap _records; }; diff --git a/libraries/shared/src/SimpleMovingAverage.cpp b/libraries/shared/src/SimpleMovingAverage.cpp index 9f7e541c9a..64198d2a06 100644 --- a/libraries/shared/src/SimpleMovingAverage.cpp +++ b/libraries/shared/src/SimpleMovingAverage.cpp @@ -14,8 +14,8 @@ SimpleMovingAverage::SimpleMovingAverage(int numSamplesToAverage) : _numSamples(0), - _average(0), - _eventDeltaAverage(0), + _average(0.0f), + _eventDeltaAverage(0.0f), WEIGHTING(1.0f / numSamplesToAverage), ONE_MINUS_WEIGHTING(1 - WEIGHTING) { @@ -45,8 +45,8 @@ int SimpleMovingAverage::updateAverage(float sample) { void SimpleMovingAverage::reset() { _numSamples = 0; - _average = 0; - _eventDeltaAverage = 0; + _average = 0.0f; + _eventDeltaAverage = 0.0f; } float SimpleMovingAverage::getEventDeltaAverage() const { @@ -55,5 +55,5 @@ float SimpleMovingAverage::getEventDeltaAverage() const { } float SimpleMovingAverage::getAverageSampleValuePerSecond() const { - return _average * (1 / getEventDeltaAverage()); + return _average * (1.0f / getEventDeltaAverage()); } diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 4132270620..0a6a5de96d 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -647,7 +647,8 @@ TestEndpoint::TestEndpoint(Mode mode) : _mode(mode), _highPriorityMessagesToSend(0.0f), _reliableMessagesToSend(0.0f), - _reliableDeltaChannel(NULL) { + _reliableDeltaChannel(NULL), + _reliableDeltaID(0) { connect(&_sequencer, SIGNAL(receivedHighPriorityMessage(const QVariant&)), SLOT(handleHighPriorityMessage(const QVariant&))); @@ -858,7 +859,7 @@ bool TestEndpoint::simulate(int iterationNumber) { bytesReceived += datagram.size(); _remainingPipelineCapacity += datagram.size(); } - int packetCount = _sequencer.startPacketGroup(); + int packetCount = _sequencer.notePacketGroup(); groupsSent++; maxPacketsPerGroup = qMax(maxPacketsPerGroup, packetCount); for (int i = 0; i < packetCount; i++) { @@ -908,7 +909,8 @@ bool TestEndpoint::simulate(int iterationNumber) { // if we're sending a reliable delta, wait until it's acknowledged if (_reliableDeltaChannel) { Bitstream& out = _sequencer.startPacket(); - out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + MetavoxelDeltaPendingMessage msg = { _reliableDeltaID }; + out << QVariant::fromValue(msg); _sequencer.endPacket(); return false; } @@ -932,7 +934,8 @@ bool TestEndpoint::simulate(int iterationNumber) { _reliableDeltaLOD = _lod; _sequencer.getOutputStream().getUnderlying().device()->seek(start); - out << QVariant::fromValue(MetavoxelDeltaPendingMessage()); + MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID }; + out << QVariant::fromValue(msg); _sequencer.endPacket(); } else { @@ -1081,15 +1084,22 @@ void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { } else if (userType == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, - _dataLOD = getLastAcknowledgedSendRecord()->getLOD()); + _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, + _remoteDataLOD = getLastAcknowledgedSendRecord()->getLOD()); + in.reset(); + _data = _remoteData; compareMetavoxelData(); } else if (userType == MetavoxelDeltaPendingMessage::Type) { - if (!_reliableDeltaChannel) { + int id = message.value().id; + if (id > _reliableDeltaID) { + _reliableDeltaID = id; _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); + PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + _remoteDataLOD = receiveRecord->getLOD(); + _remoteData = receiveRecord->getData(); } } else if (userType == QMetaType::QVariantList) { foreach (const QVariant& element, message.toList()) { @@ -1107,7 +1117,7 @@ PacketRecord* TestEndpoint::maybeCreateSendRecord() const { } PacketRecord* TestEndpoint::maybeCreateReceiveRecord() const { - return new TestReceiveRecord(_dataLOD, (_mode == METAVOXEL_SERVER_MODE) ? MetavoxelData() : _data, _remoteState); + return new TestReceiveRecord(_remoteDataLOD, _remoteData, _remoteState); } void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { @@ -1127,9 +1137,10 @@ void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { void TestEndpoint::handleReliableMessage(const QVariant& message, Bitstream& in) { if (message.userType() == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); - _data.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _dataLOD = _reliableDeltaLOD); + _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _remoteDataLOD = _reliableDeltaLOD); _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); in.clearPersistentMappings(); + _data = _remoteData; compareMetavoxelData(); _reliableDeltaChannel = NULL; return; diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 5d719ccfdf..ce357fbb90 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -79,6 +79,8 @@ private: MetavoxelData _data; MetavoxelLOD _dataLOD; + MetavoxelData _remoteData; + MetavoxelLOD _remoteDataLOD; MetavoxelLOD _lod; SharedObjectPointer _sphere; @@ -104,6 +106,7 @@ private: MetavoxelData _reliableDeltaData; MetavoxelLOD _reliableDeltaLOD; Bitstream::WriteMappings _reliableDeltaWriteMappings; + int _reliableDeltaID; }; /// A simple shared object.