diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 66b924b8ab..833b53b729 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -262,14 +262,9 @@ void AvatarMixer::broadcastAvatarData() { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - if (lastSeqToReceiver > lastSeqFromSender) { - // Did we somehow get out of order packets from the sender? - // We don't expect this to happen - in RELEASE we add this to a trackable stat - // and in DEBUG we crash on the assert - + if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { + // we got out out of order packets from the sender, track it otherNodeData->incrementNumOutOfOrderSends(); - - assert(false); } // make sure we haven't already sent this data from this sender to this receiver @@ -485,10 +480,13 @@ void AvatarMixer::sendStatsPacket() { QJsonObject avatarStats; const QString NODE_OUTBOUND_KBPS_STAT_KEY = "outbound_kbps"; + const QString NODE_INBOUND_KBPS_STAT_KEY = "inbound_kbps"; // add the key to ask the domain-server for a username replacement, if it has it avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); + avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); + avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); AvatarMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 006f58eb44..0a9be20691 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -40,11 +40,14 @@ uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& node void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar.getDisplayName(); jsonObject["full_rate_distance"] = _fullRateDistance; - jsonObject["max_avatar_distance"] = _maxAvatarDistance; - jsonObject["num_avatars_sent_last_frame"] = _numAvatarsSentLastFrame; - jsonObject["avg_other_avatar_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); - jsonObject["avg_other_avatar_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); + jsonObject["max_av_distance"] = _maxAvatarDistance; + jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame; + jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); + jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); + jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; + + jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate(); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 3dbe917ee2..e68a54c265 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -27,6 +27,7 @@ #include const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; +const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; class AvatarMixerClientData : public NodeData { Q_OBJECT diff --git a/examples/example/hmd/ipdScalingTest.js b/examples/example/hmd/ipdScalingTest.js new file mode 100644 index 0000000000..daa11170e4 --- /dev/null +++ b/examples/example/hmd/ipdScalingTest.js @@ -0,0 +1,42 @@ +// +// Created by Bradley Austin Davis on 2015/10/04 +// Copyright 2013-2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +IPDScalingTest = function() { + // Switch every 5 seconds between normal IPD and 0 IPD (in seconds) + this.UPDATE_INTERVAL = 10.0; + this.lastUpdateInterval = 0; + this.scaled = false; + + var that = this; + Script.scriptEnding.connect(function() { + that.onCleanup(); + }); + + Script.update.connect(function(deltaTime) { + that.lastUpdateInterval += deltaTime; + if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) { + that.onUpdate(that.lastUpdateInterval); + that.lastUpdateInterval = 0; + } + }); +} + +IPDScalingTest.prototype.onCleanup = function() { + HMD.setIPDScale(1.0); +} + +IPDScalingTest.prototype.onUpdate = function(deltaTime) { + this.scaled = !this.scaled; + if (this.scaled) { + HMD.ipdScale = 0.0; + } else { + HMD.ipdScale = 1.0; + } +} + +new IPDScalingTest(); \ No newline at end of file diff --git a/examples/example/hmd/pickerTest.js b/examples/example/hmd/pickerTest.js new file mode 100644 index 0000000000..96ffc6d21d --- /dev/null +++ b/examples/example/hmd/pickerTest.js @@ -0,0 +1,103 @@ +Script.include("../../libraries/utils.js"); + + +PickerTest = function() { + // Switch every 5 seconds between normal IPD and 0 IPD (in seconds) + this.UPDATE_INTERVAL = 10.0; + this.lastUpdateInterval = 0; + + this.ballId = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 0 }, + size: 0.1, + solid: true, + alpha: 1.0, + visible: true, + }); + + this.ballId2 = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + color: { red: 255, green: 0, blue: 0 }, + size: 0.05, + solid: true, + alpha: 1.0, + visible: true, + }); + + var that = this; + Script.scriptEnding.connect(function() { + that.onCleanup(); + }); + + Script.update.connect(function(deltaTime) { + that.lastUpdateInterval += deltaTime; + if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) { + that.onUpdate(that.lastUpdateInterval); + that.lastUpdateInterval = 0; + } + }); + + Controller.mousePressEvent.connect(function(event) { + that.onMousePress(event); + }); + + Controller.mouseMoveEvent.connect(function(event) { + that.onMouseMove(event); + }); + + Controller.mouseReleaseEvent.connect(function(event) { + that.onMouseRelease(event); + }); +}; + +PickerTest.prototype.onCleanup = function() { + Overlays.deleteOverlay(this.ballId) + Overlays.deleteOverlay(this.ballId2) +} + +PickerTest.prototype.updateOverlays = function() { + var pickRay = Camera.computePickRay(this.x, this.y); + var origin = pickRay.origin; + var direction = pickRay.direction; + var position = Vec3.sum(origin, direction) + Overlays.editOverlay(this.ballId, { + position: position + }); + + Overlays.editOverlay(this.ballId2, { + position: origin + }); +} + +PickerTest.prototype.onUpdate = function(deltaTime) { + if (this.clicked) { + this.updateOverlays(); + } +} + +PickerTest.prototype.onMousePress = function(event) { + if (event.button !== "LEFT") { + return + } + this.clicked = true; + this.x = event.x; + this.y = event.y; + this.updateOverlays(); +} + +PickerTest.prototype.onMouseRelease = function(event) { + if (event.button !== "LEFT") { + return + } + this.clicked = false; +} + +PickerTest.prototype.onMouseMove = function(event) { + if (this.clicked) { + this.x = event.x; + this.y = event.y; + this.updateOverlays(); + } +} + +var PickerTest = new PickerTest(); diff --git a/examples/libraries/virtualKeyboard.js b/examples/libraries/virtualKeyboard.js index 7a0ec80a98..eca723dbcc 100644 --- a/examples/libraries/virtualKeyboard.js +++ b/examples/libraries/virtualKeyboard.js @@ -342,7 +342,6 @@ Keyboard = (function(params) { if (HMD.magnifier == visible) { HMD.toggleMagnifier(); } - Window.cursorVisible = !visible; Overlays.editOverlay(tthis.background, { visible: tthis.visible }); for (var i = 0; i < this.keys.length; i++) { this.keys[i].updateVisibility(); diff --git a/examples/toys/basketball_hoop/createHoop.js b/examples/toys/basketball/createHoop.js similarity index 95% rename from examples/toys/basketball_hoop/createHoop.js rename to examples/toys/basketball/createHoop.js index 3887e0b421..2beb7c9fca 100644 --- a/examples/toys/basketball_hoop/createHoop.js +++ b/examples/toys/basketball/createHoop.js @@ -1,12 +1,10 @@ // // createHoop.js -// examples/entityScripts // // Created by James B. Pollack on 9/29/2015 // Copyright 2015 High Fidelity, Inc. // // This is a script that creates a persistent basketball hoop with a working collision hull. Feel free to move it. -// Run basketball.js to make a basketball. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/examples/toys/basketball/createRack.js b/examples/toys/basketball/createRack.js new file mode 100644 index 0000000000..f764b2ec03 --- /dev/null +++ b/examples/toys/basketball/createRack.js @@ -0,0 +1,152 @@ +// +// createRack.js +// +// Created by James B. Pollack @imgntn on 10/5/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is a script that creates a persistent basketball rack. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +Script.include("../../libraries/utils.js"); + +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; +var collisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; +var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; +var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; +var NUMBER_OF_BALLS = 4; +var DIAMETER = 0.30; +var RESET_DISTANCE = 1; +var MINIMUM_MOVE_LENGTH = 0.05; + +var GRABBABLE_DATA_KEY = "grabbableKey"; + +var rackStartPosition = + Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, { + x: 0, + y: 0.0, + z: -2 + })); + +var rack = Entities.addEntity({ + name: 'Basketball Rack', + type: "Model", + modelURL: rackURL, + position: rackStartPosition, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + linearDamping: 1, + dimensions: { + x: 0.4, + y: 1.37, + z: 1.73 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + collisionSoundURL: collisionSoundURL, + compoundShapeURL: rackCollisionHullURL, + // scriptURL: rackScriptURL +}); + + +setEntityCustomData(GRABBABLE_DATA_KEY, rack, { + grabbable: false +}); + +var nonCollidingBalls = []; +var collidingBalls = []; +var originalBallPositions = []; + +function createCollidingBalls() { + var position = rackStartPosition; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Colliding Basketball', + shapeType: 'Sphere', + position: ballPosition, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + }); + + collidingBalls.push(collidingBall); + originalBallPositions.push(position); + } +} + +function testBallDistanceFromStart() { + var resetCount = 0; + collidingBalls.forEach(function(ball, index) { + var currentPosition = Entities.getEntityProperties(ball, "position").position; + var originalPosition = originalBallPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(ball, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + resetCount++; + if (resetCount === NUMBER_OF_BALLS) { + deleteCollidingBalls(); + createCollidingBalls(); + } + } + }, 200) + } + }); +} + +function deleteEntity(entityID) { + if (entityID === rack) { + deleteCollidingBalls(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } +} + +function deleteCollidingBalls() { + while (collidingBalls.length > 0) { + Entities.deleteEntity(collidingBalls.pop()); + } +} + +createCollidingBalls(); +Entities.deletingEntity.connect(deleteEntity); + +var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); + +function atEnd() { + Script.clearInterval(distanceCheckInterval); +} + +Script.scriptEnding.connect(atEnd); \ No newline at end of file diff --git a/examples/toys/basketball.js b/examples/toys/basketball/createSingleBasketball.js similarity index 54% rename from examples/toys/basketball.js rename to examples/toys/basketball/createSingleBasketball.js index d30dce6e72..162b572bd1 100644 --- a/examples/toys/basketball.js +++ b/examples/toys/basketball/createSingleBasketball.js @@ -1,5 +1,5 @@ // -// basketball.js +// createSingleBasketball.js // examples // // Created by Philip Rosedale on August 20, 2015 @@ -17,34 +17,39 @@ var collisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; var basketball = null; var originalPosition = null; -var hasMoved = false; +var hasMoved = false; var GRAVITY = -9.8; var DISTANCE_IN_FRONT_OF_ME = 1.0; var START_MOVE = 0.01; -var DIAMETER = 0.30; +var DIAMETER = 0.30; -function makeBasketball() { +function makeBasketball() { var position = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, - { x: 0, y: 0.0, z: -DISTANCE_IN_FRONT_OF_ME })); + Vec3.multiplyQbyV(MyAvatar.orientation, { + x: 0, + y: 0.0, + z: -DISTANCE_IN_FRONT_OF_ME + })); var rotation = Quat.multiply(MyAvatar.orientation, - Quat.fromPitchYawRollDegrees(0, -90, 0)); + Quat.fromPitchYawRollDegrees(0, -90, 0)); basketball = Entities.addEntity({ - type: "Model", - position: position, - rotation: rotation, - dimensions: { x: DIAMETER, - y: DIAMETER, - z: DIAMETER }, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - modelURL: basketballURL, - restitution: 1.0, - linearDamping: 0.00001, - shapeType: "sphere" - }); - originalPosition = position; + type: "Model", + position: position, + rotation: rotation, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + modelURL: basketballURL, + restitution: 1.0, + linearDamping: 0.00001, + shapeType: "sphere" + }); + originalPosition = position; } function update() { @@ -55,28 +60,33 @@ function update() { var moved = Vec3.length(Vec3.subtract(originalPosition, newProperties.position)); if (!hasMoved && (moved > START_MOVE)) { hasMoved = true; - Entities.editEntity(basketball, { gravity: {x: 0, y: GRAVITY, z: 0 }}); + Entities.editEntity(basketball, { + gravity: { + x: 0, + y: GRAVITY, + z: 0 + } + }); } var MAX_DISTANCE = 10.0; var distance = Vec3.length(Vec3.subtract(MyAvatar.position, newProperties.position)); if (distance > MAX_DISTANCE) { deleteStuff(); } - } + } } function scriptEnding() { deleteStuff(); } -function deleteStuff() { +function deleteStuff() { if (basketball != null) { Entities.deleteEntity(basketball); basketball = null; - hasMoved = false; + hasMoved = false; } } Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); - +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file diff --git a/examples/toys/blockers/createTestBlocks.js b/examples/toys/blockers/createTestBlocks.js new file mode 100644 index 0000000000..d92d8d2b32 --- /dev/null +++ b/examples/toys/blockers/createTestBlocks.js @@ -0,0 +1,141 @@ +// +// createTestBlocks.js +// +// Script Type: Entity Spawner +// Created by James B. Pollack on 10/5/2015 +// Copyright 2015 High Fidelity, Inc. +// +// +// This script creates a 'stonehenge' formation of physical blocks for testing knock over properties. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ + +var BLOCK_GRAVITY = { + x: 0, + y: -9.8, + z: 0 +}; + +var LINEAR_DAMPING = 0.2; + +var RED = { + red: 255, + green: 0, + blue: 0 +}; + +var GREEN = { + red: 0, + green: 255, + blue: 0 +}; + +var BLUE = { + red: 0, + green: 0, + blue: 255 +}; + +var blockDimensions = { + x: 0.12, + y: 0.25, + z: 0.50 +}; + +var centerPosition = { + x: 0, + y: 0, + z: 0 +}; + +var DISTANCE_IN_FRONT_OF_ME = 2.0; +var position = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, { + x: 0, + y: 0.0, + z: -DISTANCE_IN_FRONT_OF_ME + })); + +var sideBlock1_position = { + x: position.x, + y: position.y, + z: position.z - 0.25 +}; + +var sideBlock2_position = { + x: position.x, + y: position.y, + z: position.z + 0.25 +}; + +var topBlock_position = { + x: position.x, + y: position.y + 0.31, + z: position.z +}; + +var sideBlock1_rotation = Quat.fromPitchYawRollDegrees(90, 90, 0); +var sideBlock2_rotation = Quat.fromPitchYawRollDegrees(90, 90, 0); +var topBlock_rotation = Quat.fromPitchYawRollDegrees(0, 0, 90); + +var topBlock = Entities.addEntity({ + name: 'Top Block', + color: BLUE, + type: 'Box', + shapeType: 'box', + dimensions: blockDimensions, + position: topBlock_position, + rotation: topBlock_rotation, + linearDamping: LINEAR_DAMPING, + gravity: BLOCK_GRAVITY, + collisionsWillMove: true, + velocity: { + x: 0, + y: -0.01, + z: 0 + } +}); + +var sideBlock1 = Entities.addEntity({ + name: 'Top Block', + color: GREEN, + type: 'Box', + shapeType: 'box', + dimensions: blockDimensions, + position: sideBlock1_position, + rotation: sideBlock1_rotation, + linearDamping: LINEAR_DAMPING, + gravity: BLOCK_GRAVITY, + collisionsWillMove: true +}); + +var sideBlock2 = Entities.addEntity({ + name: 'Side Block', + color: GREEN, + type: 'Box', + shapeType: 'box', + dimensions: blockDimensions, + position: sideBlock2_position, + rotation: sideBlock2_rotation, + collsionsWillMove: true, + linearDamping: LINEAR_DAMPING, + gravity: BLOCK_GRAVITY, + collisionsWillMove: true +}); + +var ground = Entities.addEntity({ + type: 'Box', + dimensions: { + x: 2, + y: 0.02, + z: 1 + }, + color: RED, + position: { + x: position.x, + y: position.y - 0.25, + z: position.z + } +}); diff --git a/examples/toys/lightSwitch.js b/examples/toys/lightSwitch.js new file mode 100644 index 0000000000..5c47108c94 --- /dev/null +++ b/examples/toys/lightSwitch.js @@ -0,0 +1,92 @@ +// +// lightSwitch.js +// examples/entityScripts +// +// Created by Eric Levin on 10/2/15. +// Copyright 2015 High Fidelity, Inc. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ +//per script + +/*global LightSwitch */ + +(function () { + var _this; + var utilitiesScript = Script.resolvePath("../libraries/utils.js"); + Script.include(utilitiesScript); + LightSwitch = function () { + _this = this; + this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav"); + }; + + LightSwitch.prototype = { + + clickReleaseOnEntity: function (entityID, mouseEvent) { + if (!mouseEvent.isLeftButton) { + return; + } + this.toggleLights(); + }, + + startNearGrabNonColliding: function () { + this.toggleLights(); + }, + + toggleLights: function () { + var lightData = getEntityCustomData(this.resetKey, this.entityID, {}); + var on = !lightData.on; + var lightType = lightData.type; + + var lights = Entities.findEntities(this.position, 20); + lights.forEach(function (light) { + var type = getEntityCustomData(_this.resetKey, light, {}).type; + if (type === lightType && JSON.stringify(light) !== JSON.stringify(_this.entityID)) { + Entities.editEntity(light, { + visible: on + }); + + } + }); + this.flipSwitch(); + Audio.playSound(this.switchSound, { + volume: 0.5, + position: this.position + }); + + setEntityCustomData(this.resetKey, this.entityID, { + on: on, + type: lightType, + resetMe: true + }); + }, + + flipSwitch: function () { + var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + Entities.editEntity(this.entityID, { + rotation: rotation + }); + }, + preload: function (entityID) { + this.entityID = entityID; + this.resetKey = "resetMe"; + //The light switch is static, so just cache its position once + this.position = Entities.getEntityProperties(this.entityID, "position").position; + } + }; + + // entity scripts always need to return a newly constructed object of our type + return new LightSwitch(); +}); \ No newline at end of file diff --git a/examples/toys/lightSwitchGarage.js b/examples/toys/lightSwitchGarage.js deleted file mode 100644 index 19d33117df..0000000000 --- a/examples/toys/lightSwitchGarage.js +++ /dev/null @@ -1,202 +0,0 @@ -// -// lightSwitchGarage.js.js -// examples/entityScripts -// -// Created by Eric Levin on 9/21/15. -// Copyright 2015 High Fidelity, Inc. -// -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - - var _this; - - // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember - // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) - LightSwitchGarage = function() { - _this = this; - - this.lightStateKey = "lightStateKey"; - this.resetKey = "resetMe"; - - this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav"); - - }; - - LightSwitchGarage.prototype = { - - clickReleaseOnEntity: function(entityID, mouseEvent) { - if (!mouseEvent.isLeftButton) { - return; - } - this.toggleLights(); - }, - - startNearGrabNonColliding: function() { - this.toggleLights(); - }, - - toggleLights: function() { - var defaultLightData = { - on: false - }; - var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData); - if (lightState.on === true) { - this.clearLights(); - } else if (lightState.on === false) { - this.createLights(); - } - - this.flipLights(); - - Audio.playSound(this.switchSound, { - volume: 0.5, - position: this.position - }); - - }, - - clearLights: function() { - var entities = Entities.findEntities(MyAvatar.position, 100); - var self = this; - entities.forEach(function(entity) { - var resetData = getEntityCustomData(self.resetKey, entity, {}) - if (resetData.resetMe === true && resetData.lightType === "Sconce Light Garage") { - Entities.deleteEntity(entity); - } - }); - - setEntityCustomData(this.lightStateKey, this.entityID, { - on: false - }); - }, - - createLights: function() { - - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, - - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - } - }); - - setEntityCustomData(this.resetKey, sconceLight3, { - resetMe: true, - lightType: "Sconce Light Garage" - }); - - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - } - }); - - setEntityCustomData(this.resetKey, sconceLight4, { - resetMe: true, - lightType: "Sconce Light Garage" - }); - - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - } - }); - - setEntityCustomData(this.resetKey, sconceLight5, { - resetMe: true, - lightType: "Sconce Light Garage" - }); - - setEntityCustomData(this.lightStateKey, this.entityID, { - on: true - }); - }, - - flipLights: function() { - // flip model to give illusion of light switch being flicked - var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - - Entities.editEntity(this.entityID, { - rotation: rotation - }); - - }, - - preload: function(entityID) { - this.entityID = entityID; - - //The light switch is static, so just cache its position once - this.position = Entities.getEntityProperties(this.entityID, "position").position; - var defaultLightData = { - on: false - }; - var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData); - - //If light is off, then we create two new lights- at the position of the sconces - if (lightState.on === false) { - this.createLights(); - this.flipLights(); - } - //If lights are on, do nothing! - }, - }; - - // entity scripts always need to return a newly constructed object of our type - return new LightSwitchGarage(); -}) diff --git a/examples/toys/lightSwitchHall.js b/examples/toys/lightSwitchHall.js deleted file mode 100644 index e1093311f9..0000000000 --- a/examples/toys/lightSwitchHall.js +++ /dev/null @@ -1,179 +0,0 @@ -// -// lightSwitchHall.js -// examples/entityScripts -// -// Created by Eric Levin on 9/21/15. -// Copyright 2015 High Fidelity, Inc. -// -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { - - var _this; - - - // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember - // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) - LightSwitchHall = function() { - _this = this; - - this.lightStateKey = "lightStateKey"; - this.resetKey = "resetMe"; - - this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav"); - }; - - LightSwitchHall.prototype = { - - clickReleaseOnEntity: function(entityId, mouseEvent) { - if (!mouseEvent.isLeftButton) { - return; - } - this.toggleLights(); - }, - - startNearGrabNonColliding: function() { - this.toggleLights(); - }, - - toggleLights: function() { - var defaultLightData = { - on: false - }; - var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData); - if (lightState.on === true) { - this.clearLights(); - } else if (lightState.on === false) { - this.createLights(); - } - - // flip model to give illusion of light switch being flicked - this.flipLights(); - - Audio.playSound(this.switchSound, { - volume: 0.5, - position: this.position - }); - - }, - - clearLights: function() { - var entities = Entities.findEntities(MyAvatar.position, 100); - var self = this; - entities.forEach(function(entity) { - var resetData = getEntityCustomData(self.resetKey, entity, {}) - if (resetData.resetMe === true && resetData.lightType === "Sconce Light Hall") { - Entities.deleteEntity(entity); - } - }); - - setEntityCustomData(this.lightStateKey, this.entityID, { - on: false - }); - }, - - createLights: function() { - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - } - }); - - setEntityCustomData(this.resetKey, sconceLight1, { - resetMe: true, - lightType: "Sconce Light Hall" - }); - - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - } - }); - - setEntityCustomData(this.resetKey, sconceLight2, { - resetMe: true, - lightType: "Sconce Light Hall" - }); - - setEntityCustomData(this.lightStateKey, this.entityID, { - on: true - }); - - }, - - flipLights: function() { - // flip model to give illusion of light switch being flicked - var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - - Entities.editEntity(this.entityID, { - rotation: rotation - }); - - }, - - // preload() will be called when the entity has become visible (or known) to the interface - // it gives us a chance to set our local JavaScript object up. In this case it means: - preload: function(entityID) { - this.entityID = entityID; - - //The light switch is static, so just cache its position once - this.position = Entities.getEntityProperties(this.entityID, "position").position; - var defaultLightData = { - on: false - }; - var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData); - - //If light is off, then we create two new lights- at the position of the sconces - if (lightState.on === false) { - this.createLights(); - this.flipLights(); - - } - //If lights are on, do nothing! - }, - }; - - // entity scripts always need to return a newly constructed object of our type - return new LightSwitchHall(); -}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b25f3aa6a5..70cc189ad6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -11,8 +11,6 @@ #include "Application.h" -#include - #include #include #include @@ -21,48 +19,34 @@ #include #include -#include -#include +#include +#include #include -#include #include +#include #include #include +#include #include +#include +#include #include -#include #include #include -#include #include -#include -#include #include #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include +#include #include +#include #include -#include -#include -#include #include - #include #include #include @@ -75,6 +59,7 @@ #include #include #include // this should probably be removed +#include #include #include #include @@ -82,79 +67,73 @@ #include #include #include -#include #include #include -#include +#include #include #include #include +#include +#include +#include #include #include #include #include -#include -#include #include #include #include +#include #include #include -#include #include -#include - +#include "AnimDebugDraw.h" #include "AudioClient.h" -#include "CrashHandler.h" -#include "DiscoverabilityManager.h" -#include "GLCanvas.h" -#include "LODManager.h" -#include "Menu.h" -#include "ModelPackager.h" -#include "Util.h" -#include "InterfaceLogging.h" -#include "InterfaceActionFactory.h" - -#include "avatar/AvatarManager.h" #include "audio/AudioScope.h" +#include "avatar/AvatarManager.h" +#include "CrashHandler.h" +#include "devices/3DConnexionClient.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" #include "devices/Leapmotion.h" -#include "devices/RealSense.h" #include "devices/MIDIManager.h" -#include "devices/3DConnexionClient.h" - +#include "devices/RealSense.h" +#include "DiscoverabilityManager.h" +#include "GLCanvas.h" +#include "InterfaceActionFactory.h" +#include "InterfaceLogging.h" +#include "LODManager.h" +#include "Menu.h" +#include "ModelPackager.h" +#include "PluginContainerProxy.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" -#include "scripting/HMDScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" -#include "scripting/WindowScriptingInterface.h" #include "scripting/WebWindowClass.h" - +#include "scripting/WindowScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif - +#include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" #include "ui/LoginDialog.h" +#include "ui/overlays/Cube3DOverlay.h" #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" #include "ui/UpdateDialog.h" -#include "ui/overlays/Cube3DOverlay.h" - -#include "PluginContainerProxy.h" -#include "AnimDebugDraw.h" +#include "Util.h" // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -164,30 +143,34 @@ extern "C" { } #endif -enum CustomEventTypes { - Lambda = QEvent::User + 1 -}; - -class LambdaEvent : public QEvent { - std::function _fun; -public: - LambdaEvent(const std::function & fun) : - QEvent(static_cast(Lambda)), _fun(fun) { - } - LambdaEvent(std::function && fun) : - QEvent(static_cast(Lambda)), _fun(fun) { - } - void call() { _fun(); } -}; - using namespace std; -static QTimer* locationUpdateTimer = NULL; -static QTimer* balanceUpdateTimer = NULL; -static QTimer* identityPacketTimer = NULL; -static QTimer* billboardPacketTimer = NULL; -static QTimer* checkFPStimer = NULL; -static QTimer* idleTimer = NULL; +static QTimer locationUpdateTimer; +static QTimer balanceUpdateTimer; +static QTimer identityPacketTimer; +static QTimer billboardPacketTimer; +static QTimer checkFPStimer; +static QTimer idleTimer; + +static const QString SNAPSHOT_EXTENSION = ".jpg"; +static const QString SVO_EXTENSION = ".svo"; +static const QString SVO_JSON_EXTENSION = ".svo.json"; +static const QString JS_EXTENSION = ".js"; +static const QString FST_EXTENSION = ".fst"; + +static const int MIRROR_VIEW_TOP_PADDING = 5; +static const int MIRROR_VIEW_LEFT_PADDING = 10; +static const int MIRROR_VIEW_WIDTH = 265; +static const int MIRROR_VIEW_HEIGHT = 215; +static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; +static const float MIRROR_REARVIEW_DISTANCE = 0.722f; +static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; +static const float MIRROR_FIELD_OF_VIEW = 30.0f; + +static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; + +static const QString INFO_HELP_PATH = "html/interface-welcome.html"; +static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; static const unsigned int TARGET_SIM_FRAMERATE = 60; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; @@ -197,6 +180,13 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml"; const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; +#ifndef __APPLE__ +static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +#else +// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 +static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js"); +#endif + const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; Setting::Handle maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); @@ -218,11 +208,11 @@ public: } if (message->message == UWM_SHOW_APPLICATION) { - MainWindow* applicationWindow = Application::getInstance()->getWindow(); + MainWindow* applicationWindow = qApp->getWindow(); if (applicationWindow->isMinimized()) { applicationWindow->showNormal(); // Restores to windowed or maximized state appropriately. } - Application::getInstance()->setActiveWindow(applicationWindow); // Flashes the taskbar icon if not focus. + qApp->setActiveWindow(applicationWindow); // Flashes the taskbar icon if not focus. return true; } @@ -240,6 +230,22 @@ public: }; #endif +enum CustomEventTypes { + Lambda = QEvent::User + 1 +}; + +class LambdaEvent : public QEvent { + std::function _fun; +public: + LambdaEvent(const std::function & fun) : + QEvent(static_cast(Lambda)), _fun(fun) { + } + LambdaEvent(std::function && fun) : + QEvent(static_cast(Lambda)), _fun(fun) { + } + void call() { _fun(); } +}; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); @@ -248,7 +254,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); #endif - Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n")); + qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); } } @@ -275,42 +281,43 @@ bool setupEssentials(int& argc, char** argv) { Setting::init(); // Set dependencies - auto addressManager = DependencyManager::set(); - auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); - auto geometryCache = DependencyManager::set(); - auto modelCache = DependencyManager::set(); - auto scriptCache = DependencyManager::set(); - auto soundCache = DependencyManager::set(); - auto faceshift = DependencyManager::set(); - auto ddeFaceTracker = DependencyManager::set(); - auto eyeTracker = DependencyManager::set(); - auto audio = DependencyManager::set(); - auto audioScope = DependencyManager::set(); - auto deferredLightingEffect = DependencyManager::set(); - auto textureCache = DependencyManager::set(); - auto framebufferCache = DependencyManager::set(); - auto animationCache = DependencyManager::set(); - auto modelBlender = DependencyManager::set(); - auto avatarManager = DependencyManager::set(); - auto lodManager = DependencyManager::set(); - auto jsConsole = DependencyManager::set(); - auto dialogsManager = DependencyManager::set(); - auto bandwidthRecorder = DependencyManager::set(); - auto resourceCacheSharedItems = DependencyManager::set(); - auto desktopScriptingInterface = DependencyManager::set(); - auto entityScriptingInterface = DependencyManager::set(); - auto windowScriptingInterface = DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent, listenPort); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) - auto speechRecognizer = DependencyManager::set(); + DependencyManager::set(); #endif - auto discoverabilityManager = DependencyManager::set(); - auto sceneScriptingInterface = DependencyManager::set(); - auto offscreenUi = DependencyManager::set(); - auto autoUpdater = DependencyManager::set(); - auto pathUtils = DependencyManager::set(); - auto actionFactory = DependencyManager::set(); - auto assetClient = DependencyManager::set(); - auto userInputMapper = DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); return true; } @@ -322,51 +329,37 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; -Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : +Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv), _dependencyManagerIsSetup(setupEssentials(argc, argv)), _window(new MainWindow(desktop())), _toolWindow(NULL), - _friendsWindow(NULL), - _undoStack(), _undoStackScriptingInterface(&_undoStack), _frameCount(0), _fps(60.0f), - _justStarted(true), _physicsEngine(new PhysicsEngine(Vectors::ZERO)), _entities(true, this, this), _entityClipboardRenderer(false, this, this), _entityClipboard(new EntityTree()), - _viewFrustum(), - _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _firstRun("firstRun", true), - _previousScriptLocation("LastScriptLocation"), - _scriptsLocationHandle("scriptsLocation"), + _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), + _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), - _cursorVisible(true), - _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), - _isTouchPressed(false), - _mousePressed(false), _enableProcessOctreeThread(true), - _octreeProcessor(), _runningScriptsWidget(NULL), _runningScriptsWidgetWasVisible(false), - _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), - _isThrottleFPSEnabled(true), _aboutToQuit(false), _notifiedPacketVersionMismatchThisDomain(false), - _domainConnectionRefusals(QList()), _maxOctreePPS(maxOctreePacketsPerSecond.get()), - _lastFaceTrackerUpdate(0), - _applicationOverlay() + _lastFaceTrackerUpdate(0) { thread()->setObjectName("Main Thread"); @@ -391,11 +384,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto nodeList = DependencyManager::get(); - _myAvatar = DependencyManager::get()->getMyAvatar(); - - _applicationStartupTime = startup_time; - - qCDebug(interfaceapp) << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); + qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion()); _bookmarks = new Bookmarks(); // Before setting up the menu @@ -432,8 +421,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto audioIO = DependencyManager::get(); - audioIO->setPositionGetter(getPositionForAudio); - audioIO->setOrientationGetter(getOrientationForAudio); + audioIO->setPositionGetter([this]{ return getMyAvatar()->getPositionForAudio(); }); + audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); }); audioIO->moveToThread(audioThread); @@ -448,8 +437,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected); connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) { auto audioClient = DependencyManager::get(); - float distance = glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), - position); + auto myAvatarPosition = DependencyManager::get()->getMyAvatar()->getPosition(); + float distance = glm::distance(myAvatarPosition, position); bool shouldMute = !audioClient->isMuted() && (distance < radius); if (shouldMute) { @@ -482,10 +471,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; - locationUpdateTimer = new QTimer(this); auto discoverabilityManager = DependencyManager::get(); - connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); - locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); + connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); + locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); // if we get a domain change, immediately attempt update location in metaverse server connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, @@ -493,8 +481,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); - connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - connect(nodeList.data(), &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID); + connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID); connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); @@ -504,9 +491,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000; - balanceUpdateTimer = new QTimer(this); - connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance); - balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS); + connect(&balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance); + balanceUpdateTimer.start(BALANCE_UPDATE_INTERVAL_MSECS); connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle); @@ -524,8 +510,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto addressManager = DependencyManager::get(); // use our MyAvatar position and quat for address manager path - addressManager->setPositionGetter(getPositionForPath); - addressManager->setOrientationGetter(getOrientationForPath); + addressManager->setPositionGetter([this]{ return getMyAvatar()->getPosition(); }); + addressManager->setOrientationGetter([this]{ return getMyAvatar()->getOrientation(); }); connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); @@ -543,14 +529,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); // send the identity packet for our avatar each second to our avatar mixer - identityPacketTimer = new QTimer(); - connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); - identityPacketTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); + identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // send the billboard packet for our avatar every few seconds - billboardPacketTimer = new QTimer(); - connect(billboardPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendBillboardPacket); - billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); + connect(&billboardPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendBillboardPacket); + billboardPacketTimer.start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -616,7 +600,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); - connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded, + connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions @@ -664,8 +648,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : cameraMenuChanged(); } - _trayIcon->show(); - // set the local loopback interface for local sounds from audio scripts AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data()); @@ -707,7 +689,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : applicationUpdater->checkForUpdate(); // Now that menu is initalized we can sync myAvatar with it's state. - _myAvatar->updateMotionBehaviorFromMenu(); + getMyAvatar()->updateMotionBehaviorFromMenu(); // the 3Dconnexion device wants to be initiliazed after a window is displayed. ConnexionClient::getInstance().init(); @@ -775,6 +757,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : }); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); + + qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); } void Application::aboutToQuit() { @@ -812,23 +796,14 @@ void Application::cleanupBeforeQuit() { // first stop all timers directly or by invokeMethod // depending on what thread they run in _avatarUpdate->terminate(); - locationUpdateTimer->stop(); - balanceUpdateTimer->stop(); - identityPacketTimer->stop(); - billboardPacketTimer->stop(); - checkFPStimer->stop(); - idleTimer->stop(); + locationUpdateTimer.stop(); + balanceUpdateTimer.stop(); + identityPacketTimer.stop(); + billboardPacketTimer.stop(); + checkFPStimer.stop(); + idleTimer.stop(); QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); - // and then delete those that got created by "new" - delete locationUpdateTimer; - delete balanceUpdateTimer; - delete identityPacketTimer; - delete billboardPacketTimer; - delete checkFPStimer; - delete idleTimer; - // no need to delete _settingsTimer here as it is no pointer - // save state _settingsThread.quit(); saveSettings(); @@ -870,7 +845,6 @@ Application::~Application() { Menu::getInstance()->deleteLater(); _physicsEngine->setCharacterController(NULL); - _myAvatar = NULL; ModelEntityItem::cleanupLoadedAnimations(); @@ -959,32 +933,20 @@ void Application::initializeGL() { _entityEditSender.initialize(_enableProcessOctreeThread); // call our timer function every second - checkFPStimer = new QTimer(this); - connect(checkFPStimer, SIGNAL(timeout()), SLOT(checkFPS())); - checkFPStimer->start(1000); + connect(&checkFPStimer, &QTimer::timeout, this, &Application::checkFPS); + checkFPStimer.start(1000); // call our idle function whenever we can - idleTimer = new QTimer(this); - connect(idleTimer, SIGNAL(timeout()), SLOT(idle())); - idleTimer->start(TARGET_SIM_FRAME_PERIOD_MS); + connect(&idleTimer, &QTimer::timeout, this, &Application::idle); + idleTimer.start(TARGET_SIM_FRAME_PERIOD_MS); _idleLoopStdev.reset(); - if (_justStarted) { - float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0f; - _justStarted = false; - qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTime); - } - // update before the first render update(1.0f / _fps); InfoView::show(INFO_HELP_PATH, true); } -QWindow* getProxyWindow() { - return qApp->getWindow()->windowHandle(); -} - void Application::initializeUi() { AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -1013,7 +975,7 @@ void Application::initializeUi() { return result; }); offscreenUi->resume(); - connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect & r){ + connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){ static qreal oldDevicePixelRatio = 0; qreal devicePixelRatio = getActiveDisplayPlugin()->devicePixelRatio(); if (devicePixelRatio != oldDevicePixelRatio) { @@ -1056,7 +1018,7 @@ void Application::paintGL() { _offscreenContext->makeCurrent(); // update the avatar with a fresh HMD pose - _myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose()); + getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); auto lodManager = DependencyManager::get(); @@ -1104,19 +1066,21 @@ void Application::paintGL() { PerformanceTimer perfTimer("renderOverlay"); // NOTE: There is no batch associated with this renderArgs // the ApplicationOverlay class assumes it's viewport is setup to be the device size - QSize size = qApp->getDeviceSize(); + QSize size = getDeviceSize(); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); _applicationOverlay.renderOverlay(&renderArgs); } { PerformanceTimer perfTimer("CameraUpdates"); - - _myAvatar->startCapture(); + + auto myAvatar = getMyAvatar(); + + myAvatar->startCapture(); if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || _myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, _myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(_myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); - Application::getInstance()->cameraMenuChanged(); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !(myAvatar->getBoomLength() <= MyAvatar::ZOOM_MIN)); + cameraMenuChanged(); } // The render mode is default or mirror if the camera is in mirror mode, assigned further below @@ -1127,51 +1091,51 @@ void Application::paintGL() { // or with changes from the face tracker if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { if (isHMDMode()) { - mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); + mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setRotation(glm::quat_cast(camMat)); } else { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + _myCamera.setPosition(myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(myAvatar->getHead()->getCameraOrientation()); } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * hmdRotation); + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * hmdRotation); // Ignore MenuOption::CenterPlayerInView in HMD view - glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + _myAvatar->getOrientation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() + * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); } else { - _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); + _myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + _myCamera.getRotation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); } else { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + _myAvatar->getOrientation() - * (_myAvatar->getScale() * _myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() + * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f))); } } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation); - glm::vec3 hmdOffset = extractTranslation(_myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset); } else { - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() + _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * _myAvatar->getScale(), 0) - + (_myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; @@ -1180,7 +1144,7 @@ void Application::paintGL() { if (!isHMDMode()) { _myCamera.update(1.0f / _fps); } - _myAvatar->endCapture(); + myAvatar->endCapture(); } // Primary rendering pass @@ -1203,9 +1167,11 @@ void Application::paintGL() { // right eye. There are FIXMEs in the relevant plugins _myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); renderArgs._context->enableStereo(true); - mat4 eyeViews[2]; + mat4 eyeOffsets[2]; mat4 eyeProjections[2]; auto baseProjection = renderArgs._viewFrustum->getProjection(); + auto hmdInterface = DependencyManager::get(); + float IPDScale = hmdInterface->getIPDScale(); // FIXME we probably don't need to set the projection matrix every frame, // only when the display plugin changes (or in non-HMD modes when the user // changes the FOV manually, which right now I don't think they can. @@ -1214,14 +1180,24 @@ void Application::paintGL() { // applied to the avatar, so we need to get the difference between the head // pose applied to the avatar and the per eye pose, and use THAT as // the per-eye stereo matrix adjustment. - mat4 eyePose = displayPlugin->getEyePose(eye); + mat4 eyeToHead = displayPlugin->getEyeToHeadTransform(eye); + // Grab the translation + vec3 eyeOffset = glm::vec3(eyeToHead[3]); + // Apply IPD scaling + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); + eyeOffsets[eye] = eyeOffsetTransform; + + // Tell the plugin what pose we're using to render. In this case we're just using the + // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it + // for rotational timewarp. If we move to support positonal timewarp, we need to + // ensure this contains the full pose composed with the eye offsets. mat4 headPose = displayPlugin->getHeadPose(); - mat4 eyeView = glm::inverse(eyePose) * headPose; - eyeViews[eye] = eyeView; + displayPlugin->setEyeRenderPose(eye, headPose); + eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); - renderArgs._context->setStereoViews(eyeViews); + renderArgs._context->setStereoViews(eyeOffsets); } displaySide(&renderArgs, _myCamera); renderArgs._context->enableStereo(false); @@ -1322,6 +1298,13 @@ void Application::faceTrackerMuteToggled() { Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setEnabled(!isMuted); } +void Application::setFieldOfView(float fov) { + if (fov != _fieldOfView.get()) { + _fieldOfView.set(fov); + resizeGL(); + } +} + void Application::aboutApp() { InfoView::show(INFO_HELP_PATH); } @@ -1330,7 +1313,7 @@ void Application::showEditEntitiesHelp() { InfoView::show(INFO_EDIT_ENTITIES_PATH); } -void Application::resizeEvent(QResizeEvent * event) { +void Application::resizeEvent(QResizeEvent* event) { resizeGL(); } @@ -1339,26 +1322,24 @@ void Application::resizeGL() { if (nullptr == _displayPlugin) { return; } - + auto displayPlugin = getActiveDisplayPlugin(); // Set the desired FBO texture size. If it hasn't changed, this does nothing. // Otherwise, it must rebuild the FBOs - uvec2 framebufferSize = getActiveDisplayPlugin()->getRecommendedRenderSize(); + uvec2 framebufferSize = displayPlugin->getRecommendedRenderSize(); uvec2 renderSize = uvec2(vec2(framebufferSize) * getRenderResolutionScale()); if (_renderResolution != renderSize) { - _numFramesSinceLastResize = 0; _renderResolution = renderSize; DependencyManager::get()->setFrameBufferSize(fromGlm(renderSize)); - - // Possible change in aspect ratio - loadViewFrustum(_myCamera, _viewFrustum); - float fov = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES); - // FIXME the aspect ratio for stereo displays is incorrect based on this. - float aspectRatio = displayPlugin->getRecommendedAspectRatio(); - _myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); } - - + + // FIXME the aspect ratio for stereo displays is incorrect based on this. + float aspectRatio = displayPlugin->getRecommendedAspectRatio(); + _myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio, + DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); + // Possible change in aspect ratio + loadViewFrustum(_myCamera, _viewFrustum); + auto offscreenUi = DependencyManager::get(); auto uiSize = displayPlugin->getRecommendedUiSize(); // Bit of a hack since there's no device pixel ratio change event I can find. @@ -1704,7 +1685,7 @@ void Application::keyPressEvent(QKeyEvent* event) { auto& cursorManager = Cursor::Manager::instance(); cursorManager.setScale(cursorManager.getScale() * 1.1f); } else { - _myAvatar->increaseSize(); + getMyAvatar()->increaseSize(); } break; } @@ -1714,19 +1695,19 @@ void Application::keyPressEvent(QKeyEvent* event) { auto& cursorManager = Cursor::Manager::instance(); cursorManager.setScale(cursorManager.getScale() / 1.1f); } else { - _myAvatar->decreaseSize(); + getMyAvatar()->decreaseSize(); } break; } case Qt::Key_Equal: - _myAvatar->resetSize(); + getMyAvatar()->resetSize(); break; case Qt::Key_Space: { if (!event->isAutoRepeat()) { // this starts an HFActionEvent HFActionEvent startActionEvent(HFActionEvent::startType(), - computePickRay(getTrueMouseX(), getTrueMouseY())); + computePickRay(getTrueMouse().x, getTrueMouse().y)); sendEvent(this, &startActionEvent); } @@ -1777,7 +1758,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (!event->isAutoRepeat()) { // this ends the HFActionEvent HFActionEvent endActionEvent(HFActionEvent::endType(), - computePickRay(getTrueMouseX(), getTrueMouseY())); + computePickRay(getTrueMouse().x, getTrueMouse().y)); sendEvent(this, &endActionEvent); } break; @@ -1819,9 +1800,6 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { PROFILE_RANGE(__FUNCTION__); // Used by application overlay to determine how to draw cursor(s) _lastMouseMoveWasSimulated = deviceID > 0; - if (!_lastMouseMoveWasSimulated) { - _lastMouseMove = usecTimestampNow(); - } if (_aboutToQuit) { return; @@ -1899,9 +1877,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { } if (event->button() == Qt::LeftButton) { - _mouseDragStarted = getTrueMouse(); - _mousePressed = true; - // nobody handled this - make it an action event on the _window object HFActionEvent actionEvent(HFActionEvent::startType(), computePickRay(event->x(), event->y())); @@ -1957,8 +1932,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { } if (event->button() == Qt::LeftButton) { - _mousePressed = false; - // fire an action end event HFActionEvent actionEvent(HFActionEvent::endType(), computePickRay(event->x(), event->y())); @@ -1984,24 +1957,6 @@ void Application::touchUpdateEvent(QTouchEvent* event) { if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->touchUpdateEvent(event); } - - bool validTouch = false; - if (hasFocus()) { - const QList& tPoints = event->touchPoints(); - _touchAvg = vec2(); - int numTouches = tPoints.count(); - if (numTouches > 1) { - for (int i = 0; i < numTouches; ++i) { - _touchAvg += toGlm(tPoints[i].pos()); - } - _touchAvg /= (float)(numTouches); - validTouch = true; - } - } - if (!_isTouchPressed) { - _touchDragStartedAvg = _touchAvg; - } - _isTouchPressed = validTouch; } void Application::touchBeginEvent(QTouchEvent* event) { @@ -2039,9 +1994,6 @@ void Application::touchEndEvent(QTouchEvent* event) { } // put any application specific touch behavior below here.. - _touchDragStartedAvg = _touchAvg; - _isTouchPressed = false; - } void Application::wheelEvent(QWheelEvent* event) { @@ -2107,29 +2059,10 @@ bool Application::acceptSnapshot(const QString& urlString) { return true; } -void Application::sendPingPackets() { - - auto nodeList = DependencyManager::get(); - - nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { - switch (node->getType()) { - case NodeType::AvatarMixer: - case NodeType::AudioMixer: - case NodeType::EntityServer: - case NodeType::AssetServer: - return true; - default: - return false; - } - }, [nodeList](const SharedNodePointer& node) { - nodeList->sendPacket(nodeList->constructPingPacket(), *node); - }); -} - // Every second, check the frame rates and other stuff void Application::checkFPS() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - sendPingPackets(); + DependencyManager::get()->sendPingPackets(); } float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f; @@ -2251,19 +2184,6 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { InputDevice::setLowVelocityFilter(lowVelocityFilter); } -bool Application::mouseOnScreen() const { - glm::ivec2 mousePosition = getTrueMouse(); - return (glm::all(glm::greaterThanEqual(mousePosition, glm::ivec2(0))) && - glm::all(glm::lessThanEqual(mousePosition, glm::ivec2(getCanvasSize())))); -} - -ivec2 Application::getMouseDragStarted() const { - if (isHMDMode()) { - return _compositor.screenToOverlay(getTrueMouseDragStarted()); - } - return getTrueMouseDragStarted(); -} - ivec2 Application::getMouse() const { if (isHMDMode()) { return _compositor.screenToOverlay(getTrueMouse()); @@ -2271,11 +2191,6 @@ ivec2 Application::getMouse() const { return getTrueMouse(); } - -ivec2 Application::getTrueMouseDragStarted() const { - return _mouseDragStarted; -} - FaceTracker* Application::getActiveFaceTracker() { auto faceshift = DependencyManager::get(); auto dde = DependencyManager::get(); @@ -2321,8 +2236,8 @@ void Application::setActiveFaceTracker() { #endif } -void Application::setActiveEyeTracker() { #ifdef HAVE_IVIEWHMD +void Application::setActiveEyeTracker() { auto eyeTracker = DependencyManager::get(); if (!eyeTracker->isInitialized()) { return; @@ -2335,29 +2250,20 @@ void Application::setActiveEyeTracker() { Menu::getInstance()->getActionForOption(MenuOption::OnePointCalibration)->setEnabled(isEyeTracking && !isSimulating); Menu::getInstance()->getActionForOption(MenuOption::ThreePointCalibration)->setEnabled(isEyeTracking && !isSimulating); Menu::getInstance()->getActionForOption(MenuOption::FivePointCalibration)->setEnabled(isEyeTracking && !isSimulating); -#endif } void Application::calibrateEyeTracker1Point() { -#ifdef HAVE_IVIEWHMD - auto eyeTracker = DependencyManager::get(); - eyeTracker->calibrate(1); -#endif + DependencyManager::get()->calibrate(1); } void Application::calibrateEyeTracker3Points() { -#ifdef HAVE_IVIEWHMD - auto eyeTracker = DependencyManager::get(); - eyeTracker->calibrate(3); -#endif + DependencyManager::get()->calibrate(3); } void Application::calibrateEyeTracker5Points() { -#ifdef HAVE_IVIEWHMD - auto eyeTracker = DependencyManager::get(); - eyeTracker->calibrate(5); -#endif + DependencyManager::get()->calibrate(5); } +#endif bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { QVector entities; @@ -2436,7 +2342,7 @@ void Application::loadSettings() { //DependencyManager::get()->setAutomaticLODAdjust(false); Menu::getInstance()->loadSettings(); - _myAvatar->loadData(); + getMyAvatar()->loadData(); } void Application::saveSettings() { @@ -2444,7 +2350,7 @@ void Application::saveSettings() { DependencyManager::get()->saveSettings(); Menu::getInstance()->saveSettings(); - _myAvatar->saveData(); + getMyAvatar()->saveData(); } bool Application::importEntities(const QString& urlOrFilename) { @@ -2479,7 +2385,6 @@ void Application::init() { DependencyManager::get()->init(this); - // TODO: move _myAvatar out of Application. Move relevant code to MyAvataar or AvatarManager DependencyManager::get()->init(); _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); @@ -2532,7 +2437,7 @@ void Application::init() { // Make sure any new sounds are loaded as soon as know about them. connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get().data(), &SoundCache::getSound); - connect(_myAvatar, &MyAvatar::newCollisionSoundURL, DependencyManager::get().data(), &SoundCache::getSound); + connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get().data(), &SoundCache::getSound); setAvatarUpdateThreading(); } @@ -2557,47 +2462,19 @@ void Application::setAvatarUpdateThreading(bool isThreaded) { if (_avatarUpdate && (_avatarUpdate->isThreaded() == isThreaded)) { return; } - bool isRigEnabled = getMyAvatar()->getEnableRigAnimations(); - bool isGraphEnabled = getMyAvatar()->getEnableAnimGraph(); + + auto myAvatar = getMyAvatar(); + bool isRigEnabled = myAvatar->getEnableRigAnimations(); + bool isGraphEnabled = myAvatar->getEnableAnimGraph(); if (_avatarUpdate) { _avatarUpdate->terminate(); // Must be before we shutdown anim graph. } - getMyAvatar()->setEnableRigAnimations(false); - getMyAvatar()->setEnableAnimGraph(false); + myAvatar->setEnableRigAnimations(false); + myAvatar->setEnableAnimGraph(false); _avatarUpdate = new AvatarUpdate(); _avatarUpdate->initialize(isThreaded); - getMyAvatar()->setEnableRigAnimations(isRigEnabled); - getMyAvatar()->setEnableAnimGraph(isGraphEnabled); -} - - -void Application::closeMirrorView() { - if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { - Menu::getInstance()->triggerOption(MenuOption::Mirror); - } -} - -void Application::restoreMirrorView() { - if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); - } -} - -void Application::shrinkMirrorView() { - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); - } -} - -const float HEAD_SPHERE_RADIUS = 0.1f; - -bool Application::isLookingAtMyAvatar(AvatarSharedPointer avatar) { - glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); - glm::vec3 myEyePosition = _myAvatar->getHead()->getEyePosition(); - if (pointInSphere(theirLookAt, myEyePosition, HEAD_SPHERE_RADIUS * _myAvatar->getScale())) { - return true; - } - return false; + myAvatar->setEnableRigAnimations(isRigEnabled); + myAvatar->setEnableAnimGraph(isGraphEnabled); } void Application::updateLOD() { @@ -2610,29 +2487,6 @@ void Application::updateLOD() { } } -void Application::updateMouseRay() { - PerformanceTimer perfTimer("mouseRay"); - - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); - - // make sure the frustum is up-to-date - loadViewFrustum(_myCamera, _viewFrustum); - - PickRay pickRay = computePickRay(getTrueMouseX(), getTrueMouseY()); - _mouseRayOrigin = pickRay.origin; - _mouseRayDirection = pickRay.direction; - - // adjust for mirroring - if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); - _mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + - _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset)); - _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + - _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection)); - } -} - // Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone. // (Maybe this code should be moved there?) // The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). @@ -2642,7 +2496,8 @@ void Application::updateMyAvatarLookAtPosition() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); - _myAvatar->updateLookAtTargetAvatar(); + auto myAvatar = getMyAvatar(); + myAvatar->updateLookAtTargetAvatar(); FaceTracker* faceTracker = getActiveFaceTracker(); auto eyeTracker = DependencyManager::get(); @@ -2658,28 +2513,28 @@ void Application::updateMyAvatarLookAtPosition() { if (isHMD) { glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); glm::quat hmdRotation = glm::quat_cast(headPose); - lookAtSpot = _myCamera.getPosition() + _myAvatar->getOrientation() * (hmdRotation * lookAtPosition); + lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition); } else { - lookAtSpot = _myAvatar->getHead()->getEyePosition() - + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * lookAtPosition); + lookAtSpot = myAvatar->getHead()->getEyePosition() + + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * lookAtPosition); } } else { - AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock(); - if (lookingAt && _myAvatar != lookingAt.get()) { + AvatarSharedPointer lookingAt = myAvatar->getLookAtTargetAvatar().lock(); + if (lookingAt && myAvatar != lookingAt.get()) { // If I am looking at someone else, look directly at one of their eyes isLookingAtSomeone = true; auto lookingAtHead = static_pointer_cast(lookingAt)->getHead(); const float MAXIMUM_FACE_ANGLE = 65.0f * RADIANS_PER_DEGREE; glm::vec3 lookingAtFaceOrientation = lookingAtHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; - glm::vec3 fromLookingAtToMe = glm::normalize(_myAvatar->getHead()->getEyePosition() + glm::vec3 fromLookingAtToMe = glm::normalize(myAvatar->getHead()->getEyePosition() - lookingAtHead->getEyePosition()); float faceAngle = glm::angle(lookingAtFaceOrientation, fromLookingAtToMe); if (faceAngle < MAXIMUM_FACE_ANGLE) { // Randomly look back and forth between look targets eyeContactTarget target = Menu::getInstance()->isOptionChecked(MenuOption::FixGaze) ? - LEFT_EYE : _myAvatar->getEyeContactTarget(); + LEFT_EYE : myAvatar->getEyeContactTarget(); switch (target) { case LEFT_EYE: lookAtSpot = lookingAtHead->getLeftEyePosition(); @@ -2701,10 +2556,10 @@ void Application::updateMyAvatarLookAtPosition() { glm::mat4 headPose = _avatarUpdate->getHeadPose() ; glm::quat headRotation = glm::quat_cast(headPose); lookAtSpot = _myCamera.getPosition() + - _myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); } else { - lookAtSpot = _myAvatar->getHead()->getEyePosition() + - (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + lookAtSpot = myAvatar->getHead()->getEyePosition() + + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); } } @@ -2713,7 +2568,7 @@ void Application::updateMyAvatarLookAtPosition() { float eyePitch = faceTracker->getEstimatedEyePitch(); float eyeYaw = faceTracker->getEstimatedEyeYaw(); const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f; - glm::vec3 origin = _myAvatar->getHead()->getEyePosition(); + glm::vec3 origin = myAvatar->getHead()->getEyePosition(); float deflection = faceTracker->getEyeDeflection(); if (isLookingAtSomeone) { deflection *= GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT; @@ -2724,7 +2579,7 @@ void Application::updateMyAvatarLookAtPosition() { } } - _myAvatar->getHead()->setLookAtPosition(lookAtSpot); + myAvatar->getHead()->setLookAtPosition(lookAtSpot); } void Application::updateThreads(float deltaTime) { @@ -2747,13 +2602,13 @@ void Application::cameraMenuChanged() { } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); - _myAvatar->setBoomLength(MyAvatar::ZOOM_MIN); + getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); } } else if (Menu::getInstance()->isOptionChecked(MenuOption::ThirdPerson)) { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); - if (_myAvatar->getBoomLength() == MyAvatar::ZOOM_MIN) { - _myAvatar->setBoomLength(MyAvatar::ZOOM_DEFAULT); + if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) { + getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } } } else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) { @@ -2781,16 +2636,10 @@ void Application::reloadResourceCaches() { void Application::rotationModeChanged() { if (!Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myAvatar->setHeadPitch(0); + getMyAvatar()->setHeadPitch(0); } } -void Application::updateCamera(float deltaTime) { - PerformanceTimer perfTimer("updateCamera"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateCamera()"); -} - void Application::updateDialogs(float deltaTime) { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); @@ -2815,26 +2664,11 @@ void Application::updateDialogs(float deltaTime) { } } -void Application::updateCursor(float deltaTime) { - PerformanceTimer perfTimer("updateCursor"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateCursor()"); - - static QPoint lastMousePos = QPoint(); - _lastMouseMove = (lastMousePos == QCursor::pos()) ? _lastMouseMove : usecTimestampNow(); - lastMousePos = QCursor::pos(); -} - -void Application::setCursorVisible(bool visible) { - _cursorVisible = visible; -} - void Application::update(float deltaTime) { 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 { PerformanceTimer perfTimer("devices"); @@ -2871,8 +2705,9 @@ void Application::update(float deltaTime) { } + auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); - userInputMapper->setSensorToWorldMat(_myAvatar->getSensorToWorldMatrix()); + userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); userInputMapper->update(deltaTime); // This needs to go after userInputMapper->update() because of the keyboard @@ -2893,15 +2728,15 @@ void Application::update(float deltaTime) { _controllerScriptingInterface.updateInputControllers(); // Transfer the user inputs to the driveKeys - _myAvatar->clearDriveKeys(); + myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface.areActionsCaptured()) { - _myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); - _myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); - _myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); - _myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN)); - _myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT)); - _myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT)); + myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); + myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); + myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); + myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN)); + myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT)); + myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT)); if (deltaTime > FLT_EPSILON) { // For rotations what we really want are meausures of "angles per second" (in order to prevent // fps-dependent spin rates) so we need to scale the units of the controller contribution. @@ -2909,14 +2744,14 @@ void Application::update(float deltaTime) { // controllers to provide a delta_per_second value rather than a raw delta.) const float EXPECTED_FRAME_RATE = 60.0f; float timeFactor = EXPECTED_FRAME_RATE * deltaTime; - _myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor); - _myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor); - _myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor); - _myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor); + myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor); + myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor); + myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor); + myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor); } } - _myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); - _myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); + myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); + myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); } UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND); UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND); @@ -2931,14 +2766,11 @@ void Application::update(float deltaTime) { } updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - - 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("physics"); - _myAvatar->relayDriveKeysToCharacterController(); + myAvatar->relayDriveKeysToCharacterController(); static VectorOfMotionStates motionStates; _entitySimulation.getObjectsToDelete(motionStates); @@ -2999,12 +2831,6 @@ void Application::update(float deltaTime) { _avatarUpdate->synchronousProcess(); - { - PerformanceTimer perfTimer("emitSimulating"); - // let external parties know we're updating - emit simulating(deltaTime); - } - // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera @@ -3063,7 +2889,7 @@ void Application::update(float deltaTime) { } // update sensorToWorldMatrix for rendering camera. - _myAvatar->updateSensorToWorldMatrix(); + myAvatar->updateSensorToWorldMatrix(); } @@ -3353,10 +3179,6 @@ glm::vec3 Application::getSunDirection() { // FIXME, preprocessor guard this check to occur only in DEBUG builds static QThread * activeRenderingThread = nullptr; -bool Application::shouldRenderMesh(float largestDimension, float distanceToCamera) { - return DependencyManager::get()->shouldRenderMesh(largestDimension, distanceToCamera); -} - float Application::getSizeScale() const { return DependencyManager::get()->getOctreeSizeScale(); } @@ -3366,18 +3188,25 @@ int Application::getBoundaryLevelAdjust() const { } PickRay Application::computePickRay(float x, float y) const { - glm::vec2 size = getCanvasSize(); - x /= size.x; - y /= size.y; + vec2 pickPoint{ x, y }; PickRay result; if (isHMDMode()) { - getApplicationCompositor().computeHmdPickRay(glm::vec2(x, y), result.origin, result.direction); + getApplicationCompositor().computeHmdPickRay(pickPoint, result.origin, result.direction); } else { - getViewFrustum()->computePickRay(x, y, result.origin, result.direction); + pickPoint /= getCanvasSize(); + getViewFrustum()->computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction); } return result; } +MyAvatar* Application::getMyAvatar() const { + return DependencyManager::get()->getMyAvatar(); +} + +const glm::vec3& Application::getAvatarPosition() const { + return getMyAvatar()->getPosition(); +} + QImage Application::renderAvatarBillboard(RenderArgs* renderArgs) { const int BILLBOARD_SIZE = 64; @@ -3561,7 +3390,7 @@ namespace render { skybox = skyStage->getSkybox(); if (skybox) { - skybox->render(batch, *(Application::getInstance()->getDisplayViewFrustum())); + skybox->render(batch, *(qApp->getDisplayViewFrustum())); } } } @@ -3572,9 +3401,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se // FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering. // Then we can move this logic into the Avatar::simulate call. - _myAvatar->startRender(); - _myAvatar->preRender(renderArgs); - _myAvatar->endRender(); + auto myAvatar = getMyAvatar(); + myAvatar->startRender(); + myAvatar->preRender(renderArgs); + myAvatar->endRender(); activeRenderingThread = QThread::currentThread(); @@ -3688,9 +3518,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se _renderEngine->setRenderContext(renderContext); // Before the deferred pass, let's try to use the render engine - _myAvatar->startRenderRun(); + myAvatar->startRenderRun(); _renderEngine->run(); - _myAvatar->endRenderRun(); + myAvatar->endRenderRun(); auto engineRC = _renderEngine->getRenderContext(); sceneInterface->setEngineFeedOpaqueItems(engineRC->_numFeedOpaqueItems); @@ -3713,15 +3543,17 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi float aspect = (float)region.width() / region.height(); float fov = MIRROR_FIELD_OF_VIEW; + auto myAvatar = getMyAvatar(); + // bool eyeRelativeCamera = false; if (billboard) { fov = BILLBOARD_FIELD_OF_VIEW; // degees - _mirrorCamera.setPosition(_myAvatar->getPosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setPosition(myAvatar->getPosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * myAvatar->getScale()); } else if (!AvatarInputs::getInstance()->mirrorZoomed()) { - _mirrorCamera.setPosition(_myAvatar->getChestPosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setPosition(myAvatar->getChestPosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); } else { // HEAD zoom level // FIXME note that the positioing of the camera relative to the avatar can suffer limited @@ -3740,11 +3572,11 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further // investigated in order to adapt the technique while fixing the head rendering issue, // but the complexity of the hack suggests that a better approach - _mirrorCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale()); + _mirrorCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale()); } _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); - _mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); + _mirrorCamera.setRotation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f))); // set the bounds of rear mirror view @@ -3778,30 +3610,11 @@ void Application::resetSensors() { QPoint windowCenter = mainWindow->geometry().center(); _glWidget->cursor().setPos(currentScreen, windowCenter); - _myAvatar->reset(); + getMyAvatar()->reset(); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } -static void setShortcutsEnabled(QWidget* widget, bool enabled) { - foreach (QAction* action, widget->actions()) { - QKeySequence shortcut = action->shortcut(); - if (!shortcut.isEmpty() && (shortcut[0] & (Qt::CTRL | Qt::ALT | Qt::META)) == 0) { - // it's a shortcut that may coincide with a "regular" key, so switch its context - action->setShortcutContext(enabled ? Qt::WindowShortcut : Qt::WidgetShortcut); - } - } - foreach (QObject* child, widget->children()) { - if (child->isWidgetType()) { - setShortcutsEnabled(static_cast(child), enabled); - } - } -} - -void Application::setMenuShortcutsEnabled(bool enabled) { - setShortcutsEnabled(_window->menuBar(), enabled); -} - void Application::updateWindowTitle(){ QString buildVersion = " (build " + applicationVersion() + ")"; @@ -3840,7 +3653,6 @@ void Application::clearDomainOctreeDetails() { // reset the model renderer _entities.clear(); - } void Application::domainChanged(const QString& domainHostname) { @@ -3881,7 +3693,7 @@ void Application::connectedToDomain(const QString& hostname) { void Application::nodeAdded(SharedNodePointer node) { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet right away - _myAvatar->sendIdentityPacket(); + getMyAvatar()->sendIdentityPacket(); } else if (node->getType() == NodeType::AssetServer) { // the addition of an asset-server always re-enables the upload to asset server menu option Menu::getInstance()->getActionForOption(MenuOption::UploadAsset)->setEnabled(true); @@ -4070,7 +3882,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri AvatarManager::registerMetaTypes(scriptEngine); // hook our avatar and avatar hash map object into this script engine - scriptEngine->registerGlobalObject("MyAvatar", _myAvatar); + scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar()); qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue); scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get().data()); @@ -4130,7 +3942,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("HMD", DependencyManager::get().data()); scriptEngine->registerFunction("HMD", "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); @@ -4243,7 +4055,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { msgBox.exec(); if (msgBox.clickedButton() == bodyAndHeadButton) { - _myAvatar->useFullAvatarURL(url, modelName); + getMyAvatar()->useFullAvatarURL(url, modelName); emit fullAvatarURLChanged(url, modelName); } else { qCDebug(interfaceapp) << "Declined to use the avatar: " << url; @@ -4371,8 +4183,8 @@ void Application::stopAllScripts(bool restart) { // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. // TODO: expose animation priorities and provide a layered animation control system. - _myAvatar->clearJointAnimationPriorities(); - _myAvatar->clearScriptableSettings(); + getMyAvatar()->clearJointAnimationPriorities(); + getMyAvatar()->clearScriptableSettings(); } bool Application::stopScript(const QString& scriptHash, bool restart) { @@ -4390,10 +4202,10 @@ bool Application::stopScript(const QString& scriptHash, bool restart) { // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. // TODO: expose animation priorities and provide a layered animation control system. - _myAvatar->clearJointAnimationPriorities(); + getMyAvatar()->clearJointAnimationPriorities(); } if (_scriptEnginesHash.empty()) { - _myAvatar->clearScriptableSettings(); + getMyAvatar()->clearScriptableSettings(); } return stoppedScript; } @@ -4414,14 +4226,6 @@ void Application::loadDefaultScripts() { } } -void Application::manageRunningScriptsWidgetVisibility(bool shown) { - if (_runningScriptsWidgetWasVisible && shown) { - _runningScriptsWidget->show(); - } else if (_runningScriptsWidgetWasVisible && !shown) { - _runningScriptsWidget->hide(); - } -} - void Application::toggleRunningScriptsWidget() { if (_runningScriptsWidget->isVisible()) { if (_runningScriptsWidget->hasFocus()) { @@ -4452,22 +4256,6 @@ void Application::openUrl(const QUrl& url) { } } -void Application::updateMyAvatarTransform() { - const float SIMULATION_OFFSET_QUANTIZATION = 16.0f; // meters - glm::vec3 avatarPosition = _myAvatar->getPosition(); - glm::vec3 physicsWorldOffset = _physicsEngine->getOriginOffset(); - if (glm::distance(avatarPosition, physicsWorldOffset) > SIMULATION_OFFSET_QUANTIZATION) { - glm::vec3 newOriginOffset = avatarPosition; - int halfExtent = (int)HALF_SIMULATION_EXTENT; - for (int i = 0; i < 3; ++i) { - newOriginOffset[i] = (float)(glm::max(halfExtent, - ((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION)); - } - // TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine - _physicsEngine->setOriginOffset(newOriginOffset); - } -} - void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) { // from the domain-handler, figure out the satoshi cost per voxel and per meter cubed const QString VOXEL_SETTINGS_KEY = "voxels"; @@ -4496,17 +4284,7 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject } QString Application::getPreviousScriptLocation() { - QString suggestedName; - if (_previousScriptLocation.get().isEmpty()) { - QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); -// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 -#ifdef __APPLE__ - suggestedName = desktopLocation.append("/script.js"); -#endif - } else { - suggestedName = _previousScriptLocation.get(); - } - return suggestedName; + return _previousScriptLocation.get(); } void Application::setPreviousScriptLocation(const QString& previousScriptLocation) { @@ -4526,7 +4304,7 @@ void Application::loadDialog() { } void Application::loadScriptURLDialog() { - QInputDialog scriptURLDialog(Application::getInstance()->getWindow()); + QInputDialog scriptURLDialog(getWindow()); scriptURLDialog.setWindowTitle("Open and Run Script URL"); scriptURLDialog.setLabelText("Script:"); scriptURLDialog.setWindowFlags(Qt::Sheet); @@ -4586,10 +4364,6 @@ void Application::takeSnapshot() { } -void Application::setThrottleFPSEnabled() { - _isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus); -} - float Application::getRenderResolutionScale() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.0f; @@ -4650,7 +4424,7 @@ void Application::notifyPacketVersionMismatch() { } void Application::checkSkeleton() { - if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) { + if (getMyAvatar()->getSkeletonModel().isActive() && !getMyAvatar()->getSkeletonModel().hasSkeleton()) { qCDebug(interfaceapp) << "MyAvatar model has no skeleton"; QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded..."; @@ -4660,9 +4434,9 @@ void Application::checkSkeleton() { msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); - _myAvatar->useFullAvatarURL(AvatarData::defaultFullAvatarModelUrl(), DEFAULT_FULL_AVATAR_MODEL_NAME); + getMyAvatar()->useFullAvatarURL(AvatarData::defaultFullAvatarModelUrl(), DEFAULT_FULL_AVATAR_MODEL_NAME); } else { - _physicsEngine->setCharacterController(_myAvatar->getCharacterController()); + _physicsEngine->setCharacterController(getMyAvatar()->getCharacterController()); } } @@ -4681,21 +4455,20 @@ void Application::activeChanged(Qt::ApplicationState state) { } } void Application::showFriendsWindow() { + const QString FRIENDS_WINDOW_OBJECT_NAME = "FriendsWindow"; const QString FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; const QString FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; const int FRIENDS_WINDOW_WIDTH = 290; const int FRIENDS_WINDOW_HEIGHT = 500; - if (!_friendsWindow) { - _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, - FRIENDS_WINDOW_HEIGHT, false); - connect(_friendsWindow, &WebWindowClass::closed, this, &Application::friendsWindowClosed); + auto webWindowClass = _window->findChildren(FRIENDS_WINDOW_OBJECT_NAME); + if (webWindowClass.empty()) { + auto friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, + FRIENDS_WINDOW_HEIGHT, false); + friendsWindow->setParent(_window); + friendsWindow->setObjectName(FRIENDS_WINDOW_OBJECT_NAME); + connect(friendsWindow, &WebWindowClass::closed, &WebWindowClass::deleteLater); + friendsWindow->setVisible(true); } - _friendsWindow->setVisible(true); -} - -void Application::friendsWindowClosed() { - delete _friendsWindow; - _friendsWindow = NULL; } void Application::postLambdaEvent(std::function f) { @@ -4725,7 +4498,7 @@ QSize Application::getDeviceSize() const { } PickRay Application::computePickRay() const { - return computePickRay(getTrueMouseX(), getTrueMouseY()); + return computePickRay(getTrueMouse().x, getTrueMouse().y); } bool Application::isThrottleRendering() const { @@ -4759,7 +4532,7 @@ qreal Application::getDevicePixelRatio() { return (_window && _window->windowHandle()) ? _window->windowHandle()->devicePixelRatio() : 1.0; } -DisplayPlugin * Application::getActiveDisplayPlugin() { +DisplayPlugin* Application::getActiveDisplayPlugin() { if (nullptr == _displayPlugin) { updateDisplayMode(); Q_ASSERT(_displayPlugin); @@ -4767,7 +4540,7 @@ DisplayPlugin * Application::getActiveDisplayPlugin() { return _displayPlugin.data(); } -const DisplayPlugin * Application::getActiveDisplayPlugin() const { +const DisplayPlugin* Application::getActiveDisplayPlugin() const { return ((Application*)this)->getActiveDisplayPlugin(); } @@ -4775,7 +4548,6 @@ bool _activatingDisplayPlugin{ false }; QVector> _currentDisplayPluginActions; QVector> _currentInputPluginActions; - static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { auto menu = Menu::getInstance(); QString name = displayPlugin->getName(); @@ -4980,19 +4752,25 @@ mat4 Application::getEyeProjection(int eye) const { mat4 Application::getEyePose(int eye) const { if (isHMDMode()) { - return getActiveDisplayPlugin()->getEyePose((Eye)eye); + auto hmdInterface = DependencyManager::get(); + float IPDScale = hmdInterface->getIPDScale(); + auto displayPlugin = getActiveDisplayPlugin(); + mat4 headPose = displayPlugin->getHeadPose(); + mat4 eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye); + { + vec3 eyeOffset = glm::vec3(eyeToHead[3]); + // Apply IPD scaling + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); + eyeToHead[3] = vec4(eyeOffset, 1.0); + } + return eyeToHead * headPose; } - return mat4(); } mat4 Application::getEyeOffset(int eye) const { - if (isHMDMode()) { - mat4 identity; - return getActiveDisplayPlugin()->getView((Eye)eye, identity); - } - - return mat4(); + // FIXME invert? + return getActiveDisplayPlugin()->getEyeToHeadTransform((Eye)eye); } mat4 Application::getHMDSensorPose() const { @@ -5023,8 +4801,8 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float // transform from sensor space, to world space, to avatar model space. glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); - glm::mat4 sensorToWorldMat = _myAvatar->getSensorToWorldMatrix(); - glm::mat4 modelMat = createMatFromQuatAndPos(_myAvatar->getOrientation(), _myAvatar->getPosition()); + glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix(); + glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition()); glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; glm::vec3 position = extractTranslation(objectPose); @@ -5095,17 +4873,17 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; - if (qApp->isHMDMode()) { - pos = qApp->getApplicationCompositor().getPalmClickLocation(palm); + if (isHMDMode()) { + pos = getApplicationCompositor().getPalmClickLocation(palm); } else { // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(_myAvatar->getOrientation()) * palm->getFingerDirection(); + glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection(); // Get the angles, scaled between (-0.5,0.5) float xAngle = (atan2f(direction.z, direction.x) + (float)M_PI_2); float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); - auto canvasSize = qApp->getCanvasSize(); + auto canvasSize = getCanvasSize(); // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult(); @@ -5120,14 +4898,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) if (_oldHandLeftClick[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); - qApp->mouseReleaseEvent(&mouseEvent, deviceID); + mouseReleaseEvent(&mouseEvent, deviceID); _oldHandLeftClick[index] = false; } if (_oldHandRightClick[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); - qApp->mouseReleaseEvent(&mouseEvent, deviceID); + mouseReleaseEvent(&mouseEvent, deviceID); _oldHandRightClick[index] = false; } @@ -5141,7 +4919,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) // Only send the mouse event if the opposite left button isnt held down. // Is this check necessary? if (!_oldHandLeftClick[(int)(!index)]) { - qApp->mouseMoveEvent(&mouseEvent, deviceID); + mouseMoveEvent(&mouseEvent, deviceID); } } _oldHandMouseX[index] = pos.x(); @@ -5162,12 +4940,12 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); - qApp->mousePressEvent(&mouseEvent, deviceID); + mousePressEvent(&mouseEvent, deviceID); } } else if (_oldHandRightClick[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); - qApp->mouseReleaseEvent(&mouseEvent, deviceID); + mouseReleaseEvent(&mouseEvent, deviceID); _oldHandRightClick[index] = false; } @@ -5179,12 +4957,12 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); - qApp->mousePressEvent(&mouseEvent, deviceID); + mousePressEvent(&mouseEvent, deviceID); } } else if (_oldHandLeftClick[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); - qApp->mouseReleaseEvent(&mouseEvent, deviceID); + mouseReleaseEvent(&mouseEvent, deviceID); _oldHandLeftClick[index] = false; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 20ae2f23ab..ec5133b131 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -12,6 +12,8 @@ #ifndef hifi_Application_h #define hifi_Application_h +#include + #include #include #include @@ -19,103 +21,52 @@ #include #include #include -#include #include #include #include #include -#include #include -#include #include -#include #include #include #include #include #include -#include -#include -#include #include +#include +#include -#include "AudioClient.h" +#include "avatar/AvatarUpdate.h" +#include "avatar/MyAvatar.h" #include "Bookmarks.h" #include "Camera.h" #include "Environment.h" #include "FileLogger.h" +#include "gpu/Context.h" #include "Menu.h" -#include "Physics.h" -#include "Stars.h" -#include "avatar/AvatarUpdate.h" -#include "avatar/Avatar.h" -#include "avatar/MyAvatar.h" +#include "octree/OctreePacketProcessor.h" +#include "render/Engine.h" #include "scripting/ControllerScriptingInterface.h" #include "scripting/DialogsManagerScriptingInterface.h" -#include "scripting/WebWindowClass.h" +#include "ui/ApplicationCompositor.h" +#include "ui/ApplicationOverlay.h" #include "ui/AudioStatsDialog.h" #include "ui/BandwidthDialog.h" -#include "ui/ModelsBrowser.h" -#include "ui/OctreeStatsDialog.h" -#include "ui/SnapshotShareDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" -#include "ui/overlays/Overlays.h" -#include "ui/ApplicationOverlay.h" -#include "ui/ApplicationCompositor.h" +#include "ui/OctreeStatsDialog.h" #include "ui/OverlayConductor.h" +#include "ui/overlays/Overlays.h" #include "ui/RunningScriptsWidget.h" +#include "ui/SnapshotShareDialog.h" #include "ui/ToolWindow.h" -#include "octree/OctreePacketProcessor.h" #include "UndoStackScriptingInterface.h" -#include "gpu/Context.h" - -#include "render/Engine.h" - -class QGLWidget; -class QKeyEvent; -class QMouseEvent; -class QSystemTrayIcon; -class QTouchEvent; -class QWheelEvent; class OffscreenGlCanvas; - class GLCanvas; class FaceTracker; class MainWindow; -class Node; -class ScriptEngine; - -namespace gpu { - class Context; - typedef std::shared_ptr ContextPointer; -} - - -static const QString SNAPSHOT_EXTENSION = ".jpg"; -static const QString SVO_EXTENSION = ".svo"; -static const QString SVO_JSON_EXTENSION = ".svo.json"; -static const QString JS_EXTENSION = ".js"; -static const QString FST_EXTENSION = ".fst"; - -static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees -static const float BILLBOARD_DISTANCE = 5.56f; // meters - -static const int MIRROR_VIEW_TOP_PADDING = 5; -static const int MIRROR_VIEW_LEFT_PADDING = 10; -static const int MIRROR_VIEW_WIDTH = 265; -static const int MIRROR_VIEW_HEIGHT = 215; -static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; -static const float MIRROR_REARVIEW_DISTANCE = 0.722f; -static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f; -static const float MIRROR_FIELD_OF_VIEW = 30.0f; - -static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; - -static const QString INFO_HELP_PATH = "html/interface-welcome.html"; -static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; #ifdef Q_OS_WIN static const UINT UWM_IDENTIFY_INSTANCES = @@ -130,24 +81,19 @@ class Application; #endif #define qApp (static_cast(QCoreApplication::instance())) -typedef bool (Application::* AcceptURLMethod)(const QString &); - class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { Q_OBJECT - + + // TODO? Get rid of those friend class OctreePacketProcessor; - friend class DatagramProcessor; + friend class PluginContainerProxy; public: - static Application* getInstance() { return qApp; } // TODO: replace fully by qApp - static const glm::vec3& getPositionForPath() { return getInstance()->_myAvatar->getPosition(); } - static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); } - static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getPositionForAudio(); } - static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getOrientationForAudio(); } + // FIXME? Empty methods, do we still need them? static void initPlugins(); static void shutdownPlugins(); - Application(int& argc, char** argv, QElapsedTimer &startup_time); + Application(int& argc, char** argv, QElapsedTimer& startup_time); ~Application(); void postLambdaEvent(std::function f); @@ -161,27 +107,6 @@ public: void paintGL(); void resizeGL(); - void resizeEvent(QResizeEvent * size); - - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); - - void focusOutEvent(QFocusEvent* event); - void focusInEvent(QFocusEvent* event); - - void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); - void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); - void mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0); - void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); - - void touchBeginEvent(QTouchEvent* event); - void touchEndEvent(QTouchEvent* event); - void touchUpdateEvent(QTouchEvent* event); - - void wheelEvent(QWheelEvent* event); - void dropEvent(QDropEvent* event); - void dragEnterEvent(QDragEnterEvent* event); - bool event(QEvent* event); bool eventFilter(QObject* object, QEvent* event); @@ -190,7 +115,6 @@ public: QSize getDeviceSize() const; bool hasFocus() const; PickRay computePickRay() const; - PickRay computeViewPickRay(float xRatio, float yRatio) const; bool isThrottleRendering() const; @@ -208,49 +132,29 @@ public: EntityTreeRenderer* getEntities() { return &_entities; } QUndoStack* getUndoStack() { return &_undoStack; } MainWindow* getWindow() { return _window; } - OctreeQuery& getOctreeQuery() { return _octreeQuery; } EntityTreePointer getEntityClipboard() { return _entityClipboard; } EntityTreeRenderer* getEntityClipboardRenderer() { return &_entityClipboardRenderer; } EntityEditPacketSender* getEntityEditPacketSender() { return &_entityEditSender; } - bool isMousePressed() const { return _mousePressed; } - bool isMouseHidden() const { return !_cursorVisible; } - const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } - const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } - bool mouseOnScreen() const; - ivec2 getMouse() const; ivec2 getTrueMouse() const; - ivec2 getMouseDragStarted() const; - ivec2 getTrueMouseDragStarted() const; - - // TODO get rid of these and use glm types directly - int getMouseX() const { return getMouse().x; } - int getMouseY() const { return getMouse().y; } - int getTrueMouseX() const { return getTrueMouse().x; } - int getTrueMouseY() const { return getTrueMouse().y; } - int getMouseDragStartedX() const { return getMouseDragStarted().x; } - int getMouseDragStartedY() const { return getMouseDragStarted().y; } - int getTrueMouseDragStartedX() const { return getTrueMouseDragStarted().x; } - int getTrueMouseDragStartedY() const { return getTrueMouseDragStarted().y; } bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; } FaceTracker* getActiveFaceTracker(); FaceTracker* getSelectedFaceTracker(); - QSystemTrayIcon* getTrayIcon() { return _trayIcon; } ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } ApplicationCompositor& getApplicationCompositor() { return _compositor; } const ApplicationCompositor& getApplicationCompositor() const { return _compositor; } Overlays& getOverlays() { return _overlays; } + bool isForeground() const { return _isForeground; } + float getFps() const { return _fps; } float getFieldOfView() { return _fieldOfView.get(); } - void setFieldOfView(float fov) { _fieldOfView.set(fov); } - - bool importSVOFromURL(const QString& urlString); + void setFieldOfView(float fov); NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } @@ -259,21 +163,14 @@ public: virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); - void resetProfile(const QString& username); - - virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera); - QImage renderAvatarBillboard(RenderArgs* renderArgs); - void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false); - - virtual const glm::vec3& getShadowDistances() const { return _shadowDistances; } virtual ViewFrustum* getCurrentViewFrustum() { return getDisplayViewFrustum(); } virtual QThread* getMainThread() { return thread(); } virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; virtual PickRay computePickRay(float x, float y) const; - virtual const glm::vec3& getAvatarPosition() const { return _myAvatar->getPosition(); } + virtual const glm::vec3& getAvatarPosition() const; virtual void overrideEnvironmentData(const EnvironmentData& newData) { _environment.override(newData); } virtual void endOverrideEnvironmentData() { _environment.endOverride(); } virtual qreal getDevicePixelRatio(); @@ -283,8 +180,6 @@ public: DisplayPlugin* getActiveDisplayPlugin(); const DisplayPlugin* getActiveDisplayPlugin() const; -public: - FileLogger* getLogger() { return _logger; } glm::vec2 getViewportDimensions() const; @@ -293,11 +188,8 @@ public: QStringList getRunningScripts() { return _scriptEnginesHash.keys(); } ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); } - - bool isLookingAtMyAvatar(AvatarSharedPointer avatar); float getRenderResolutionScale() const; - int getRenderAmbientLight() const; bool isAboutToQuit() const { return _aboutToQuit; } @@ -318,7 +210,6 @@ public: QString getScriptsLocation(); void setScriptsLocation(const QString& scriptsLocation); - void initializeAcceptedFiles(); bool canAcceptURL(const QString& url); bool acceptURL(const QString& url); @@ -326,30 +217,20 @@ public: int getMaxOctreePacketsPerSecond(); render::ScenePointer getMain3DScene() { return _main3DScene; } - render::EnginePointer getRenderEngine() { return _renderEngine; } - render::ScenePointer getMain3DScene() const { return _main3DScene; } - + render::EnginePointer getRenderEngine() { return _renderEngine; } gpu::ContextPointer getGPUContext() const { return _gpuContext; } const QRect& getMirrorViewRect() const { return _mirrorViewRect; } void updateMyAvatarLookAtPosition(); AvatarUpdate* getAvatarUpdater() { return _avatarUpdate; } - MyAvatar* getMyAvatar() { return _myAvatar; } float getAvatarSimrate(); void setAvatarSimrateSample(float sample); float getAverageSimsPerSecond(); signals: - - /// Fired when we're simulating; allows external parties to hook in. - void simulating(float deltaTime); - - /// Fired when the import window is closed - void importDone(); - void scriptLocationChanged(const QString& newPath); void svoImportRequested(const QString& url); @@ -357,23 +238,12 @@ signals: void checkBackgroundDownloads(); void domainConnectionRefused(const QString& reason); - void headURLChanged(const QString& newValue, const QString& modelName); - void bodyURLChanged(const QString& newValue, const QString& modelName); void fullAvatarURLChanged(const QString& newValue, const QString& modelName); void beforeAboutToQuit(); void activeDisplayPluginChanged(); public slots: - void setSessionUUID(const QUuid& sessionUUID); - void domainChanged(const QString& domainHostname); - void updateWindowTitle(); - void nodeAdded(SharedNodePointer node); - void nodeKilled(SharedNodePointer node); - void packetSent(quint64 length); - void updateDisplayMode(); - void updateInputModes(); - QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs); bool exportEntities(const QString& filename, float x, float y, float z, float scale); @@ -383,64 +253,50 @@ public slots: void loadDialog(); void loadScriptURLDialog(); void toggleLogDialog(); - bool acceptSnapshot(const QString& urlString); - bool askToSetAvatarUrl(const QString& url); - bool askToLoadScript(const QString& scriptFilenameOrURL); ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); - void reloadScript(const QString& scriptName, bool isUserLoaded = true); - void scriptFinished(const QString& scriptName); void stopAllScripts(bool restart = false); bool stopScript(const QString& scriptHash, bool restart = false); void reloadAllScripts(); void reloadOneScript(const QString& scriptName); void loadDefaultScripts(); void toggleRunningScriptsWidget(); - void saveScripts(); void showFriendsWindow(); - void friendsWindowClosed(); void packageModel(); void openUrl(const QUrl& url); - void updateMyAvatarTransform(); void setAvatarUpdateThreading(); void setAvatarUpdateThreading(bool isThreaded); void setRawAvatarUpdateThreading(); void setRawAvatarUpdateThreading(bool isThreaded); - void domainSettingsReceived(const QJsonObject& domainSettingsObject); - - void setThrottleFPSEnabled(); - bool isThrottleFPSEnabled() { return _isThrottleFPSEnabled; } - void resetSensors(); void setActiveFaceTracker(); - + +#ifdef HAVE_IVIEWHMD void setActiveEyeTracker(); void calibrateEyeTracker1Point(); void calibrateEyeTracker3Points(); void calibrateEyeTracker5Points(); +#endif void aboutApp(); void showEditEntitiesHelp(); - void loadSettings(); - void saveSettings(); - - void notifyPacketVersionMismatch(); - - void handleDomainConnectionDeniedPacket(QSharedPointer packet); - void cameraMenuChanged(); void reloadResourceCaches(); void crashApplication(); - + + void rotationModeChanged(); + + void runTests(); + private slots: void clearDomainOctreeDetails(); void checkFPS(); @@ -452,27 +308,37 @@ private slots: void connectedToDomain(const QString& hostname); - void rotationModeChanged(); - - void closeMirrorView(); - void restoreMirrorView(); - void shrinkMirrorView(); - - void manageRunningScriptsWidgetVisibility(bool shown); - - void runTests(); - void audioMuteToggled(); void faceTrackerMuteToggled(); - void setCursorVisible(bool visible); void activeChanged(Qt::ApplicationState state); - + + void domainSettingsReceived(const QJsonObject& domainSettingsObject); + void handleDomainConnectionDeniedPacket(QSharedPointer packet); + + void notifyPacketVersionMismatch(); + + void loadSettings(); + void saveSettings(); + + void scriptFinished(const QString& scriptName); + void saveScripts(); + void reloadScript(const QString& scriptName, bool isUserLoaded = true); + + bool acceptSnapshot(const QString& urlString); + bool askToSetAvatarUrl(const QString& url); + bool askToLoadScript(const QString& scriptFilenameOrURL); + + void setSessionUUID(const QUuid& sessionUUID); + void domainChanged(const QString& domainHostname); + void updateWindowTitle(); + void nodeAdded(SharedNodePointer node); + void nodeKilled(SharedNodePointer node); + void packetSent(quint64 length); + void updateDisplayMode(); + void updateInputModes(); + private: - void resetCameras(Camera& camera, const glm::uvec2& size); - - void sendPingPackets(); - void initDisplay(); void init(); @@ -487,15 +353,8 @@ private: // Various helper functions called during update() void updateLOD(); - void updateMouseRay(); void updateThreads(float deltaTime); - void updateCamera(float deltaTime); void updateDialogs(float deltaTime); - void updateCursor(float deltaTime); - - Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); - - void renderLookatIndicator(glm::vec3 pointOfInterest); void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -504,12 +363,45 @@ private: void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool billboard = false); - void setMenuShortcutsEnabled(bool enabled); - - static void attachNewHeadToNode(Node *newNode); - static void* networkReceive(void* args); // network receive thread - int sendNackPackets(); + + void takeSnapshot(); + + MyAvatar* getMyAvatar() const; + + void checkSkeleton(); + + void initializeAcceptedFiles(); + int getRenderAmbientLight() const; + + void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false); + + bool importSVOFromURL(const QString& urlString); + + int processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode); + void trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket); + + void resizeEvent(QResizeEvent* size); + + void keyPressEvent(QKeyEvent* event); + void keyReleaseEvent(QKeyEvent* event); + + void focusOutEvent(QFocusEvent* event); + void focusInEvent(QFocusEvent* event); + + void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); + void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); + void mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0); + void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); + + void touchBeginEvent(QTouchEvent* event); + void touchEndEvent(QTouchEvent* event); + void touchUpdateEvent(QTouchEvent* event); + + void wheelEvent(QWheelEvent* event); + void dropEvent(QDropEvent* event); + void dragEnterEvent(QDragEnterEvent* event); + bool _dependencyManagerIsSetup; @@ -520,21 +412,15 @@ private: MainWindow* _window; ToolWindow* _toolWindow; - WebWindowClass* _friendsWindow; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; - glm::vec3 _gravity; - // Frame Rate Measurement - int _frameCount; float _fps; - QElapsedTimer _applicationStartupTime; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; - bool _justStarted; ShapeManager _shapeManager; PhysicalEntitySimulation _entitySimulation; @@ -550,12 +436,9 @@ private: ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; - float _trailingAudioLoudness; - OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad - MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) AvatarUpdate* _avatarUpdate {nullptr}; SimpleMovingAverage _avatarSimsPerSecond {10}; int _avatarSimsPerSecondReport {0}; @@ -564,37 +447,19 @@ private: Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; - Setting::Handle _firstRun; - Setting::Handle _previousScriptLocation; - Setting::Handle _scriptsLocationHandle; - Setting::Handle _fieldOfView; + Setting::Handle _firstRun; + Setting::Handle _previousScriptLocation; + Setting::Handle _scriptsLocationHandle; + Setting::Handle _fieldOfView; float _scaleMirror; float _rotateMirror; float _raiseMirror; - static const int CASCADED_SHADOW_MATRIX_COUNT = 4; - glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT]; - glm::vec3 _shadowDistances; - Environment _environment; - bool _cursorVisible; - ivec2 _mouseDragStarted; - - quint64 _lastMouseMove; bool _lastMouseMoveWasSimulated; - glm::vec3 _mouseRayOrigin; - glm::vec3 _mouseRayDirection; - - vec2 _touchAvg; - vec2 _touchDragStartedAvg; - - bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) - - bool _mousePressed; // true if mouse has been pressed (clear when finished) - QSet _keysPressed; bool _enableProcessOctreeThread; @@ -605,9 +470,6 @@ private: StDev _idleLoopStdev; float _idleLoopMeasuredJitter; - int processOctreeStats(NLPacket& packet, SharedNodePointer sendingNode); - void trackIncomingOctreePacket(NLPacket& packet, SharedNodePointer sendingNode, bool wasStatsPacket); - NodeToJurisdictionMap _entityServerJurisdictions; NodeToOctreeSceneStats _octreeServerSceneStats; @@ -617,8 +479,6 @@ private: FileLogger* _logger; - void takeSnapshot(); - TouchEvent _lastTouchEvent; RunningScriptsWidget* _runningScriptsWidget; @@ -626,12 +486,8 @@ private: bool _runningScriptsWidgetWasVisible; QString _scriptsLocation; - QSystemTrayIcon* _trayIcon; - quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; - - bool _isThrottleFPSEnabled; bool _aboutToQuit; @@ -643,9 +499,8 @@ private: QTimer _settingsTimer; GLCanvas* _glWidget{ nullptr }; - - void checkSkeleton(); - + + typedef bool (Application::* AcceptURLMethod)(const QString &); QHash _acceptedExtensions; QList _domainConnectionRefusals; @@ -668,10 +523,7 @@ private: int _oldHandMouseY[2]; bool _oldHandLeftClick[2]; bool _oldHandRightClick[2]; - int _numFramesSinceLastResize = 0; - bool _overlayEnabled = true; - QRect _savedGeometry; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); EntityItemID _keyboardFocusedItem; @@ -682,8 +534,6 @@ private: quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; - - friend class PluginContainerProxy; }; #endif // hifi_Application_h diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index f09397322f..10dce38fbc 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -59,7 +59,7 @@ protected: _lastRollTime = now; file.open(QIODevice::WriteOnly | QIODevice::Truncate); file.close(); - qDebug() << "Rolled log file: " << newFileName; + qDebug() << "Rolled log file:" << newFileName; } } } diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index 66aae5343b..d9cde868a9 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -17,6 +17,7 @@ #include #include "MainWindow.h" +#include "Menu.h" static QGLFormat& getDesiredGLFormat() { // Specify an OpenGL 3.3 format using the Core profile. @@ -62,16 +63,16 @@ void GLCanvas::paintGL() { // FIXME - I'm not sure why this still remains, it appears as if this GLCanvas gets a single paintGL call near // the beginning of the application starting up. I'm not sure if we really need to call Application::paintGL() // in this case, since the display plugins eventually handle all the painting - if (!Application::getInstance()->getWindow()->isMinimized() || !Application::getInstance()->isThrottleFPSEnabled()) { - Application::getInstance()->paintGL(); + bool isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus); + if (!qApp->getWindow()->isMinimized() || !isThrottleFPSEnabled) { + qApp->paintGL(); } } void GLCanvas::resizeGL(int width, int height) { - Application::getInstance()->resizeGL(); + qApp->resizeGL(); } -int updateTime = 0; bool GLCanvas::event(QEvent* event) { switch (event->type()) { case QEvent::MouseMove: @@ -95,7 +96,7 @@ bool GLCanvas::event(QEvent* event) { break; case QEvent::Paint: // Ignore paint events that occur after we've decided to quit - if (Application::getInstance()->isAboutToQuit()) { + if (qApp->isAboutToQuit()) { return true; } break; diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index c57f1d0aa7..2a2edef831 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -26,14 +26,14 @@ LODManager::LODManager() { } float LODManager::getLODDecreaseFPS() { - if (Application::getInstance()->isHMDMode()) { + if (qApp->isHMDMode()) { return getHMDLODDecreaseFPS(); } return getDesktopLODDecreaseFPS(); } float LODManager::getLODIncreaseFPS() { - if (Application::getInstance()->isHMDMode()) { + if (qApp->isHMDMode()) { return getHMDLODIncreaseFPS(); } return getDesktopLODIncreaseFPS(); diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index 16aedc4bb7..34e1638e94 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -31,6 +31,7 @@ MainWindow::MainWindow(QWidget* parent) : _windowState("WindowState", 0) { setAcceptDrops(true); + _trayIcon.show(); } void MainWindow::restoreGeometry() { diff --git a/interface/src/MainWindow.h b/interface/src/MainWindow.h index eb262e0f97..6a401cf2b7 100644 --- a/interface/src/MainWindow.h +++ b/interface/src/MainWindow.h @@ -13,6 +13,7 @@ #define __hifi__MainWindow__ #include +#include #include @@ -42,6 +43,7 @@ protected: private: Setting::Handle _windowGeometry; Setting::Handle _windowState; + QSystemTrayIcon _trayIcon; }; #endif /* defined(__hifi__MainWindow__) */ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 96f640b96e..9abfd1e34c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -332,8 +332,7 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true, - qApp, SLOT(setThrottleFPSEnabled())); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true); MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index 469e7f7c77..19bb6ab8de 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -16,8 +16,11 @@ PluginContainerProxy::PluginContainerProxy() { Plugin::setContainer(this); } +PluginContainerProxy::~PluginContainerProxy() { +} + bool PluginContainerProxy::isForeground() { - return qApp->_isForeground && !qApp->getWindow()->isMinimized(); + return qApp->isForeground() && !qApp->getWindow()->isMinimized(); } void PluginContainerProxy::addMenu(const QString& menuName) { @@ -78,7 +81,7 @@ void PluginContainerProxy::setIsOptionChecked(const QString& path, bool checked) // Additionally, setting fullscreen isn't hiding the menu on windows // make it useless for stereoscopic modes. void PluginContainerProxy::setFullscreen(const QScreen* target, bool hideMenu) { - auto _window = qApp->_window; + auto _window = qApp->getWindow(); if (!_window->isFullScreen()) { _savedGeometry = _window->geometry(); } @@ -101,7 +104,7 @@ void PluginContainerProxy::setFullscreen(const QScreen* target, bool hideMenu) { } void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) { - auto _window = qApp->_window; + auto _window = qApp->getWindow(); _window->showNormal(); QRect targetGeometry = _savedGeometry; @@ -151,3 +154,7 @@ void PluginContainerProxy::showDisplayPluginsTools() { QGLWidget* PluginContainerProxy::getPrimarySurface() { return qApp->_glWidget; } + +const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const { + return qApp->getActiveDisplayPlugin(); +} diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h index 95865609c8..79f8287b66 100644 --- a/interface/src/PluginContainerProxy.h +++ b/interface/src/PluginContainerProxy.h @@ -11,6 +11,7 @@ class PluginContainerProxy : public QObject, PluginContainer { Q_OBJECT PluginContainerProxy(); + virtual ~PluginContainerProxy(); virtual void addMenu(const QString& menuName) override; virtual void removeMenu(const QString& menuName) override; virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override; @@ -23,6 +24,8 @@ class PluginContainerProxy : public QObject, PluginContainer { virtual void requestReset() override; virtual QGLWidget* getPrimarySurface() override; virtual bool isForeground() override; + virtual const DisplayPlugin* getActiveDisplayPlugin() const override; + QRect _savedGeometry{ 10, 120, 800, 600 }; friend class Application; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index d09dd41999..3d97d48f90 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Util.h" + #include #include #include @@ -19,17 +21,17 @@ #include #include +#include #include #include -#include #include +#include +#include +#include -#include "world.h" -#include "Application.h" #include "InterfaceLogging.h" - -#include "Util.h" +#include "world.h" using namespace std; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index faf4bffeba..ddee5dfc1f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -72,7 +72,7 @@ namespace render { avatarPtr->setDisplayingLookatTarget(renderLookAtTarget); if (avatarPtr->isInitialized() && args) { - avatarPtr->render(args, Application::getInstance()->getCamera()->getPosition()); + avatarPtr->render(args, qApp->getCamera()->getPosition()); } } } @@ -98,7 +98,7 @@ Avatar::Avatar(RigPointer rig) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread - moveToThread(Application::getInstance()->thread()); + moveToThread(qApp->thread()); // give the pointer to our head to inherited _headData variable from AvatarData _headData = static_cast(new Head(this)); @@ -152,7 +152,7 @@ void Avatar::simulate(float deltaTime) { // update the avatar's position according to its referential if (_referential) { if (_referential->hasExtraData()) { - EntityTreePointer tree = Application::getInstance()->getEntities()->getTree(); + EntityTreePointer tree = qApp->getEntities()->getTree(); switch (_referential->type()) { case Referential::MODEL: _referential = new ModelReferential(_referential, @@ -189,7 +189,7 @@ void Avatar::simulate(float deltaTime) { // simple frustum check float boundingRadius = getBillboardSize(); - bool inViewFrustum = Application::getInstance()->getViewFrustum()->sphereInFrustum(_position, boundingRadius) != + bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(_position, boundingRadius) != ViewFrustum::OUTSIDE; { @@ -245,6 +245,14 @@ void Avatar::simulate(float deltaTime) { measureMotionDerivatives(deltaTime); } +bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) { + const float HEAD_SPHERE_RADIUS = 0.1f; + glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); + glm::vec3 myEyePosition = getHead()->getEyePosition(); + + return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getScale()); +} + void Avatar::slamPosition(const glm::vec3& newPosition) { setPosition(newPosition); _positionDeltaAccumulator = glm::vec3(0.0f); @@ -388,9 +396,9 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { float boundingRadius = getBillboardSize(); ViewFrustum* frustum = nullptr; if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { - frustum = Application::getInstance()->getShadowViewFrustum(); + frustum = qApp->getShadowViewFrustum(); } else { - frustum = Application::getInstance()->getDisplayViewFrustum(); + frustum = qApp->getDisplayViewFrustum(); } if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) { @@ -539,7 +547,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float DISPLAYNAME_DISTANCE = 20.0f; setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); - auto cameraMode = Application::getInstance()->getCamera()->getMode(); + auto cameraMode = qApp->getCamera()->getMode(); if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) { renderDisplayName(batch, *renderArgs->_viewFrustum, renderArgs->_viewport); } @@ -566,7 +574,7 @@ void Avatar::fixupModelsInScene() { // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (_skeletonModel.isRenderable() && _skeletonModel.needsFixupInScene()) { _skeletonModel.removeFromScene(scene, pendingChanges); @@ -653,7 +661,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { } // rotate about vertical to face the camera glm::quat rotation = getOrientation(); - glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position); + glm::vec3 cameraVector = glm::inverse(rotation) * (qApp->getCamera()->getPosition() - _position); rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f)); // compute the size from the billboard camera parameters and scale diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 5ff3f37ef5..1800f4fdd1 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -37,6 +37,9 @@ static const float SCALING_RATIO = .05f; static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1 static const float RESCALING_TOLERANCE = .02f; +static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees +static const float BILLBOARD_DISTANCE = 5.56f; // meters + extern const float CHAT_MESSAGE_SCALE; extern const float CHAT_MESSAGE_HEIGHT; @@ -77,7 +80,7 @@ public: typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; - + void init(); void simulate(float deltaTime); @@ -198,7 +201,9 @@ protected: glm::vec3 _worldUpDirection; float _stringLength; bool _moving; ///< set when position is changing - + + bool isLookingAtMe(AvatarSharedPointer avatar); + // 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 1790682e56..69d76db7de 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -81,7 +81,7 @@ void AvatarManager::init() { connect(DependencyManager::get().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged, this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (DependencyManager::get()->shouldRenderAvatars()) { _myAvatar->addToScene(_myAvatar, scene, pendingChanges); @@ -146,7 +146,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { const float SHRINK_RATE = 0.9f; const float MIN_FADE_SCALE = 0.001f; - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; while (fadingIterator != _avatarFades.end()) { auto avatar = std::static_pointer_cast(*fadingIterator); @@ -171,7 +171,7 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { // virtual AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { auto avatar = std::dynamic_pointer_cast(AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer)); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (DependencyManager::get()->shouldRenderAvatars()) { avatar->addToScene(avatar, scene, pendingChanges); @@ -328,7 +328,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { if (DependencyManager::get()->shouldRenderAvatars()) { for (auto avatarData : _avatarHash) { auto avatar = std::dynamic_pointer_cast(avatarData); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; avatar->addToScene(avatar, scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); @@ -336,7 +336,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { } else { for (auto avatarData : _avatarHash) { auto avatar = std::dynamic_pointer_cast(avatarData); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; avatar->removeFromScene(avatar, scene, pendingChanges); scene->enqueuePendingChanges(pendingChanges); diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index a4391172a7..acdb251950 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -29,11 +29,11 @@ AvatarUpdate::AvatarUpdate() : GenericThread(), _lastAvatarUpdate(0) { void AvatarUpdate::synchronousProcess() { // Keep our own updated value, so that our asynchronous code can consult it. - _isHMDMode = Application::getInstance()->isHMDMode(); - _headPose = Application::getInstance()->getActiveDisplayPlugin()->getHeadPose(); + _isHMDMode = qApp->isHMDMode(); + _headPose = qApp->getActiveDisplayPlugin()->getHeadPose(); if (_updateBillboard) { - Application::getInstance()->getMyAvatar()->doUpdateBillboard(); + DependencyManager::get()->getMyAvatar()->doUpdateBillboard(); } if (!isThreaded()) { @@ -47,7 +47,7 @@ bool AvatarUpdate::process() { quint64 deltaMicroseconds = start - _lastAvatarUpdate; _lastAvatarUpdate = start; float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND; - Application::getInstance()->setAvatarSimrateSample(1.0f / deltaSeconds); + qApp->setAvatarSimrateSample(1.0f / deltaSeconds); QSharedPointer manager = DependencyManager::get(); MyAvatar* myAvatar = manager->getMyAvatar(); @@ -57,7 +57,7 @@ bool AvatarUpdate::process() { manager->updateOtherAvatars(deltaSeconds); myAvatar->startUpdate(); - Application::getInstance()->updateMyAvatarLookAtPosition(); + qApp->updateMyAvatarLookAtPosition(); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes manager->updateMyAvatar(deltaSeconds); myAvatar->endUpdate(); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d9a1b190d6..1a9b8a49e2 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -94,7 +94,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { // Only use face trackers when not playing back a recording. if (!myAvatar->isPlaying()) { - FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker(); + FaceTracker* faceTracker = qApp->getActiveFaceTracker(); _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); if (_isFaceTrackerConnected) { _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9654305d70..7592aad061 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include "devices/Faceshift.h" + #include "Application.h" #include "AvatarManager.h" #include "Environment.h" @@ -126,7 +128,7 @@ MyAvatar::~MyAvatar() { } QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { - CameraMode mode = Application::getInstance()->getCamera()->getMode(); + CameraMode mode = qApp->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = _position; @@ -374,7 +376,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { return; } - FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); + FaceTracker* tracker = qApp->getActiveFaceTracker(); bool inFacetracker = tracker && !tracker->isMuted(); if (inHmd) { @@ -388,7 +390,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { estimatedPosition = tracker->getHeadTranslation(); _trackedHeadPosition = estimatedPosition; estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); - if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) { // Invert yaw and roll when in mirror mode // NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror // it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to @@ -426,8 +428,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { head->setDeltaYaw(estimatedRotation.y); head->setDeltaRoll(estimatedRotation.z); } else { - float magnifyFieldOfView = qApp->getFieldOfView() / - _realWorldFieldOfView.get(); + float magnifyFieldOfView = qApp->getViewFrustum()->getFieldOfView() / _realWorldFieldOfView.get(); head->setDeltaPitch(estimatedRotation.x * magnifyFieldOfView); head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView); head->setDeltaRoll(estimatedRotation.z); @@ -436,16 +437,16 @@ void MyAvatar::updateFromTrackers(float deltaTime) { // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f); - const float MAX_LEAN = 45.0f; // Invert left/right lean when in mirror mode // NOTE: this is kinda a hack, it's the same hack we use to make the head tilt. But it's not really a mirror // it just makes you feel like you're looking in a mirror because the body movements of the avatar appear to // match your body movements. - if ((inHmd || inFacetracker) && Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + if ((inHmd || inFacetracker) && qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) { relativePosition.x = -relativePosition.x; } + const float MAX_LEAN = 45.0f; head->setLeanSideways(glm::clamp(glm::degrees(atanf(relativePosition.x * _leanScale / TORSO_LENGTH)), -MAX_LEAN, MAX_LEAN)); head->setLeanForward(glm::clamp(glm::degrees(atanf(relativePosition.z * _leanScale / TORSO_LENGTH)), @@ -538,7 +539,7 @@ void MyAvatar::clearReferential() { } bool MyAvatar::setModelReferential(const QUuid& id) { - EntityTreePointer tree = Application::getInstance()->getEntities()->getTree(); + EntityTreePointer tree = qApp->getEntities()->getTree(); changeReferential(new ModelReferential(id, tree, this)); if (_referential->isValid()) { return true; @@ -549,7 +550,7 @@ bool MyAvatar::setModelReferential(const QUuid& id) { } bool MyAvatar::setJointReferential(const QUuid& id, int jointIndex) { - EntityTreePointer tree = Application::getInstance()->getEntities()->getTree(); + EntityTreePointer tree = qApp->getEntities()->getTree(); changeReferential(new JointReferential(jointIndex, id, tree, this)); if (!_referential->isValid()) { return true; @@ -856,7 +857,7 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { } void MyAvatar::setEnableMeshVisible(bool isEnabled) { - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); _skeletonModel.setVisibleInScene(isEnabled, scene); } @@ -1005,7 +1006,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _targetAvatarPosition = glm::vec3(0.0f); glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT; - glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f; const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; @@ -1023,7 +1024,7 @@ void MyAvatar::updateLookAtTargetAvatar() { _targetAvatarPosition = avatarPointer->getPosition(); smallestAngleTo = angleTo; } - if (Application::getInstance()->isLookingAtMyAvatar(avatar)) { + if (isLookingAtMe(avatar)) { // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. @@ -1035,11 +1036,11 @@ void MyAvatar::updateLookAtTargetAvatar() { // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. - glm::mat4 leftEye = Application::getInstance()->getEyeOffset(Eye::Left); - glm::mat4 rightEye = Application::getInstance()->getEyeOffset(Eye::Right); + glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left); + glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right); glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); - auto humanSystem = Application::getInstance()->getViewFrustum(); + auto humanSystem = qApp->getViewFrustum(); glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); @@ -1065,7 +1066,7 @@ void MyAvatar::updateLookAtTargetAvatar() { */ // And now we can finally add that offset to the camera. - glm::vec3 corrected = Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset; + glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset; avatar->getHead()->setCorrectedLookAtPosition(corrected); @@ -1172,7 +1173,7 @@ void MyAvatar::clearJointAnimationPriorities() { void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { Avatar::setFaceModelURL(faceModelURL); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); getHead()->getFaceModel().setVisibleInScene(_prevShouldDrawHead, scene); _billboardValid = false; } @@ -1180,7 +1181,7 @@ void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { Avatar::setSkeletonModelURL(skeletonModelURL); - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); _billboardValid = false; _skeletonModel.setVisibleInScene(true, scene); _headBoneSet.clear(); @@ -1238,7 +1239,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) } glm::vec3 MyAvatar::getSkeletonPosition() const { - CameraMode mode = Application::getInstance()->getCamera()->getMode(); + CameraMode mode = qApp->getCamera()->getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. @@ -1339,13 +1340,15 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl // This is drawing the lookat vectors from our avatar to wherever we're looking. if (qApp->isHMDMode()) { - glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); - glm::mat4 leftEyePose = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Left); + glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(); + glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left); + leftEyePose = leftEyePose * headPose; glm::vec3 leftEyePosition = glm::vec3(leftEyePose[3]); - glm::mat4 rightEyePose = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Right); + glm::mat4 rightEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Right); + rightEyePose = rightEyePose * headPose; glm::vec3 rightEyePosition = glm::vec3(rightEyePose[3]); - glm::mat4 headPose = Application::getInstance()->getActiveDisplayPlugin()->getHeadPose(); glm::vec3 headPosition = glm::vec3(headPose[3]); getHead()->renderLookAts(renderArgs, @@ -1421,7 +1424,7 @@ void MyAvatar::destroyAnimGraph() { void MyAvatar::preRender(RenderArgs* renderArgs) { - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); const bool shouldDrawHead = shouldRenderHead(renderArgs); if (_skeletonModel.initWhenReady(scene)) { @@ -1476,13 +1479,13 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; bool MyAvatar::cameraInsideHead() const { const Head* head = getHead(); - const glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * _scale); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { return ((renderArgs->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) || - (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || + (qApp->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || !cameraInsideHead()); } @@ -1526,7 +1529,7 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::vec3 euler = glm::eulerAngles(localOrientation) * DEGREES_PER_RADIAN; //Invert yaw and roll when in mirror mode - if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_MIRROR) { + if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) { YAW(euler) *= -1.0f; ROLL(euler) *= -1.0f; } @@ -1951,7 +1954,7 @@ glm::vec3 MyAvatar::getPositionForAudio() { case AudioListenerMode::FROM_HEAD: return getHead()->getPosition(); case AudioListenerMode::FROM_CAMERA: - return Application::getInstance()->getCamera()->getPosition(); + return qApp->getCamera()->getPosition(); case AudioListenerMode::CUSTOM: return _customListenPosition; } @@ -1963,7 +1966,7 @@ glm::quat MyAvatar::getOrientationForAudio() { case AudioListenerMode::FROM_HEAD: return getHead()->getFinalOrientationInWorldFrame(); case AudioListenerMode::FROM_CAMERA: - return Application::getInstance()->getCamera()->getOrientation(); + return qApp->getCamera()->getOrientation(); case AudioListenerMode::CUSTOM: return _customListenOrientation; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index a892447961..312b7cbf44 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -135,7 +135,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.isInHMD = true; // get HMD position from sensor space into world space, and back into model space - AnimPose avatarToWorld(glm::vec3(1), myAvatar->getOrientation(), myAvatar->getPosition()); + AnimPose avatarToWorld(glm::vec3(1.0f), myAvatar->getOrientation(), myAvatar->getPosition()); glm::mat4 worldToAvatar = glm::inverse((glm::mat4)avatarToWorld); glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); glm::mat4 hmdMat = worldToAvatar * worldHMDMat; diff --git a/interface/src/devices/3DConnexionClient.cpp b/interface/src/devices/3DConnexionClient.cpp index d49fafa3e0..722fedcc3a 100755 --- a/interface/src/devices/3DConnexionClient.cpp +++ b/interface/src/devices/3DConnexionClient.cpp @@ -10,6 +10,7 @@ // #include "3DConnexionClient.h" +#include "Menu.h" #include "UserActivityLogger.h" const float MAX_AXIS = 75.0f; // max forward = 2x speed diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index dd5dc7cb08..cdf8e1e2a1 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -16,8 +16,6 @@ #include #include "InterfaceLogging.h" -#include "Application.h" - #ifndef HAVE_3DCONNEXIONCLIENT class ConnexionClient : public QObject { diff --git a/interface/src/devices/RealSense.cpp b/interface/src/devices/RealSense.cpp index d9b1486f09..ce28e40f2b 100644 --- a/interface/src/devices/RealSense.cpp +++ b/interface/src/devices/RealSense.cpp @@ -250,7 +250,7 @@ void RealSense::update() { void RealSense::loadRSSDKFile() { QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation)); - QString fileNameString = QFileDialog::getOpenFileName(Application::getInstance()->getWindow(), tr("Open RSSDK clip"), + QString fileNameString = QFileDialog::getOpenFileName(qApp->getWindow(), tr("Open RSSDK clip"), locationDir, tr("RSSDK Recordings (*.rssdk)")); if (!fileNameString.isEmpty()) { diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 4a92e7e8ac..2fa0267dc6 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -33,11 +33,10 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share const int WAY_BEHIND = 300; - if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) { + if (packetsToProcessCount() > WAY_BEHIND && qApp->getLogger()->extraDebugging()) { qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount()); } - Application* app = Application::getInstance(); bool wasStatsPacket = false; PacketType octreePacketType = packet->getType(); @@ -46,7 +45,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share // immediately following them inside the same packet. So, we process the PacketType_OCTREE_STATS first // then process any remaining bytes as if it was another packet if (octreePacketType == PacketType::OctreeStats) { - int statsMessageLength = app->processOctreeStats(*packet, sendingNode); + int statsMessageLength = qApp->processOctreeStats(*packet, sendingNode); wasStatsPacket = true; int piggybackBytes = packet->getPayloadSize() - statsMessageLength; @@ -84,7 +83,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share return; // bail since piggyback version doesn't match } - app->trackIncomingOctreePacket(*packet, sendingNode, wasStatsPacket); + qApp->trackIncomingOctreePacket(*packet, sendingNode, wasStatsPacket); // seek back to beginning of packet after tracking packet->seek(0); @@ -92,13 +91,13 @@ void OctreePacketProcessor::processPacket(QSharedPointer packet, Share switch(packetType) { case PacketType::EntityErase: { if (DependencyManager::get()->shouldRenderEntities()) { - app->_entities.processEraseMessage(*packet, sendingNode); + qApp->getEntities()->processEraseMessage(*packet, sendingNode); } } break; case PacketType::EntityData: { if (DependencyManager::get()->shouldRenderEntities()) { - app->_entities.processDatagram(*packet, sendingNode); + qApp->getEntities()->processDatagram(*packet, sendingNode); } } break; diff --git a/interface/src/scripting/ClipboardScriptingInterface.cpp b/interface/src/scripting/ClipboardScriptingInterface.cpp index 4cebb85648..b0ef6c760d 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.cpp +++ b/interface/src/scripting/ClipboardScriptingInterface.cpp @@ -15,12 +15,12 @@ ClipboardScriptingInterface::ClipboardScriptingInterface() { } float ClipboardScriptingInterface::getClipboardContentsLargestDimension() { - return Application::getInstance()->getEntityClipboard()->getContentsLargestDimension(); + return qApp->getEntityClipboard()->getContentsLargestDimension(); } bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector& entityIDs) { bool retVal; - QMetaObject::invokeMethod(Application::getInstance(), "exportEntities", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, retVal), Q_ARG(const QString&, filename), Q_ARG(const QVector&, entityIDs)); @@ -29,7 +29,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) { bool retVal; - QMetaObject::invokeMethod(Application::getInstance(), "exportEntities", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, retVal), Q_ARG(const QString&, filename), Q_ARG(float, x), @@ -41,7 +41,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float bool ClipboardScriptingInterface::importEntities(const QString& filename) { bool retVal; - QMetaObject::invokeMethod(Application::getInstance(), "importEntities", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(qApp, "importEntities", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, retVal), Q_ARG(const QString&, filename)); return retVal; @@ -49,7 +49,7 @@ bool ClipboardScriptingInterface::importEntities(const QString& filename) { QVector ClipboardScriptingInterface::pasteEntities(glm::vec3 position) { QVector retVal; - QMetaObject::invokeMethod(Application::getInstance(), "pasteEntities", Qt::BlockingQueuedConnection, + QMetaObject::invokeMethod(qApp, "pasteEntities", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVector, retVal), Q_ARG(float, position.x), Q_ARG(float, position.y), diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index b182d3b339..6f36a6267f 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -378,7 +378,7 @@ void ControllerScriptingInterface::releaseJoystick(int joystickIndex) { } glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { - return Application::getInstance()->getUiSize(); + return qApp->getUiSize(); } AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index 746cdaf36f..843a40348e 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -18,10 +18,10 @@ #include "MainWindow.h" int DesktopScriptingInterface::getWidth() { - QSize size = Application::getInstance()->getWindow()->windowHandle()->screen()->virtualSize(); + QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize(); return size.width(); } int DesktopScriptingInterface::getHeight() { - QSize size = Application::getInstance()->getWindow()->windowHandle()->screen()->virtualSize(); + QSize size = qApp->getWindow()->windowHandle()->screen()->virtualSize(); return size.height(); } diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/GlobalServicesScriptingInterface.cpp index 668bd92664..8ca381a607 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/GlobalServicesScriptingInterface.cpp @@ -143,5 +143,5 @@ void GlobalServicesScriptingInterface::updateDownloadInfo() { } void GlobalServicesScriptingInterface::editFriends() { - QMetaObject::invokeMethod(Application::getInstance(), "showFriendsWindow"); + QMetaObject::invokeMethod(qApp, "showFriendsWindow"); } diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 68ac511eaf..3d39bdd143 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -10,50 +10,56 @@ // #include "HMDScriptingInterface.h" + +#include + #include "display-plugins/DisplayPlugin.h" #include +#include "Application.h" -HMDScriptingInterface& HMDScriptingInterface::getInstance() { - static HMDScriptingInterface sharedInstance; - return sharedInstance; -} - -bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { - Camera* camera = Application::getInstance()->getCamera(); - glm::vec3 position = camera->getPosition(); - glm::quat orientation = camera->getOrientation(); - - glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); - - const auto& compositor = Application::getInstance()->getApplicationCompositor(); - - return compositor.calculateRayUICollisionPoint(position, direction, result); +HMDScriptingInterface::HMDScriptingInterface() { } QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { - glm::vec3 hudIntersection; - - if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(hudIntersection)) { + auto instance = DependencyManager::get(); + if (instance->getHUDLookAtPosition3D(hudIntersection)) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); glm::vec3 sphereCenter = myAvatar->getDefaultEyePosition(); glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); glm::vec3 eulers = ::safeEulerAngles(rotation); - return qScriptValueFromValue(engine, Application::getInstance()->getApplicationCompositor() - .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); + return qScriptValueFromValue(engine, qApp->getApplicationCompositor() + .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); } return QScriptValue::NullValue; } QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine) { glm::vec3 result; - if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(result)) { + auto instance = DependencyManager::get(); + if (instance->getHUDLookAtPosition3D(result)) { return qScriptValueFromValue(engine, result); } return QScriptValue::NullValue; } -float HMDScriptingInterface::getIPD() const { - return Application::getInstance()->getActiveDisplayPlugin()->getIPD(); +void HMDScriptingInterface::toggleMagnifier() { + qApp->getApplicationCompositor().toggleMagnifier(); +} + +bool HMDScriptingInterface::getMagnifier() const { + return qApp->getApplicationCompositor().hasMagnifier(); +} + +bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { + Camera* camera = qApp->getCamera(); + glm::vec3 position = camera->getPosition(); + glm::quat orientation = camera->getOrientation(); + + glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); + + const auto& compositor = qApp->getApplicationCompositor(); + + return compositor.calculateRayUICollisionPoint(position, direction, result); } diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 82b444abaa..c097cde5e3 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -12,32 +12,29 @@ #ifndef hifi_HMDScriptingInterface_h #define hifi_HMDScriptingInterface_h +#include +class QScriptContext; +class QScriptEngine; + #include +#include +#include -#include "Application.h" -class HMDScriptingInterface : public QObject { +class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT Q_PROPERTY(bool magnifier READ getMagnifier) - Q_PROPERTY(bool active READ isHMDMode) - Q_PROPERTY(float ipd READ getIPD) public: - static HMDScriptingInterface& getInstance(); - + HMDScriptingInterface(); static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); public slots: - void toggleMagnifier() { Application::getInstance()->getApplicationCompositor().toggleMagnifier(); }; + void toggleMagnifier(); private: - HMDScriptingInterface() {}; - bool getMagnifier() const { return Application::getInstance()->getApplicationCompositor().hasMagnifier(); }; - bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } - float getIPD() const; - + bool getMagnifier() const; bool getHUDLookAtPosition3D(glm::vec3& result) const; - }; #endif // hifi_HMDScriptingInterface_h diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 277c611f04..ff7784b9ae 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Application.h" - #include "MenuScriptingInterface.h" +#include "Menu.h" +#include MenuScriptingInterface* MenuScriptingInterface::getInstance() { static MenuScriptingInterface sharedInstance; diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h index fda8207780..5c01318a38 100644 --- a/interface/src/scripting/MenuScriptingInterface.h +++ b/interface/src/scripting/MenuScriptingInterface.h @@ -12,13 +12,10 @@ #ifndef hifi_MenuScriptingInterface_h #define hifi_MenuScriptingInterface_h -#include -#include #include #include -#include "Menu.h" -#include +class MenuItemProperties; class MenuScriptingInterface : public QObject { Q_OBJECT diff --git a/interface/src/scripting/SettingsScriptingInterface.cpp b/interface/src/scripting/SettingsScriptingInterface.cpp index a5994779e2..2f14c33dc7 100644 --- a/interface/src/scripting/SettingsScriptingInterface.cpp +++ b/interface/src/scripting/SettingsScriptingInterface.cpp @@ -9,10 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #include "SettingsScriptingInterface.h" +#include SettingsScriptingInterface* SettingsScriptingInterface::getInstance() { static SettingsScriptingInterface sharedInstance; diff --git a/interface/src/scripting/SettingsScriptingInterface.h b/interface/src/scripting/SettingsScriptingInterface.h index e3138be4a0..2fe55eaea0 100644 --- a/interface/src/scripting/SettingsScriptingInterface.h +++ b/interface/src/scripting/SettingsScriptingInterface.h @@ -12,12 +12,9 @@ #ifndef hifi_SettingsScriptingInterface_h #define hifi_SettingsScriptingInterface_h -#include #include #include -#include "Application.h" - class SettingsScriptingInterface : public QObject { Q_OBJECT SettingsScriptingInterface() { }; diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index a549410305..f9dc9e07ae 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -41,7 +41,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid _isToolWindow(isToolWindow) { if (_isToolWindow) { - ToolWindow* toolWindow = Application::getInstance()->getToolWindow(); + ToolWindow* toolWindow = qApp->getToolWindow(); auto dockWidget = new QDockWidget(title, toolWindow); dockWidget->setFeatures(QDockWidget::DockWidgetMovable); @@ -56,7 +56,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid _windowWidget = dockWidget; } else { - auto dialogWidget = new QDialog(Application::getInstance()->getWindow(), Qt::Window); + auto dialogWidget = new QDialog(qApp->getWindow(), Qt::Window); dialogWidget->setWindowTitle(title); dialogWidget->resize(width, height); dialogWidget->installEventFilter(this); @@ -120,7 +120,7 @@ void WebWindowClass::setVisible(bool visible) { if (visible) { if (_isToolWindow) { QMetaObject::invokeMethod( - Application::getInstance()->getToolWindow(), "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); + qApp->getToolWindow(), "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); } else { QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection); QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 6be67a7261..a2886c8b77 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -32,8 +32,8 @@ WindowScriptingInterface::WindowScriptingInterface() : { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); - connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested); - connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); + connect(qApp, &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested); + connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); } WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow) { @@ -41,13 +41,13 @@ WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title } QScriptValue WindowScriptingInterface::hasFocus() { - return Application::getInstance()->hasFocus(); + return qApp->hasFocus(); } void WindowScriptingInterface::setFocus() { // It's forbidden to call focus() from another thread. - Application::getInstance()->postLambdaEvent([] { - auto window = Application::getInstance()->getWindow(); + qApp->postLambdaEvent([] { + auto window = qApp->getWindow(); window->activateWindow(); window->setFocus(); }); @@ -55,20 +55,11 @@ void WindowScriptingInterface::setFocus() { void WindowScriptingInterface::raiseMainWindow() { // It's forbidden to call raise() from another thread. - Application::getInstance()->postLambdaEvent([] { - Application::getInstance()->getWindow()->raise(); + qApp->postLambdaEvent([] { + qApp->getWindow()->raise(); }); } -void WindowScriptingInterface::setCursorVisible(bool visible) { - QMetaObject::invokeMethod(Application::getInstance(), "setCursorVisible", Qt::BlockingQueuedConnection, - Q_ARG(bool, visible)); -} - -bool WindowScriptingInterface::isCursorVisible() const { - return !Application::getInstance()->isMouseHidden(); -} - void WindowScriptingInterface::setCursorPosition(int x, int y) { QCursor::setPos(x, y); } @@ -167,7 +158,7 @@ QScriptValue WindowScriptingInterface::peekNonBlockingFormResult(QScriptValue fo /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue QScriptValue WindowScriptingInterface::showAlert(const QString& message) { - QMessageBox::warning(Application::getInstance()->getWindow(), "", message); + QMessageBox::warning(qApp->getWindow(), "", message); return QScriptValue::UndefinedValue; } @@ -175,7 +166,7 @@ QScriptValue WindowScriptingInterface::showAlert(const QString& message) { /// \param const QString& message message to display /// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise QScriptValue WindowScriptingInterface::showConfirm(const QString& message) { - QMessageBox::StandardButton response = QMessageBox::question(Application::getInstance()->getWindow(), "", message); + QMessageBox::StandardButton response = QMessageBox::question(qApp->getWindow(), "", message); return QScriptValue(response == QMessageBox::Yes); } @@ -487,7 +478,7 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue form) { - QDialog* editDialog = new QDialog(Application::getInstance()->getWindow()); + QDialog* editDialog = new QDialog(qApp->getWindow()); editDialog->setWindowTitle(title); bool cancelButton = false; @@ -597,7 +588,7 @@ QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue /// \param const QString& defaultText default text in the text box /// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) { - QInputDialog promptDialog(Application::getInstance()->getWindow()); + QInputDialog promptDialog(qApp->getWindow()); promptDialog.setWindowTitle(""); promptDialog.setLabelText(message); promptDialog.setTextValue(defaultText); @@ -627,7 +618,7 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS path = fileInfo.filePath(); } - QFileDialog fileDialog(Application::getInstance()->getWindow(), title, path, nameFilter); + QFileDialog fileDialog(qApp->getWindow(), title, path, nameFilter); fileDialog.setAcceptMode(acceptMode); QUrl fileUrl(directory); if (acceptMode == QFileDialog::AcceptSave) { @@ -657,17 +648,17 @@ QScriptValue WindowScriptingInterface::showS3Browse(const QString& nameFilter) { } int WindowScriptingInterface::getInnerWidth() { - return Application::getInstance()->getWindow()->geometry().width(); + return qApp->getWindow()->geometry().width(); } int WindowScriptingInterface::getInnerHeight() { - return Application::getInstance()->getWindow()->geometry().height(); + return qApp->getWindow()->geometry().height(); } int WindowScriptingInterface::getX() { - return Application::getInstance()->getWindow()->x(); + return qApp->getWindow()->x(); } int WindowScriptingInterface::getY() { - return Application::getInstance()->getWindow()->y(); + return qApp->getWindow()->y(); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 9bc8a834bd..1395639cd0 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -27,20 +27,17 @@ class WindowScriptingInterface : public QObject, public Dependency { Q_PROPERTY(int innerHeight READ getInnerHeight) Q_PROPERTY(int x READ getX) Q_PROPERTY(int y READ getY) - Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible) public: WindowScriptingInterface(); int getInnerWidth(); int getInnerHeight(); int getX(); int getY(); - bool isCursorVisible() const; public slots: QScriptValue getCursorPositionX(); QScriptValue getCursorPositionY(); void setCursorPosition(int x, int y); - void setCursorVisible(bool visible); QScriptValue hasFocus(); void setFocus(); void raiseMainWindow(); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 98c2efc8f3..a2d98eea66 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -285,7 +286,10 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int mat4 camMat; _cameraBaseTransform.getMatrix(camMat); - camMat = camMat * qApp->getEyePose(eye); + auto displayPlugin = qApp->getActiveDisplayPlugin(); + auto headPose = displayPlugin->getHeadPose(); + auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye); + camMat = (headPose * eyeToHead) * camMat; batch.setViewportTransform(renderArgs->_viewport); batch.setViewTransform(camMat); @@ -346,31 +350,28 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { - cursorPos *= qApp->getCanvasSize(); - const glm::vec2 projection = screenToSpherical(cursorPos); + const glm::vec2 projection = overlayToSpherical(cursorPos); // The overlay space orientation of the mouse coordinates - const glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - // FIXME We now have the direction of the ray FROM THE DEFAULT HEAD POSE. - // Now we need to account for the actual camera position relative to the overlay - glm::vec3 overlaySpaceDirection = glm::normalize(orientation * IDENTITY_FRONT); + const glm::quat cursorOrientation(glm::vec3(-projection.y, projection.x, 0.0f)); + // The orientation and position of the HEAD, not the overlay + glm::vec3 worldSpaceHeadPosition = qApp->getCamera()->getPosition(); + glm::quat worldSpaceOrientation = qApp->getCamera()->getOrientation(); - // We need the RAW camera orientation and position, because this is what the overlay is - // rendered relative to - glm::vec3 overlayPosition = qApp->getCamera()->getPosition(); - glm::quat overlayOrientation = qApp->getCamera()->getRotation(); + auto headPose = qApp->getHMDSensorPose(); + auto headOrientation = glm::quat_cast(headPose); + auto headTranslation = extractTranslation(headPose); + auto overlayOrientation = worldSpaceOrientation * glm::inverse(headOrientation); + auto overlayPosition = worldSpaceHeadPosition - (overlayOrientation * headTranslation); if (Menu::getInstance()->isOptionChecked(MenuOption::StandingHMDSensorMode)) { overlayPosition = _modelTransform.getTranslation(); overlayOrientation = _modelTransform.getRotation(); } - // Intersection UI overlay space - glm::vec3 worldSpaceDirection = overlayOrientation * overlaySpaceDirection; - glm::vec3 worldSpaceIntersection = (glm::normalize(worldSpaceDirection) * _oculusUIRadius) + overlayPosition; - glm::vec3 worldSpaceHeadPosition = (overlayOrientation * extractTranslation(qApp->getHMDSensorPose())) + overlayPosition; - // Intersection in world space + glm::vec3 worldSpaceIntersection = ((overlayOrientation * (cursorOrientation * Vectors::FRONT)) * _oculusUIRadius) + overlayPosition; + origin = worldSpaceHeadPosition; direction = glm::normalize(worldSpaceIntersection - worldSpaceHeadPosition); } @@ -425,11 +426,11 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi //Renders optional pointers void ApplicationCompositor::renderPointers(gpu::Batch& batch) { - if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { + if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated()) { //If we are in oculus, render reticle later auto trueMouse = qApp->getTrueMouse(); trueMouse /= qApp->getCanvasSize(); - QPoint position = QPoint(qApp->getTrueMouseX(), qApp->getTrueMouseY()); + QPoint position = QPoint(qApp->getTrueMouse().x, qApp->getTrueMouse().y); _reticlePosition[MOUSE] = position; _reticleActive[MOUSE] = true; _magActive[MOUSE] = _magnifier; @@ -682,7 +683,6 @@ glm::vec2 ApplicationCompositor::screenToSpherical(const glm::vec2& screenPos) { result.y = (screenPos.y / screenSize.y - 0.5f); result.x *= MOUSE_YAW_RANGE; result.y *= MOUSE_PITCH_RANGE; - return result; } @@ -701,13 +701,13 @@ glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalP result /= _textureFov; result.x /= _textureAspectRatio; result += 0.5f; - result *= qApp->getCanvasSize(); + result *= qApp->getUiSize(); return result; } glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const { glm::vec2 result = overlayPos; - result /= qApp->getCanvasSize(); + result /= qApp->getUiSize(); result -= 0.5f; result *= _textureFov; result.x *= _textureAspectRatio; diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 019deb9690..a9827a23c2 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -7,13 +7,14 @@ // -#include "Application.h" #include "AvatarInputs.h" +#include #include -#include "Menu.h" +#include "Application.h" #include "devices/FaceTracker.h" +#include "Menu.h" HIFI_QML_DEF(AvatarInputs) @@ -106,7 +107,7 @@ void AvatarInputs::update() { } void AvatarInputs::toggleCameraMute() { - FaceTracker* faceTracker = Application::getInstance()->getSelectedFaceTracker(); + FaceTracker* faceTracker = qApp->getSelectedFaceTracker(); if (faceTracker) { faceTracker->toggleMute(); } diff --git a/interface/src/ui/ChatMessageArea.cpp b/interface/src/ui/ChatMessageArea.cpp index 1dc38e9c94..bef22b9fed 100644 --- a/interface/src/ui/ChatMessageArea.cpp +++ b/interface/src/ui/ChatMessageArea.cpp @@ -20,7 +20,7 @@ ChatMessageArea::ChatMessageArea(bool useFixedHeight) : QTextBrowser(), _useFixe connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &ChatMessageArea::updateLayout); - connect(this, &QTextBrowser::anchorClicked, Application::getInstance(), &Application::openUrl); + connect(this, &QTextBrowser::anchorClicked, qApp, &Application::openUrl); } void ChatMessageArea::setHtml(const QString& html) { diff --git a/interface/src/ui/DataWebDialog.cpp b/interface/src/ui/DataWebDialog.cpp index f405ef50c5..a92e0ac214 100644 --- a/interface/src/ui/DataWebDialog.cpp +++ b/interface/src/ui/DataWebDialog.cpp @@ -28,7 +28,7 @@ DataWebDialog::DataWebDialog() { setPage(new DataWebPage(this)); // have the Application handle external links - connect(this, &QWebView::linkClicked, Application::getInstance(), &Application::openUrl); + connect(this, &QWebView::linkClicked, qApp, &Application::openUrl); } DataWebDialog* DataWebDialog::dialogForPath(const QString& path, diff --git a/interface/src/ui/DataWebPage.cpp b/interface/src/ui/DataWebPage.cpp index 69c9954245..01feacc393 100644 --- a/interface/src/ui/DataWebPage.cpp +++ b/interface/src/ui/DataWebPage.cpp @@ -34,15 +34,15 @@ void DataWebPage::javaScriptConsoleMessage(const QString& message, int lineNumbe bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) { // Handle hifi:// links and links to files with particular extensions QString urlString = request.url().toString(); - if (Application::getInstance()->canAcceptURL(urlString)) { - if (Application::getInstance()->acceptURL(urlString)) { + if (qApp->canAcceptURL(urlString)) { + if (qApp->acceptURL(urlString)) { return false; // we handled it, so QWebPage doesn't need to handle it } } // Make hyperlinks with target="_blank" open in user's Web browser if (type == QWebPage::NavigationTypeLinkClicked && frame == nullptr) { - Application::getInstance()->openUrl(request.url()); + qApp->openUrl(request.url()); return false; // We handled it. } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 308cfc9e8c..00bc95b5fa 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -32,6 +33,19 @@ #include "ScriptEditorWindow.h" #include "UpdateDialog.h" +template +void DialogsManager::maybeCreateDialog(QPointer& member) { + if (!member) { + MainWindow* parent = qApp->getWindow(); + Q_CHECK_PTR(parent); + member = new T(parent); + Q_CHECK_PTR(member); + + if (_hmdToolsDialog && member->windowHandle()) { + _hmdToolsDialog->watchWindow(member->windowHandle()); + } + } +} void DialogsManager::toggleAddressBar() { AddressBarDialog::toggle(); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index d54e91b9d6..133fe459d0 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -14,13 +14,10 @@ #include -#include #include #include "HMDToolsDialog.h" -class QAction; - class AnimationsDialog; class AttachmentsDialog; class AudioStatsDialog; @@ -78,18 +75,7 @@ private: DialogsManager() {} template - void maybeCreateDialog(QPointer& member) { - if (!member) { - MainWindow* parent = qApp->getWindow(); - Q_CHECK_PTR(parent); - member = new T(parent); - Q_CHECK_PTR(member); - - if (_hmdToolsDialog && member->windowHandle()) { - _hmdToolsDialog->watchWindow(member->windowHandle()); - } - } - } + void maybeCreateDialog(QPointer& member); QPointer _animationsDialog; QPointer _attachmentsDialog; diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index e03e05912c..c1ea541864 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -9,11 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include #include -#include - -#include +#include #include #include #include @@ -22,6 +22,7 @@ #include #include +#include "Application.h" #include "MainWindow.h" #include "Menu.h" #include "ui/DialogsManager.h" @@ -78,11 +79,11 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : // what screens we're allowed on watchWindow(windowHandle()); auto dialogsManager = DependencyManager::get(); - if (Application::getInstance()->getRunningScriptsWidget()) { - watchWindow(Application::getInstance()->getRunningScriptsWidget()->windowHandle()); + if (qApp->getRunningScriptsWidget()) { + watchWindow(qApp->getRunningScriptsWidget()->windowHandle()); } - if (Application::getInstance()->getToolWindow()) { - watchWindow(Application::getInstance()->getToolWindow()->windowHandle()); + if (qApp->getToolWindow()) { + watchWindow(qApp->getToolWindow()->windowHandle()); } if (dialogsManager->getBandwidthDialog()) { watchWindow(dialogsManager->getBandwidthDialog()->windowHandle()); @@ -110,7 +111,7 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : }); // watch for our application window moving screens. If it does we want to update our screen details - QWindow* mainWindow = Application::getInstance()->getWindow()->windowHandle(); + QWindow* mainWindow = qApp->getWindow()->windowHandle(); connect(mainWindow, &QWindow::screenChanged, [this]{ updateUi(); }); @@ -142,7 +143,7 @@ QString HMDToolsDialog::getDebugDetails() const { results += "Desktop's Primary Screen: " + desktopPrimaryScreen->name() + "\n"; results += "Application Primary Screen: " + QGuiApplication::primaryScreen()->name() + "\n"; - QScreen* mainWindowScreen = Application::getInstance()->getWindow()->windowHandle()->screen(); + QScreen* mainWindowScreen = qApp->getWindow()->windowHandle()->screen(); results += "Application Main Window Screen: " + mainWindowScreen->name() + "\n"; results += "Total Screens: " + QString::number(QApplication::desktop()->screenCount()) + "\n"; @@ -159,15 +160,15 @@ void HMDToolsDialog::toggleHMDMode() { void HMDToolsDialog::enterHMDMode() { if (!qApp->isHMDMode()) { - Application::getInstance()->setActiveDisplayPlugin(_hmdPluginName); - Application::getInstance()->getWindow()->activateWindow(); + qApp->setActiveDisplayPlugin(_hmdPluginName); + qApp->getWindow()->activateWindow(); } } void HMDToolsDialog::leaveHMDMode() { if (qApp->isHMDMode()) { - Application::getInstance()->setActiveDisplayPlugin(_defaultPluginName); - Application::getInstance()->getWindow()->activateWindow(); + qApp->setActiveDisplayPlugin(_defaultPluginName); + qApp->getWindow()->activateWindow(); } } @@ -200,7 +201,7 @@ void HMDToolsDialog::showEvent(QShowEvent* event) { void HMDToolsDialog::hideEvent(QHideEvent* event) { // center the cursor on the main application window - centerCursorOnWidget(Application::getInstance()->getWindow()); + centerCursorOnWidget(qApp->getWindow()); } void HMDToolsDialog::screenCountChanged(int newCount) { @@ -275,7 +276,7 @@ void HMDWindowWatcher::windowScreenChanged(QScreen* screen) { QScreen* betterScreen = NULL; QScreen* lastApplicationScreen = _hmdTools->getLastApplicationScreen(); - QWindow* appWindow = Application::getInstance()->getWindow()->windowHandle(); + QWindow* appWindow = qApp->getWindow()->windowHandle(); QScreen* appScreen = appWindow->screen(); if (_previousScreen && _previousScreen != hmdScreen) { diff --git a/interface/src/ui/HMDToolsDialog.h b/interface/src/ui/HMDToolsDialog.h index 11cab91673..7d30fc17f6 100644 --- a/interface/src/ui/HMDToolsDialog.h +++ b/interface/src/ui/HMDToolsDialog.h @@ -15,6 +15,7 @@ #include class HMDWindowWatcher; +class QLabel; class HMDToolsDialog : public QDialog { Q_OBJECT diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 74585df855..6e5b22399d 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -53,7 +53,7 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : if (_scriptEngine == NULL) { - _scriptEngine = Application::getInstance()->loadScript(QString(), false); + _scriptEngine = qApp->loadScript(QString(), false); } connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)), diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp index b9c640054c..11286e0ee1 100644 --- a/interface/src/ui/MarketplaceDialog.cpp +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -20,8 +20,8 @@ MarketplaceDialog::MarketplaceDialog(QQuickItem* parent) : OffscreenQmlDialog(pa bool MarketplaceDialog::navigationRequested(const QString& url) { qDebug() << url; - if (Application::getInstance()->canAcceptURL(url)) { - if (Application::getInstance()->acceptURL(url)) { + if (qApp->canAcceptURL(url)) { + if (qApp->acceptURL(url)) { return false; // we handled it, so QWebPage doesn't need to handle it } } diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index c2d72de9d6..d3ff017633 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -129,7 +129,7 @@ OctreeStatsDialog::~OctreeStatsDialog() { void OctreeStatsDialog::paintEvent(QPaintEvent* event) { // Processed Entities Related stats - auto entities = Application::getInstance()->getEntities(); + auto entities = qApp->getEntities(); auto entitiesTree = entities->getTree(); // Do this ever paint event... even if we don't update @@ -196,7 +196,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { unsigned long totalInternal = 0; unsigned long totalLeaves = 0; - NodeToOctreeSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); + NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); sceneStats->withReadLock([&] { for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) { //const QUuid& uuid = i->first; @@ -264,7 +264,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { QString averageReadBitstreamPerPacketString = locale.toString(averageReadBitstreamPerPacket); label = _labels[_processedPackets]; - const OctreePacketProcessor& entitiesPacketProcessor = Application::getInstance()->getOctreePacketProcessor(); + const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor(); auto incomingPPS = entitiesPacketProcessor.getIncomingPPS(); auto processedPPS = entitiesPacketProcessor.getProcessedPPS(); @@ -351,7 +351,7 @@ void OctreeStatsDialog::showAllOctreeServers() { int serverCount = 0; showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity", - Application::getInstance()->getEntityServerJurisdictions()); + qApp->getEntityServerJurisdictions()); if (_octreeServerLabelsCount > serverCount) { for (int i = serverCount; i < _octreeServerLabelsCount; i++) { @@ -427,7 +427,7 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser // now lookup stats details for this server... if (_extraServerDetails[serverCount-1] != LESS) { - NodeToOctreeSceneStats* sceneStats = Application::getInstance()->getOcteeSceneStats(); + NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats(); sceneStats->withReadLock([&] { if (sceneStats->find(nodeUUID) != sceneStats->end()) { OctreeSceneStats& stats = sceneStats->at(nodeUUID); diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp index 6e4d7e8248..f777e5d4dc 100644 --- a/interface/src/ui/OverlayConductor.cpp +++ b/interface/src/ui/OverlayConductor.cpp @@ -8,10 +8,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Application.h" -#include "InterfaceLogging.h" -#include "avatar/AvatarManager.h" +#include +#include "Application.h" +#include "avatar/AvatarManager.h" +#include "InterfaceLogging.h" #include "OverlayConductor.h" OverlayConductor::OverlayConductor() { diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 81e4d61b5e..f90bac234d 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -45,14 +45,14 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser); connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser); - connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, Application::getInstance(), &Application::loadDefaultScripts); + connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, qApp, &Application::loadDefaultScripts); connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser); connect(ui.appearanceDescription, &QLineEdit::textChanged, this, [this](const QString& url) { DependencyManager::get()->getMyAvatar()->useFullAvatarURL(url, ""); this->fullAvatarURLChanged(url, ""); }); - connect(Application::getInstance(), &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged); + connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged); // move dialog to left side move(parentWidget()->geometry().topLeft()); @@ -183,7 +183,7 @@ void PreferencesDialog::loadPreferences() { ui.outputStarveDetectionThresholdSpinner->setValue(audio->getOutputStarveDetectionThreshold()); ui.outputStarveDetectionPeriodSpinner->setValue(audio->getOutputStarveDetectionPeriod()); - ui.realWorldFieldOfViewSpin->setValue(DependencyManager::get()->getMyAvatar()->getRealWorldFieldOfView()); + ui.realWorldFieldOfViewSpin->setValue(myAvatar->getRealWorldFieldOfView()); ui.fieldOfViewSpin->setValue(qApp->getFieldOfView()); @@ -258,7 +258,7 @@ void PreferencesDialog::savePreferences() { } } - DependencyManager::get()->getMyAvatar()->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); + myAvatar->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); qApp->setFieldOfView(ui.fieldOfViewSpin->value()); @@ -298,7 +298,7 @@ void PreferencesDialog::savePreferences() { audio->setOutputStarveDetectionThreshold(ui.outputStarveDetectionThresholdSpinner->value()); audio->setOutputStarveDetectionPeriod(ui.outputStarveDetectionPeriodSpinner->value()); - Application::getInstance()->resizeGL(); + qApp->resizeGL(); // LOD items auto lodManager = DependencyManager::get(); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 61b03bd610..3752ea2176 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -57,15 +57,15 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter); connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList); - connect(ui->reloadAllButton, &QPushButton::clicked, Application::getInstance(), &Application::reloadAllScripts); + connect(ui->reloadAllButton, &QPushButton::clicked, qApp, &Application::reloadAllScripts); connect(ui->stopAllButton, &QPushButton::clicked, this, &RunningScriptsWidget::allScriptsStopped); - connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, Application::getInstance(), &Application::loadDialog); - connect(ui->loadScriptFromURLButton, &QPushButton::clicked, Application::getInstance(), &Application::loadScriptURLDialog); + connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, qApp, &Application::loadDialog); + connect(ui->loadScriptFromURLButton, &QPushButton::clicked, qApp, &Application::loadScriptURLDialog); connect(&_reloadSignalMapper, static_cast(&QSignalMapper::mapped), - Application::getInstance(), &Application::reloadOneScript); + qApp, &Application::reloadOneScript); connect(&_stopSignalMapper, static_cast(&QSignalMapper::mapped), - [](const QString& script) { Application::getInstance()->stopScript(script); }); + [](const QString& script) { qApp->stopScript(script); }); UIUtil::scaleWidgetFontSizes(this); } @@ -83,7 +83,7 @@ void RunningScriptsWidget::updateFileFilter(const QString& filter) { void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) { QVariant scriptFile = _scriptsModelFilter.data(index, ScriptsModel::ScriptPath); - Application::getInstance()->loadScript(scriptFile.toString()); + qApp->loadScript(scriptFile.toString()); } void RunningScriptsWidget::loadSelectedScript() { @@ -172,7 +172,7 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { ui->filterLineEdit->setFocus(); } - QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry(); + QRect parentGeometry = qApp->getDesirableApplicationGeometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); int topMargin = titleBarHeight; @@ -217,13 +217,13 @@ void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) { } void RunningScriptsWidget::allScriptsStopped() { - Application::getInstance()->stopAllScripts(); + qApp->stopAllScripts(); } QVariantList RunningScriptsWidget::getRunning() { const int WINDOWS_DRIVE_LETTER_SIZE = 1; QVariantList result; - foreach(const QString& runningScript, Application::getInstance()->getRunningScripts()) { + foreach(const QString& runningScript, qApp->getRunningScripts()) { QUrl runningScriptURL = QUrl(runningScript); if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded))); @@ -245,7 +245,7 @@ QVariantList RunningScriptsWidget::getPublic() { QVariantList RunningScriptsWidget::getPublicChildNodes(TreeNodeFolder* parent) { QVariantList result; - QList treeNodes = Application::getInstance()->getRunningScriptsWidget()->getScriptsModel() + QList treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel() ->getFolderNodes(parent); for (int i = 0; i < treeNodes.size(); i++) { TreeNodeBase* node = treeNodes.at(i); @@ -273,7 +273,7 @@ QVariantList RunningScriptsWidget::getPublicChildNodes(TreeNodeFolder* parent) { QVariantList RunningScriptsWidget::getLocal() { QVariantList result; - QList treeNodes = Application::getInstance()->getRunningScriptsWidget()->getScriptsModel() + QList treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel() ->getFolderNodes(NULL); for (int i = 0; i < treeNodes.size(); i++) { TreeNodeBase* node = treeNodes.at(i); @@ -293,14 +293,14 @@ QVariantList RunningScriptsWidget::getLocal() { } bool RunningScriptsWidget::stopScriptByName(const QString& name) { - foreach (const QString& runningScript, Application::getInstance()->getRunningScripts()) { + foreach (const QString& runningScript, qApp->getRunningScripts()) { if (QUrl(runningScript).fileName().toLower() == name.trimmed().toLower()) { - return Application::getInstance()->stopScript(runningScript, false); + return qApp->stopScript(runningScript, false); } } return false; } bool RunningScriptsWidget::stopScript(const QString& name, bool restart) { - return Application::getInstance()->stopScript(name, restart); + return qApp->stopScript(name, restart); } diff --git a/interface/src/ui/ScriptEditBox.cpp b/interface/src/ui/ScriptEditBox.cpp index acabaa3c8f..2aea225b17 100644 --- a/interface/src/ui/ScriptEditBox.cpp +++ b/interface/src/ui/ScriptEditBox.cpp @@ -10,8 +10,11 @@ // #include "ScriptEditBox.h" + +#include +#include + #include "ScriptLineNumberArea.h" -#include "Application.h" ScriptEditBox::ScriptEditBox(QWidget* parent) : QPlainTextEdit(parent) diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index fa829d4ace..76327804b6 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -98,7 +98,7 @@ bool ScriptEditorWidget::setRunning(bool run) { if (run) { const QString& scriptURLString = QUrl(_currentScript).toString(); // Reload script so that an out of date copy is not retrieved from the cache - _scriptEngine = Application::getInstance()->loadScript(scriptURLString, true, true, false, true); + _scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true); connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); @@ -106,7 +106,7 @@ bool ScriptEditorWidget::setRunning(bool run) { connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } else { connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - Application::getInstance()->stopScript(_currentScript); + qApp->stopScript(_currentScript); _scriptEngine = NULL; } return true; @@ -170,7 +170,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { } const QString& scriptURLString = QUrl(_currentScript).toString(); - _scriptEngine = Application::getInstance()->getScriptEngine(scriptURLString); + _scriptEngine = qApp->getScriptEngine(scriptURLString); if (_scriptEngine != NULL) { connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); @@ -186,10 +186,10 @@ bool ScriptEditorWidget::save() { bool ScriptEditorWidget::saveAs() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), - Application::getInstance()->getPreviousScriptLocation(), + qApp->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!fileName.isEmpty()) { - Application::getInstance()->setPreviousScriptLocation(fileName); + qApp->setPreviousScriptLocation(fileName); return saveFile(fileName); } else { return false; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index 41d172cf48..b5c8500083 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -90,10 +90,10 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { void ScriptEditorWindow::loadScriptClicked() { QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), - Application::getInstance()->getPreviousScriptLocation(), + qApp->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!scriptName.isEmpty()) { - Application::getInstance()->setPreviousScriptLocation(scriptName); + qApp->setPreviousScriptLocation(scriptName); addScriptEditorWidget("loading...")->loadFile(scriptName); updateButtons(); } @@ -101,7 +101,7 @@ void ScriptEditorWindow::loadScriptClicked() { void ScriptEditorWindow::loadMenuAboutToShow() { _loadMenu->clear(); - QStringList runningScripts = Application::getInstance()->getRunningScripts(); + QStringList runningScripts = qApp->getRunningScripts(); if (runningScripts.count() > 0) { QSignalMapper* signalMapper = new QSignalMapper(this); foreach (const QString& runningScript, runningScripts) { diff --git a/interface/src/ui/ScriptLineNumberArea.cpp b/interface/src/ui/ScriptLineNumberArea.cpp index 5bb08918b9..6d7e9185ea 100644 --- a/interface/src/ui/ScriptLineNumberArea.cpp +++ b/interface/src/ui/ScriptLineNumberArea.cpp @@ -11,7 +11,7 @@ #include "ScriptLineNumberArea.h" -#include "Application.h" +#include "ScriptEditBox.h" ScriptLineNumberArea::ScriptLineNumberArea(ScriptEditBox* scriptEditBox) : QWidget(scriptEditBox) diff --git a/interface/src/ui/ScriptLineNumberArea.h b/interface/src/ui/ScriptLineNumberArea.h index 47c540ca0e..00bd078170 100644 --- a/interface/src/ui/ScriptLineNumberArea.h +++ b/interface/src/ui/ScriptLineNumberArea.h @@ -13,7 +13,8 @@ #define hifi_ScriptLineNumberArea_h #include -#include "ScriptEditBox.h" + +class ScriptEditBox; class ScriptLineNumberArea : public QWidget { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 831664e3e6..5e73e62832 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -18,8 +18,10 @@ #include #include +#include #include #include +#include #include #include "BandwidthRecorder.h" @@ -115,7 +117,7 @@ void Stats::updateStats(bool force) { STAT_UPDATE(avatarCount, avatarManager->size() - 1); STAT_UPDATE(serverCount, nodeList->size()); STAT_UPDATE(framerate, (int)qApp->getFps()); - STAT_UPDATE(simrate, (int)Application::getInstance()->getAverageSimsPerSecond()); + STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond()); STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate()); auto bandwidthRecorder = DependencyManager::get(); @@ -207,7 +209,7 @@ void Stats::updateStats(bool force) { unsigned long totalLeaves = 0; std::stringstream sendingModeStream(""); sendingModeStream << "["; - NodeToOctreeSceneStats* octreeServerSceneStats = Application::getInstance()->getOcteeSceneStats(); + NodeToOctreeSceneStats* octreeServerSceneStats = qApp->getOcteeSceneStats(); for (NodeToOctreeSceneStatsIterator i = octreeServerSceneStats->begin(); i != octreeServerSceneStats->end(); i++) { //const QUuid& uuid = i->first; OctreeSceneStats& stats = i->second; diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 4edae4f2a4..95dd522415 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -25,7 +25,7 @@ ToolWindow::ToolWindow(QWidget* parent) : # ifndef Q_OS_LINUX setDockOptions(QMainWindow::ForceTabbedDocks); # endif - Application::getInstance()->installEventFilter(this); + qApp->installEventFilter(this); } bool ToolWindow::event(QEvent* event) { @@ -34,7 +34,7 @@ bool ToolWindow::event(QEvent* event) { if (!_hasShown) { _hasShown = true; - QMainWindow* mainWindow = Application::getInstance()->getWindow(); + QMainWindow* mainWindow = qApp->getWindow(); QRect mainGeometry = mainWindow->geometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); @@ -57,7 +57,7 @@ bool ToolWindow::eventFilter(QObject* sender, QEvent* event) { # ifndef Q_OS_LINUX switch (event->type()) { case QEvent::WindowStateChange: - if (Application::getInstance()->getWindow()->isMinimized()) { + if (qApp->getWindow()->isMinimized()) { // If we are already visible, we are self-hiding _selfHidden = isVisible(); setVisible(false); diff --git a/interface/src/ui/overlays/Billboard3DOverlay.cpp b/interface/src/ui/overlays/Billboard3DOverlay.cpp index 0b27673e18..908676e0eb 100644 --- a/interface/src/ui/overlays/Billboard3DOverlay.cpp +++ b/interface/src/ui/overlays/Billboard3DOverlay.cpp @@ -10,7 +10,6 @@ // #include "Billboard3DOverlay.h" -#include "Application.h" Billboard3DOverlay::Billboard3DOverlay(const Billboard3DOverlay* billboard3DOverlay) : Planar3DOverlay(billboard3DOverlay), diff --git a/interface/src/ui/overlays/Billboardable.cpp b/interface/src/ui/overlays/Billboardable.cpp index c6edad501f..997cf27609 100644 --- a/interface/src/ui/overlays/Billboardable.cpp +++ b/interface/src/ui/overlays/Billboardable.cpp @@ -12,6 +12,7 @@ #include "Billboardable.h" #include +#include void Billboardable::setProperties(const QScriptValue &properties) { QScriptValue isFacingAvatar = properties.property("isFacingAvatar"); @@ -30,7 +31,7 @@ QScriptValue Billboardable::getProperty(QScriptEngine* scriptEngine, const QStri void Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) { if (isFacingAvatar()) { glm::vec3 billboardPos = transform.getTranslation(); - glm::vec3 cameraPos = Application::getInstance()->getCamera()->getPosition(); + glm::vec3 cameraPos = qApp->getCamera()->getPosition(); glm::vec3 look = cameraPos - billboardPos; float elevation = -asinf(look.y / glm::length(look)); float azimuth = atan2f(look.x, look.z); diff --git a/interface/src/ui/overlays/Billboardable.h b/interface/src/ui/overlays/Billboardable.h index 1388f13e60..82387ad703 100644 --- a/interface/src/ui/overlays/Billboardable.h +++ b/interface/src/ui/overlays/Billboardable.h @@ -13,9 +13,12 @@ #define hifi_Billboardable_h #include -#include -#include +#include + +class QScriptEngine; +class QString; +class Transform; class Billboardable { public: diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 2f8450131f..11fb647f01 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -18,8 +18,8 @@ #include #include #include +#include -#include "Application.h" #include "GeometryUtil.h" diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index b6422d86e0..b9cbde9f31 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -71,7 +71,7 @@ void ModelOverlay::render(RenderArgs* args) { // check to see if when we added our model to the scene they were ready, if they were not ready, then // fix them up in the scene - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; if (_model.needsFixupInScene()) { _model.removeFromScene(scene, pendingChanges); diff --git a/interface/src/ui/overlays/OverlayPanel.cpp b/interface/src/ui/overlays/OverlayPanel.cpp index db91b7e0e3..3d7b93822e 100644 --- a/interface/src/ui/overlays/OverlayPanel.cpp +++ b/interface/src/ui/overlays/OverlayPanel.cpp @@ -18,7 +18,6 @@ #include "avatar/AvatarManager.h" #include "avatar/MyAvatar.h" -#include "Application.h" #include "Base3DOverlay.h" PropertyBinding::PropertyBinding(QString avatar, QUuid entity) : diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index db3360cbbf..96553843c8 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -10,10 +10,11 @@ #include "Overlays.h" -#include - #include +#include + +#include #include #include @@ -76,7 +77,7 @@ void Overlays::update(float deltatime) { void Overlays::cleanupOverlaysToDelete() { if (!_overlaysToDelete.isEmpty()) { - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; { @@ -169,7 +170,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope } else if (type == Grid3DOverlay::TYPE) { thisOverlay = std::make_shared(); } else if (type == LocalModelsOverlay::TYPE) { - thisOverlay = std::make_shared(Application::getInstance()->getEntityClipboardRenderer()); + thisOverlay = std::make_shared(qApp->getEntityClipboardRenderer()); } else if (type == ModelOverlay::TYPE) { thisOverlay = std::make_shared(); } else if (type == Web3DOverlay::TYPE) { @@ -196,7 +197,7 @@ unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { } else { _overlaysWorld[thisID] = overlay; - render::ScenePointer scene = Application::getInstance()->getMain3DScene(); + render::ScenePointer scene = qApp->getMain3DScene(); render::PendingChanges pendingChanges; overlay->addToScene(overlay, scene, pendingChanges); diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index 5b91ff5b52..02d432ea81 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -13,8 +13,8 @@ #include #include -#include #include +#include #include #include diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 563f454057..8c448234ad 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -11,11 +11,11 @@ #include "Text3DOverlay.h" #include +#include +#include #include #include -#include "Application.h" - const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; const float DEFAULT_BACKGROUND_ALPHA = 0.7f; const float DEFAULT_MARGIN = 0.1f; diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 4285fd72cc..8a9971f362 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -78,17 +78,6 @@ void AnimInverseKinematics::setTargetVars( } } -static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) { - // walk down the skeleton hierarchy to find the joint's root - int rootIndex = -1; - int parentIndex = skeleton->getParentIndex(index); - while (parentIndex != -1) { - rootIndex = parentIndex; - parentIndex = skeleton->getParentIndex(parentIndex); - } - return rootIndex; -} - void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { // build a list of valid targets from _targetVarVec and animVars _maxTargetIndex = -1; @@ -100,7 +89,6 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: if (jointIndex >= 0) { // this targetVar has a valid joint --> cache the indices targetVar.jointIndex = jointIndex; - targetVar.rootIndex = findRootJointInSkeleton(_skeleton, jointIndex); } else { qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton"; removeUnfoundJoints = true; @@ -111,7 +99,6 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); target.setType(animVars.lookup(targetVar.typeVar, QString(""))); - target.rootIndex = targetVar.rootIndex; target.index = targetVar.jointIndex; targets.push_back(target); if (target.index > _maxTargetIndex) { @@ -295,7 +282,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector _constraints; diff --git a/libraries/animation/src/AnimNode.cpp b/libraries/animation/src/AnimNode.cpp index 02d0e1b283..864252ff47 100644 --- a/libraries/animation/src/AnimNode.cpp +++ b/libraries/animation/src/AnimNode.cpp @@ -29,23 +29,6 @@ void AnimNode::setSkeleton(const AnimSkeleton::Pointer skeleton) { } } -const AnimPose AnimNode::getRootPose(int jointIndex) const { - AnimPose pose = AnimPose::identity; - if (_skeleton && jointIndex != -1) { - const AnimPoseVec& poses = getPosesInternal(); - int numJoints = (int)(poses.size()); - if (jointIndex < numJoints) { - int parentIndex = _skeleton->getParentIndex(jointIndex); - while (parentIndex != -1 && parentIndex < numJoints) { - jointIndex = parentIndex; - parentIndex = _skeleton->getParentIndex(jointIndex); - } - pose = poses[jointIndex]; - } - } - return pose; -} - void AnimNode::setCurrentFrame(float frame) { setCurrentFrameInternal(frame); for (auto&& child : _children) { diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index e8c611d3e4..9a21526409 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -75,8 +75,6 @@ public: return evaluate(animVars, dt, triggersOut); } - const AnimPose getRootPose(int jointIndex) const; - protected: void setCurrentFrame(float frame); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 173e387608..e43a55150c 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -93,6 +93,23 @@ const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const { return _absoluteBindPoses[jointIndex]; } +AnimPose AnimSkeleton::getRootAbsoluteBindPoseByChildName(const QString& childName) const { + AnimPose pose = AnimPose::identity; + int jointIndex = nameToJointIndex(childName); + if (jointIndex >= 0) { + int numJoints = (int)(_absoluteBindPoses.size()); + if (jointIndex < numJoints) { + int parentIndex = getParentIndex(jointIndex); + while (parentIndex != -1 && parentIndex < numJoints) { + jointIndex = parentIndex; + parentIndex = getParentIndex(jointIndex); + } + pose = _absoluteBindPoses[jointIndex]; + } + } + return pose; +} + const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const { return _relativeBindPoses[jointIndex]; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 056d8fbd9b..d59719df73 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -57,6 +57,7 @@ public: // absolute pose, not relative to parent const AnimPose& getAbsoluteBindPose(int jointIndex) const; + AnimPose getRootAbsoluteBindPoseByChildName(const QString& childName) const; // relative to parent pose const AnimPose& getRelativeBindPose(int jointIndex) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ee3ac0a05a..7f300fc84a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1036,7 +1036,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { glm::vec3 headPos, neckPos; glm::quat headRot, neckRot; - AnimPose avatarHMDPose(glm::vec3(1), params.localHeadOrientation, params.localHeadPosition); + AnimPose avatarHMDPose(glm::vec3(1.0f), params.localHeadOrientation, params.localHeadPosition); computeHeadNeckAnimVars(_animSkeleton, avatarHMDPose, headPos, headRot, neckPos, neckRot); // debug rendering @@ -1126,18 +1126,17 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { // TODO: figure out how to obtain the yFlip from where it is actually stored glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - int leftHandIndex = _animSkeleton->nameToJointIndex("LeftHand"); - AnimPose rootPose = _animNode->getRootPose(leftHandIndex); + AnimPose rootBindPose = _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); if (params.isLeftEnabled) { - _animVars.set("leftHandPosition", rootPose.trans + rootPose.rot * yFlipHACK * params.leftPosition); - _animVars.set("leftHandRotation", rootPose.rot * yFlipHACK * params.leftOrientation); + _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); + _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); } else { _animVars.unset("leftHandPosition"); _animVars.unset("leftHandRotation"); } if (params.isRightEnabled) { - _animVars.set("rightHandPosition", rootPose.trans + rootPose.rot * yFlipHACK * params.rightPosition); - _animVars.set("rightHandRotation", rootPose.rot * yFlipHACK * params.rightOrientation); + _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); + _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); } else { _animVars.unset("rightHandPosition"); _animVars.unset("rightHandRotation"); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 1ef56649fa..e699ee9266 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -76,8 +76,6 @@ class QIODevice; typedef struct ty_gverb ty_gverb; -typedef glm::vec3 (*AudioPositionGetter)(); -typedef glm::quat (*AudioOrientationGetter)(); class NLPacket; @@ -85,6 +83,8 @@ class AudioClient : public AbstractAudioInterface, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: + using AudioPositionGetter = std::function; + using AudioOrientationGetter = std::function; class AudioOutputIODevice : public QIODevice { public: diff --git a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp new file mode 100644 index 0000000000..9987ae345c --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp @@ -0,0 +1,52 @@ +// +// Created by Bradley Austin Davis on 2015/10/04 +// Copyright 2013-2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AbstractHMDScriptingInterface.h" + +#include + +#include "DisplayPlugin.h" +#include +#include + +static Setting::Handle IPD_SCALE_HANDLE("hmd.ipdScale", 1.0f); + +AbstractHMDScriptingInterface::AbstractHMDScriptingInterface() { + _IPDScale = IPD_SCALE_HANDLE.get(); +} + +float AbstractHMDScriptingInterface::getIPD() const { + return PluginContainer::getInstance().getActiveDisplayPlugin()->getIPD(); +} + +float AbstractHMDScriptingInterface::getEyeHeight() const { + // FIXME update the display plugin interface to expose per-plugin settings + return OVR_DEFAULT_EYE_HEIGHT; +} + +float AbstractHMDScriptingInterface::getPlayerHeight() const { + // FIXME update the display plugin interface to expose per-plugin settings + return OVR_DEFAULT_PLAYER_HEIGHT; +} + +float AbstractHMDScriptingInterface::getIPDScale() const { + return _IPDScale; +} + +void AbstractHMDScriptingInterface::setIPDScale(float IPDScale) { + IPDScale = glm::clamp(IPDScale, -1.0f, 3.0f); + if (IPDScale != _IPDScale) { + _IPDScale = IPDScale; + IPD_SCALE_HANDLE.set(IPDScale); + emit IPDScaleChanged(); + } +} + +bool AbstractHMDScriptingInterface::isHMDMode() const { + return PluginContainer::getInstance().getActiveDisplayPlugin()->isHmd(); +} diff --git a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h new file mode 100644 index 0000000000..5df58ce677 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h @@ -0,0 +1,39 @@ +// +// Created by Bradley Austin Davis on 2015/10/04 +// Copyright 2013-2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_AbstractHMDScriptingInterface_h +#define hifi_AbstractHMDScriptingInterface_h + +#include + +class AbstractHMDScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(bool active READ isHMDMode) + Q_PROPERTY(float ipd READ getIPD) + Q_PROPERTY(float eyeHeight READ getEyeHeight) + Q_PROPERTY(float playerHeight READ getPlayerHeight) + Q_PROPERTY(float ipdScale READ getIPDScale WRITE setIPDScale NOTIFY IPDScaleChanged) + +public: + AbstractHMDScriptingInterface(); + float getIPD() const; + float getEyeHeight() const; + float getPlayerHeight() const; + float getIPDScale() const; + void setIPDScale(float ipdScale); + bool isHMDMode() const; + +signals: + void IPDScaleChanged(); + +private: + float _IPDScale{ 1.0 }; +}; + +#endif // hifi_AbstractHMDScriptingInterface_h diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 8b9d249bd4..b4ae6be97f 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -46,6 +46,8 @@ void for_each_eye(F f, FF ff) { class QWindow; +#define AVERAGE_HUMAN_IPD 0.064f + class DisplayPlugin : public Plugin { Q_OBJECT public: @@ -107,21 +109,22 @@ public: return baseProjection; } - virtual glm::mat4 getView(Eye eye, const glm::mat4& baseView) const { - return glm::inverse(getEyePose(eye)) * baseView; - } - // HMD specific methods // TODO move these into another class? - virtual glm::mat4 getEyePose(Eye eye) const { - static const glm::mat4 pose; return pose; + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const { + static const glm::mat4 transform; return transform; } virtual glm::mat4 getHeadPose() const { static const glm::mat4 pose; return pose; } - virtual float getIPD() const { return 0.0f; } + // Needed for timewarp style features + virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) { + // NOOP + } + + virtual float getIPD() const { return AVERAGE_HUMAN_IPD; } virtual void abandonCalibration() {} virtual void resetSensors() {} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp index f2a7b06510..859a4a810a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp @@ -19,7 +19,6 @@ void OculusBaseDisplayPlugin::preRender() { #if (OVR_MAJOR_VERSION >= 6) ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex); _trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds); - ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses); #endif } @@ -33,14 +32,19 @@ void OculusBaseDisplayPlugin::resetSensors() { #endif } -glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const { - return toGlm(_eyePoses[eye]); +glm::mat4 OculusBaseDisplayPlugin::getEyeToHeadTransform(Eye eye) const { + return glm::translate(mat4(), toGlm(_eyeOffsets[eye])); } glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const { return toGlm(_trackingState.HeadPose.ThePose); } +void OculusBaseDisplayPlugin::setEyeRenderPose(Eye eye, const glm::mat4& pose) { + _eyePoses[eye] = ovrPoseFromGlm(pose); +} + + bool OculusBaseDisplayPlugin::isSupported() const { #if (OVR_MAJOR_VERSION >= 6) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { @@ -151,9 +155,9 @@ void OculusBaseDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sce } float OculusBaseDisplayPlugin::getIPD() const { - float result = 0.0f; + float result = OVR_DEFAULT_IPD; #if (OVR_MAJOR_VERSION >= 6) - result = ovr_GetFloat(_hmd, OVR_KEY_IPD, OVR_DEFAULT_IPD); + result = ovr_GetFloat(_hmd, OVR_KEY_IPD, result); #endif return result; -} \ No newline at end of file +} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h index d879085b8f..6307f6bbf9 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h @@ -29,8 +29,9 @@ public: virtual glm::uvec2 getRecommendedRenderSize() const override final; virtual glm::uvec2 getRecommendedUiSize() const override final { return uvec2(1920, 1080); } virtual void resetSensors() override final; - virtual glm::mat4 getEyePose(Eye eye) const override final; + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override final; virtual glm::mat4 getHeadPose() const override final; + virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) override final; virtual float getIPD() const override final; protected: @@ -39,6 +40,7 @@ protected: protected: ovrPosef _eyePoses[2]; + ovrVector3f _eyeOffsets[2]; mat4 _eyeProjections[3]; mat4 _compositeEyeProjections[2]; @@ -50,13 +52,12 @@ protected: ovrHmd _hmd; float _ipd{ OVR_DEFAULT_IPD }; ovrEyeRenderDesc _eyeRenderDescs[2]; - ovrVector3f _eyeOffsets[2]; ovrFovPort _eyeFovs[2]; - ovrHmdDesc _hmdDesc; - ovrLayerEyeFov _sceneLayer; + ovrHmdDesc _hmdDesc; + ovrLayerEyeFov _sceneLayer; #endif #if (OVR_MAJOR_VERSION == 7) - ovrGraphicsLuid _luid; + ovrGraphicsLuid _luid; #endif }; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h index df0a6c5228..5a6999075b 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h @@ -79,3 +79,11 @@ inline ovrQuatf ovrFromGlm(const glm::quat & q) { return{ q.x, q.y, q.z, q.w }; } +inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) { + glm::vec3 translation = glm::vec3(m[3]) / m[3].w; + glm::quat orientation = glm::quat_cast(m); + ovrPosef result; + result.Orientation = ovrFromGlm(orientation); + result.Position = ovrFromGlm(translation); + return result; +} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index ade34afcae..1ad61513d6 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -59,11 +59,11 @@ void OculusLegacyDisplayPlugin::resetSensors() { #endif } -glm::mat4 OculusLegacyDisplayPlugin::getEyePose(Eye eye) const { +glm::mat4 OculusLegacyDisplayPlugin::getEyeToHeadTransform(Eye eye) const { #if (OVR_MAJOR_VERSION == 5) return toGlm(_eyePoses[eye]); #else - return WindowOpenGLDisplayPlugin::getEyePose(eye); + return WindowOpenGLDisplayPlugin::getEyeToHeadTransform(eye); #endif } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h index 5bce032948..9e2e47f434 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h @@ -31,7 +31,7 @@ public: virtual glm::uvec2 getRecommendedRenderSize() const override; virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } virtual void resetSensors() override; - virtual glm::mat4 getEyePose(Eye eye) const override; + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; virtual glm::mat4 getHeadPose() const override; protected: diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index fab9cc5dd4..faf5a7b781 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -160,8 +160,8 @@ void OpenVrDisplayPlugin::resetSensors() { _sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0])); } -glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const { - return getHeadPose() * _eyesData[eye]._eyeOffset; +glm::mat4 OpenVrDisplayPlugin::getEyeToHeadTransform(Eye eye) const { + return _eyesData[eye]._eyeOffset; } glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index afe024e72b..7849623552 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -29,7 +29,7 @@ public: virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual void resetSensors() override; - virtual glm::mat4 getEyePose(Eye eye) const override; + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; virtual glm::mat4 getHeadPose() const override; protected: diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 77906d1857..2ea79ed2e0 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -61,10 +61,6 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje return eyeProjection; } -glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const { - return mat4(); -} - std::vector _screenActions; void StereoDisplayPlugin::activate() { auto screens = qApp->screens(); diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index 86f35c1260..33b0b09b0d 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -21,7 +21,14 @@ public: virtual float getRecommendedAspectRatio() const override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; - virtual glm::mat4 getEyePose(Eye eye) const override; + + // NOTE, because Stereo displays don't include head tracking, and therefore + // can't include roll or pitch, the eye separation is embedded into the projection + // matrix. However, this eliminates the possibility of easily mainpulating + // the IPD at the Application level, the way we now allow with HMDs. + // If that becomes an issue then we'll need to break up the functionality similar + // to the HMD plugins. + // virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; protected: void updateScreen(); diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index c34f072601..d874646b4b 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -237,16 +237,16 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa return 0.0f; } - QVariant vV = arguments[argumentName]; - bool vOk = true; - float v = vV.toFloat(&vOk); + QVariant variant = arguments[argumentName]; + bool variantOk = true; + float value = variant.toFloat(&variantOk); - if (!vOk || v != v) { + if (!variantOk || std::isnan(value)) { ok = false; return 0.0f; } - return v; + return value; } int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments, @@ -259,16 +259,16 @@ int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMa return 0.0f; } - QVariant vV = arguments[argumentName]; - bool vOk = true; - int v = vV.toInt(&vOk); + QVariant variant = arguments[argumentName]; + bool variantOk = true; + int value = variant.toInt(&variantOk); - if (!vOk || v != v) { + if (!variantOk) { ok = false; return 0; } - return v; + return value; } QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 0c738c5a5b..4b3b3b0fea 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -264,78 +264,6 @@ void appendModelIDs(const QString& parentID, const QMultiHash& } } - -gpu::BufferPointer FBXMeshPart::getMergedTriangles() const { - // if we've been asked for our triangulation of the original quads, but we don't yet have them - // then create them now. - if (!mergedTrianglesAvailable) { - mergedTrianglesAvailable = true; - - mergedTrianglesIndicesBuffer = std::make_shared(); - - // QVector quadIndices; // original indices from the FBX mesh - QVector mergedTrianglesIndices; // triangle versions of quads converted when first needed - const int INDICES_PER_ORIGINAL_TRIANGLE = 3; - const int INDICES_PER_ORIGINAL_QUAD = 4; - const int INDICES_PER_TRIANGULATED_QUAD = 6; - int numberOfQuads = quadIndices.size() / INDICES_PER_ORIGINAL_QUAD; - int numberOfTriangles = triangleIndices.size() / INDICES_PER_ORIGINAL_TRIANGLE; - int mergedNumberOfIndices = (numberOfQuads * INDICES_PER_TRIANGULATED_QUAD) + triangleIndices.size(); - - // resized our merged indices to be enough room for our triangulated quads and our original triangles - mergedTrianglesIndices.resize(mergedNumberOfIndices); - - int originalIndex = 0; - int triangulatedIndex = 0; - - // triangulate our quads - for (int fromQuad = 0; fromQuad < numberOfQuads; fromQuad++) { - int i0 = quadIndices[originalIndex + 0]; - int i1 = quadIndices[originalIndex + 1]; - int i2 = quadIndices[originalIndex + 2]; - int i3 = quadIndices[originalIndex + 3]; - - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - mergedTrianglesIndices[triangulatedIndex + 0] = i0; - mergedTrianglesIndices[triangulatedIndex + 1] = i1; - mergedTrianglesIndices[triangulatedIndex + 2] = i3; - - mergedTrianglesIndices[triangulatedIndex + 3] = i1; - mergedTrianglesIndices[triangulatedIndex + 4] = i2; - mergedTrianglesIndices[triangulatedIndex + 5] = i3; - - originalIndex += INDICES_PER_ORIGINAL_QUAD; - triangulatedIndex += INDICES_PER_TRIANGULATED_QUAD; - } - - // add our original triangs - originalIndex = 0; - for (int fromTriangle = 0; fromTriangle < numberOfTriangles; fromTriangle++) { - int i0 = triangleIndices[originalIndex + 0]; - int i1 = triangleIndices[originalIndex + 1]; - int i2 = triangleIndices[originalIndex + 2]; - - mergedTrianglesIndices[triangulatedIndex + 0] = i0; - mergedTrianglesIndices[triangulatedIndex + 1] = i1; - mergedTrianglesIndices[triangulatedIndex + 2] = i2; - - originalIndex += INDICES_PER_ORIGINAL_TRIANGLE; - triangulatedIndex += INDICES_PER_ORIGINAL_TRIANGLE; - } - - mergedTrianglesIndicesCount = mergedNumberOfIndices; - mergedTrianglesIndicesBuffer->append(mergedNumberOfIndices * sizeof(quint32), (gpu::Byte*)mergedTrianglesIndices.data()); - - } - return mergedTrianglesIndicesBuffer; -} - FBXBlendshape extractBlendshape(const FBXNode& object) { FBXBlendshape blendshape; foreach (const FBXNode& data, object.children) { @@ -1584,10 +1512,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } extracted.mesh.isEye = (maxJointIndex == geometry.leftEyeJointIndex || maxJointIndex == geometry.rightEyeJointIndex); -# if USE_MODEL_MESH buildModelMesh(extracted, url); -# endif - + if (extracted.mesh.isEye) { if (maxJointIndex == geometry.leftEyeJointIndex) { geometry.leftEyeSize = extracted.mesh.meshExtents.largestDimension() * offsetScale; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 2277d84d9c..c0cbf5fb18 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -12,8 +12,6 @@ #ifndef hifi_FBXReader_h #define hifi_FBXReader_h -#define USE_MODEL_MESH 1 - #include #include #include @@ -123,27 +121,22 @@ class FBXMeshPart { public: QVector quadIndices; // original indices from the FBX mesh + QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles QVector triangleIndices; // original indices from the FBX mesh - mutable gpu::BufferPointer mergedTrianglesIndicesBuffer; // both the quads and the triangles merged into a single set of triangles QString materialID; - - mutable bool mergedTrianglesAvailable = false; - mutable int mergedTrianglesIndicesCount = 0; - - gpu::BufferPointer getMergedTriangles() const; }; class FBXMaterial { public: FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, - const glm::vec2& emissiveParams, float shininess, float opacity) : - diffuseColor(diffuseColor), - specularColor(specularColor), - emissiveColor(emissiveColor), - emissiveParams(emissiveParams), - shininess(shininess), + FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + const glm::vec2& emissiveParams, float shininess, float opacity) : + diffuseColor(diffuseColor), + specularColor(specularColor), + emissiveColor(emissiveColor), + emissiveParams(emissiveParams), + shininess(shininess), opacity(opacity) {} glm::vec3 diffuseColor{ 1.0f }; @@ -193,9 +186,8 @@ public: QVector blendshapes; unsigned int meshIndex; // the order the meshes appeared in the object file -# if USE_MODEL_MESH - model::Mesh _mesh; -# endif + + model::MeshPointer _mesh; }; class ExtractedMesh { diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index 6da53dbede..e947a0356e 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -107,6 +107,8 @@ void FBXReader::consolidateFBXMaterials() { if (!specularTextureID.isNull()) { specularTexture = getTexture(specularTextureID); detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity()); + + material.specularTexture = specularTexture; } FBXTexture emissiveTexture; diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c0cf57df58..097862ef38 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -348,6 +348,28 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); appendIndex(data, part.quadIndices, beginIndex++); + + int quadStartIndex = part.quadIndices.size() - 4; + int i0 = part.quadIndices[quadStartIndex + 0]; + int i1 = part.quadIndices[quadStartIndex + 1]; + int i2 = part.quadIndices[quadStartIndex + 2]; + int i3 = part.quadIndices[quadStartIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + part.quadTrianglesIndices.append(i0); + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i3); + + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i2); + part.quadTrianglesIndices.append(i3); + } else { for (int nextIndex = beginIndex + 1;; ) { appendIndex(data, part.triangleIndices, beginIndex); @@ -364,25 +386,33 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn return data.extracted; } - -#if USE_MODEL_MESH void FBXReader::buildModelMesh(ExtractedMesh& extracted, const QString& url) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); + unsigned int totalSourceIndices = 0; + foreach(const FBXMeshPart& part, extracted.mesh.parts) { + totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); + } + + if (!totalSourceIndices) { + qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url; + return; + } + if (extracted.mesh.vertices.size() == 0) { - extracted.mesh._mesh = model::Mesh(); qCDebug(modelformat) << "buildModelMesh failed -- no vertices, url = " << url; return; } + FBXMesh& fbxMesh = extracted.mesh; - model::Mesh mesh; + model::MeshPointer mesh(new model::Mesh()); // Grab the vertices in a buffer auto vb = std::make_shared(); vb->setData(extracted.mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) extracted.mesh.vertices.data()); gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - mesh.setVertexBuffer(vbv); + mesh->setVertexBuffer(vbv); // evaluate all attribute channels sizes int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3); @@ -414,98 +444,106 @@ void FBXReader::buildModelMesh(ExtractedMesh& extracted, const QString& url) { attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData()); if (normalsSize) { - mesh.addAttribute(gpu::Stream::NORMAL, - model::BufferView(attribBuffer, normalsOffset, normalsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); + mesh->addAttribute(gpu::Stream::NORMAL, + model::BufferView(attribBuffer, normalsOffset, normalsSize, + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); } if (tangentsSize) { - mesh.addAttribute(gpu::Stream::TANGENT, - model::BufferView(attribBuffer, tangentsOffset, tangentsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); + mesh->addAttribute(gpu::Stream::TANGENT, + model::BufferView(attribBuffer, tangentsOffset, tangentsSize, + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); } if (colorsSize) { - mesh.addAttribute(gpu::Stream::COLOR, - model::BufferView(attribBuffer, colorsOffset, colorsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB))); + mesh->addAttribute(gpu::Stream::COLOR, + model::BufferView(attribBuffer, colorsOffset, colorsSize, + gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB))); } if (texCoordsSize) { - mesh.addAttribute(gpu::Stream::TEXCOORD, - model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, - gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + mesh->addAttribute(gpu::Stream::TEXCOORD, + model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); } if (texCoords1Size) { - mesh.addAttribute(gpu::Stream::TEXCOORD1, - model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size, - gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + mesh->addAttribute( gpu::Stream::TEXCOORD1, + model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size, + gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + } else if (texCoordsSize) { + mesh->addAttribute(gpu::Stream::TEXCOORD1, + model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); } + if (clusterIndicesSize) { - mesh.addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, + mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW))); } if (clusterWeightsSize) { - mesh.addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, + mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW))); } unsigned int totalIndices = 0; - foreach(const FBXMeshPart& part, extracted.mesh.parts) { - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + totalIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } if (! totalIndices) { - extracted.mesh._mesh = model::Mesh(); qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url; return; } - auto ib = std::make_shared(); - ib->resize(totalIndices * sizeof(int)); + auto indexBuffer = std::make_shared(); + indexBuffer->resize(totalIndices * sizeof(int)); int indexNum = 0; int offset = 0; std::vector< model::Mesh::Part > parts; - + if (extracted.mesh.parts.size() > 1) { + indexNum = 0; + } foreach(const FBXMeshPart& part, extracted.mesh.parts) { - model::Mesh::Part quadPart(indexNum, part.quadIndices.size(), 0, model::Mesh::QUADS); - if (quadPart._numIndices) { - parts.push_back(quadPart); - ib->setSubData(offset, part.quadIndices.size() * sizeof(int), - (gpu::Byte*) part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - indexNum += part.quadIndices.size(); + model::Mesh::Part modelPart(indexNum, 0, 0, model::Mesh::TRIANGLES); + + if (part.quadTrianglesIndices.size()) { + indexBuffer->setSubData(offset, + part.quadTrianglesIndices.size() * sizeof(int), + (gpu::Byte*) part.quadTrianglesIndices.constData()); + offset += part.quadTrianglesIndices.size() * sizeof(int); + indexNum += part.quadTrianglesIndices.size(); + modelPart._numIndices += part.quadTrianglesIndices.size(); } - model::Mesh::Part triPart(indexNum, part.triangleIndices.size(), 0, model::Mesh::TRIANGLES); - if (triPart._numIndices) { - ib->setSubData(offset, part.triangleIndices.size() * sizeof(int), - (gpu::Byte*) part.triangleIndices.constData()); + + if (part.triangleIndices.size()) { + indexBuffer->setSubData(offset, + part.triangleIndices.size() * sizeof(int), + (gpu::Byte*) part.triangleIndices.constData()); offset += part.triangleIndices.size() * sizeof(int); indexNum += part.triangleIndices.size(); + modelPart._numIndices += part.triangleIndices.size(); } + + parts.push_back(modelPart); } - gpu::BufferView ibv(ib, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::XYZ)); - mesh.setIndexBuffer(ibv); + gpu::BufferView indexBufferView(indexBuffer, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::XYZ)); + mesh->setIndexBuffer(indexBufferView); if (parts.size()) { auto pb = std::make_shared(); pb->setData(parts.size() * sizeof(model::Mesh::Part), (const gpu::Byte*) parts.data()); gpu::BufferView pbv(pb, gpu::Element(gpu::VEC4, gpu::UINT32, gpu::XYZW)); - mesh.setPartBuffer(pbv); + mesh->setPartBuffer(pbv); } else { - extracted.mesh._mesh = model::Mesh(); qCDebug(modelformat) << "buildModelMesh failed -- no parts, url = " << url; return; } // model::Box box = - mesh.evalPartBound(0); + mesh->evalPartBound(0); extracted.mesh._mesh = mesh; } -#endif // USE_MODEL_MESH - diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index b1e63a18bd..37135ccd98 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -319,8 +319,11 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { uint32 startIndex = batch._params[paramOffset + 0]._uint; GLenum glType = _elementTypeToGLType[_input._indexBufferType]; + + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); - glDrawElements(mode, numIndices, glType, reinterpret_cast(startIndex + _input._indexBufferOffset)); + glDrawElements(mode, numIndices, glType, indexBufferByteOffset); (void) CHECK_GL_ERROR(); } @@ -353,10 +356,13 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) { uint32 startInstance = batch._params[paramOffset + 0]._uint; GLenum glType = _elementTypeToGLType[_input._indexBufferType]; + auto typeByteSize = TYPE_SIZE[_input._indexBufferType]; + GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); + #if (GPU_INPUT_PROFILE == GPU_CORE_43) - glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, reinterpret_cast(startIndex + _input._indexBufferOffset), numInstances, 0, startInstance); + glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); #else - glDrawElementsInstanced(mode, numIndices, glType, reinterpret_cast(startIndex + _input._indexBufferOffset), numInstances); + glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, numInstances); Q_UNUSED(startInstance); #endif (void)CHECK_GL_ERROR(); @@ -389,7 +395,7 @@ void GLBackend::do_multiDrawIndexedIndirect(Batch& batch, uint32 paramOffset) { uint commandCount = batch._params[paramOffset + 0]._uint; GLenum mode = _primitiveToGLmode[(Primitive)batch._params[paramOffset + 1]._uint]; GLenum indexType = _elementTypeToGLType[_input._indexBufferType]; - + glMultiDrawElementsIndirect(mode, indexType, reinterpret_cast(_input._indirectBufferOffset), commandCount, _input._indirectBufferStride); #else // FIXME implement the slow path diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index f12cda827a..e01dbcd0dc 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -286,7 +286,7 @@ protected: BufferPointer _indexBuffer; Offset _indexBufferOffset; Type _indexBufferType; - + BufferPointer _indirectBuffer; Offset _indirectBufferOffset{ 0 }; Offset _indirectBufferStride{ 0 }; diff --git a/libraries/gpu/src/gpu/GLBackendInput.cpp b/libraries/gpu/src/gpu/GLBackendInput.cpp index a70e91d7fa..5cdcf0adc6 100755 --- a/libraries/gpu/src/gpu/GLBackendInput.cpp +++ b/libraries/gpu/src/gpu/GLBackendInput.cpp @@ -315,7 +315,7 @@ void GLBackend::do_setIndirectBuffer(Batch& batch, uint32 paramOffset) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, getBufferID(*buffer)); } else { // FIXME do we really need this? Is there ever a draw call where we care that the element buffer is null? - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); } } diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index 61150ab90e..183f2137fb 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -92,3 +92,16 @@ void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset _offsets.push_back(offset); _strides.push_back(stride); } + +BufferStream BufferStream::makeRangedStream(uint32 offset, uint32 count) const { + if ((offset < _buffers.size())) { + auto rangeSize = std::min(count, (uint32)(_buffers.size() - offset)); + BufferStream newStream; + newStream._buffers.insert(newStream._buffers.begin(), _buffers.begin() + offset, _buffers.begin() + offset + rangeSize); + newStream._offsets.insert(newStream._offsets.begin(), _offsets.begin() + offset, _offsets.begin() + offset + rangeSize); + newStream._strides.insert(newStream._strides.begin(), _strides.begin() + offset, _strides.begin() + offset + rangeSize); + return newStream; + } + + return BufferStream(); +} diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index 420aa50f72..492af5f62a 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -134,12 +134,15 @@ public: BufferStream(); ~BufferStream(); + void clear() { _buffers.clear(); _offsets.clear(); _strides.clear(); } void addBuffer(const BufferPointer& buffer, Offset offset, Offset stride); const Buffers& getBuffers() const { return _buffers; } const Offsets& getOffsets() const { return _offsets; } const Strides& getStrides() const { return _strides; } - uint8 getNumBuffers() const { return _buffers.size(); } + uint32 getNumBuffers() const { return _buffers.size(); } + + BufferStream makeRangedStream(uint32 offset, uint32 count = -1) const; protected: Buffers _buffers; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 780cf83649..c7d3523496 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -266,125 +266,9 @@ void NetworkGeometry::modelRequestError(QNetworkReply::NetworkError error) { } static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBaseUrl) { - auto textureCache = DependencyManager::get(); NetworkMesh* networkMesh = new NetworkMesh(); - int totalIndices = 0; - //bool checkForTexcoordLightmap = false; - - - - // process network parts - foreach (const FBXMeshPart& part, mesh.parts) { - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); - } - - // initialize index buffer - { - networkMesh->_indexBuffer = std::make_shared(); - networkMesh->_indexBuffer->resize(totalIndices * sizeof(int)); - int offset = 0; - foreach(const FBXMeshPart& part, mesh.parts) { - networkMesh->_indexBuffer->setSubData(offset, part.quadIndices.size() * sizeof(int), - (gpu::Byte*) part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - networkMesh->_indexBuffer->setSubData(offset, part.triangleIndices.size() * sizeof(int), - (gpu::Byte*) part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); - } - } - - // initialize vertex buffer - { - networkMesh->_vertexBuffer = std::make_shared(); - // if we don't need to do any blending, the positions/normals can be static - if (mesh.blendshapes.isEmpty()) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int texCoords1Offset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterIndicesOffset = texCoords1Offset + mesh.texCoords1.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - - networkMesh->_vertexBuffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); - networkMesh->_vertexBuffer->setSubData(normalsOffset, mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); - networkMesh->_vertexBuffer->setSubData(tangentsOffset, - mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh->_vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh->_vertexBuffer->setSubData(texCoords1Offset, - mesh.texCoords1.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords1.constData()); - networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - // otherwise, at least the cluster indices/weights can be static - networkMesh->_vertexStream = std::make_shared(); - networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.normals.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, normalsOffset, sizeof(glm::vec3)); - if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, tangentsOffset, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.texCoords1.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoords1Offset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh->_vertexFormat = std::make_shared(); - networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.texCoords1.size()) { - networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - // } else if (checkForTexcoordLightmap && mesh.texCoords.size()) { - } else if (mesh.texCoords.size()) { - // need lightmap texcoord UV but doesn't have uv#1 so just reuse the same channel - networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD1, channelNum - 1, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - } - if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)); - } - else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - - networkMesh->_vertexBuffer->resize(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); - networkMesh->_vertexBuffer->setSubData(0, mesh.tangents.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.tangents.constData()); - networkMesh->_vertexBuffer->setSubData(colorsOffset, mesh.colors.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.colors.constData()); - networkMesh->_vertexBuffer->setSubData(texCoordsOffset, - mesh.texCoords.size() * sizeof(glm::vec2), (gpu::Byte*) mesh.texCoords.constData()); - networkMesh->_vertexBuffer->setSubData(clusterIndicesOffset, - mesh.clusterIndices.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterIndices.constData()); - networkMesh->_vertexBuffer->setSubData(clusterWeightsOffset, - mesh.clusterWeights.size() * sizeof(glm::vec4), (gpu::Byte*) mesh.clusterWeights.constData()); - - networkMesh->_vertexStream = std::make_shared(); - if (mesh.tangents.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, 0, sizeof(glm::vec3)); - if (mesh.colors.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, colorsOffset, sizeof(glm::vec3)); - if (mesh.texCoords.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, texCoordsOffset, sizeof(glm::vec2)); - if (mesh.clusterIndices.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterIndicesOffset, sizeof(glm::vec4)); - if (mesh.clusterWeights.size()) networkMesh->_vertexStream->addBuffer(networkMesh->_vertexBuffer, clusterWeightsOffset, sizeof(glm::vec4)); - - int channelNum = 0; - networkMesh->_vertexFormat = std::make_shared(); - networkMesh->_vertexFormat->setAttribute(gpu::Stream::POSITION, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.normals.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::NORMAL, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.tangents.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TANGENT, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - if (mesh.colors.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum++, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)); - if (mesh.texCoords.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, channelNum++, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - if (mesh.clusterIndices.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, channelNum++, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)); - if (mesh.clusterWeights.size()) networkMesh->_vertexFormat->setAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, channelNum++, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)); - } - } + networkMesh->_mesh = mesh._mesh; return networkMesh; } @@ -393,8 +277,6 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const auto textureCache = DependencyManager::get(); NetworkMaterial* networkMaterial = new NetworkMaterial(); - //bool checkForTexcoordLightmap = false; - networkMaterial->_material = material._material; if (!material.diffuseTexture.filename.isEmpty()) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 1110d36e3e..cd75794e2b 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -191,15 +191,7 @@ public: /// The state associated with a single mesh. class NetworkMesh { public: - gpu::BufferPointer _indexBuffer; - gpu::BufferPointer _vertexBuffer; - - gpu::BufferStreamPointer _vertexStream; - - gpu::Stream::FormatPointer _vertexFormat; - - int getTranslucentPartCount(const FBXMesh& fbxMesh) const; - bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; + model::MeshPointer _mesh; }; #endif // hifi_GeometryCache_h diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 6723293dc1..63adeec0f1 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -64,6 +64,24 @@ void Mesh::evalVertexFormat() { } _vertexFormat.reset(vf); + + evalVertexStream(); +} + + +void Mesh::evalVertexStream() { + _vertexStream.clear(); + + int channelNum = 0; + if (hasVertexData()) { + _vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); + channelNum++; + } + for (auto attrib : _attributeBuffers) { + BufferView& view = attrib.second; + _vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); + channelNum++; + } } void Mesh::setIndexBuffer(const BufferView& buffer) { @@ -116,23 +134,6 @@ const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const return totalBound; } -const gpu::BufferStream Mesh::makeBufferStream() const { - gpu::BufferStream stream; - - int channelNum = 0; - if (hasVertexData()) { - stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); - channelNum++; - } - for (auto attrib : _attributeBuffers) { - BufferView& view = attrib.second; - stream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); - channelNum++; - } - - return stream; -} - Geometry::Geometry() { } diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index 5ef414a2d1..8b5f448a64 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -57,6 +57,9 @@ public: // Stream format const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; } + // BufferStream on the mesh vertices and attributes matching the vertex format + const gpu::BufferStream getVertexStream() const { return _vertexStream; } + // Index Buffer void setIndexBuffer(const BufferView& buffer); const BufferView& getIndexBuffer() const { return _indexBuffer; } @@ -109,15 +112,12 @@ public: // the returned box is the bounding box of ALL the evaluated part bounds. const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const; - - // Generate a BufferStream on the mesh vertices and attributes - const gpu::BufferStream makeBufferStream() const; - static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast(topo); } protected: gpu::Stream::FormatPointer _vertexFormat; + gpu::BufferStream _vertexStream; BufferView _vertexBuffer; BufferViewMap _attributeBuffers; @@ -127,6 +127,7 @@ protected: BufferView _partBuffer; void evalVertexFormat(); + void evalVertexStream(); }; typedef std::shared_ptr< Mesh > MeshPointer; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index fbb895795b..e0b54e4072 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -28,9 +28,6 @@ const QString INDEX_PATH = "/"; const QString GET_PLACE = "/api/v1/places/%1"; -typedef const glm::vec3& (*PositionGetter)(); -typedef glm::quat (*OrientationGetter)(); - class AddressManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -40,6 +37,8 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + using PositionGetter = std::function; + using OrientationGetter = std::function; enum LookupTrigger { UserInput, @@ -130,8 +129,8 @@ private: QString _host; quint16 _port; QUuid _rootPlaceID; - PositionGetter _positionGetter { nullptr }; - OrientationGetter _orientationGetter { nullptr }; + PositionGetter _positionGetter; + OrientationGetter _orientationGetter; QStack _backStack; QStack _forwardStack; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c13a82f821..d4b15c323b 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -274,10 +274,14 @@ void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& conn qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode) { Q_ASSERT(!packet.isPartOfMessage()); + if (!destinationNode.getActiveSocket()) { return 0; } + emit dataSent(destinationNode.getType(), packet.getDataSize()); + destinationNode.recordBytesSent(packet.getDataSize()); + return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret()); } @@ -298,7 +302,10 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& if (!destinationNode.getActiveSocket()) { return 0; } + emit dataSent(destinationNode.getType(), packet->getDataSize()); + destinationNode.recordBytesSent(packet->getDataSize()); + return sendPacket(std::move(packet), *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret()); } @@ -514,7 +521,7 @@ std::unique_ptr LimitedNodeList::constructPingPacket(PingType_t pingTy pingPacket->writePrimitive(pingType); pingPacket->writePrimitive(usecTimestampNow()); - return std::move(pingPacket); + return pingPacket; } std::unique_ptr LimitedNodeList::constructPingReplyPacket(NLPacket& pingPacket) { @@ -529,7 +536,7 @@ std::unique_ptr LimitedNodeList::constructPingReplyPacket(NLPacket& pi replyPacket->writePrimitive(timeFromOriginalPing); replyPacket->writePrimitive(usecTimestampNow()); - return std::move(replyPacket); + return replyPacket; } std::unique_ptr LimitedNodeList::constructICEPingPacket(PingType_t pingType, const QUuid& iceID) { @@ -539,7 +546,7 @@ std::unique_ptr LimitedNodeList::constructICEPingPacket(PingType_t pin icePingPacket->write(iceID.toRfc4122()); icePingPacket->writePrimitive(pingType); - return std::move(icePingPacket); + return icePingPacket; } std::unique_ptr LimitedNodeList::constructICEPingReplyPacket(NLPacket& pingPacket, const QUuid& iceID) { @@ -661,6 +668,22 @@ void LimitedNodeList::sendSTUNRequest() { _nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); } +void LimitedNodeList::sendPingPackets() { + eachMatchingNode([](const SharedNodePointer& node)->bool { + switch (node->getType()) { + case NodeType::AvatarMixer: + case NodeType::AudioMixer: + case NodeType::EntityServer: + case NodeType::AssetServer: + return true; + default: + return false; + } + }, [&](const SharedNodePointer& node) { + sendPacket(constructPingPacket(), *node); + }); +} + void LimitedNodeList::processSTUNResponse(std::unique_ptr packet) { // check the cookie to make sure this is actually a STUN response // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index abf292e65e..40c5390c7e 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -230,6 +230,7 @@ public slots: void startSTUNPublicSocketUpdate(); virtual void sendSTUNRequest(); + void sendPingPackets(); void killNodeWithUUID(const QUuid& nodeUUID); diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index b17656aea0..9253243a7f 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -232,22 +232,22 @@ BandwidthRecorder& getBandwidthRecorder(const QUuid & uuid) { return *PEER_BANDWIDTH[uuid].data(); } -void NetworkPeer::recordBytesSent(int count) { +void NetworkPeer::recordBytesSent(int count) const { auto& bw = getBandwidthRecorder(_uuid); bw.updateOutboundData(0, count); } -void NetworkPeer::recordBytesReceived(int count) { +void NetworkPeer::recordBytesReceived(int count) const { auto& bw = getBandwidthRecorder(_uuid); bw.updateInboundData(0, count); } -float NetworkPeer::getOutboundBandwidth() { +float NetworkPeer::getOutboundBandwidth() const { auto& bw = getBandwidthRecorder(_uuid); return bw.getAverageOutputKilobitsPerSecond(0); } -float NetworkPeer::getInboundBandwidth() { +float NetworkPeer::getInboundBandwidth() const { auto& bw = getBandwidthRecorder(_uuid); return bw.getAverageInputKilobitsPerSecond(0); } diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 8446586121..c10d44bfa9 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -70,11 +70,11 @@ public: void incrementConnectionAttempts() { ++_connectionAttempts; } void resetConnectionAttempts() { _connectionAttempts = 0; } - void recordBytesSent(int count); - void recordBytesReceived(int count); + void recordBytesSent(int count) const; + void recordBytesReceived(int count) const; - float getOutboundBandwidth(); // in kbps - float getInboundBandwidth(); // in kbps + float getOutboundBandwidth() const; // in kbps + float getInboundBandwidth() const; // in kbps friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer); friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 0efb8bba7c..9d25724f6c 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -404,6 +404,8 @@ void PacketReceiver::handleVerifiedPacket(std::unique_ptr packet) { if (matchingNode) { emit dataReceived(matchingNode->getType(), nlPacket->getDataSize()); + matchingNode->recordBytesReceived(nlPacket->getDataSize()); + QMetaMethod metaMethod = listener.second; static const QByteArray QSHAREDPOINTER_NODE_NORMALIZED = QMetaObject::normalizedType("QSharedPointer"); diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 0d661ad34a..54342d9cba 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -126,10 +126,6 @@ BasePacket& BasePacket::operator=(BasePacket&& other) { return *this; } -qint64 BasePacket::getDataSize() const { - return (_payloadStart - _packet.get()) + _payloadSize; -} - void BasePacket::setPayloadSize(qint64 payloadSize) { if (isWritable()) { Q_ASSERT(payloadSize <= _payloadCapacity); diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index cc18c75a80..392a7c4384 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -48,7 +48,10 @@ public: const char* getData() const { return _packet.get(); } // Returns the size of the packet, including the header - qint64 getDataSize() const; + qint64 getDataSize() const { return (_payloadStart - _packet.get()) + _payloadSize; } + + // Returns the size of the packet, including the header AND the UDP/IP header + qint64 getWireSize() const { return getDataSize() + UDP_IPV4_HEADER_SIZE; } // Returns the size of the payload only qint64 getPayloadSize() const { return _payloadSize; } diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index e8b22f6ab8..96d73676f0 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -52,15 +52,14 @@ Connection::~Connection() { } void Connection::stopSendQueue() { - if (_sendQueue) { + if (auto sendQueue = _sendQueue.release()) { // grab the send queue thread so we can wait on it - QThread* sendQueueThread = _sendQueue->thread(); + QThread* sendQueueThread = sendQueue->thread(); // tell the send queue to stop and be deleted - _sendQueue->stop(); - _sendQueue->deleteLater(); - _sendQueue.release(); + sendQueue->stop(); + sendQueue->deleteLater(); // since we're stopping the send queue we should consider our handshake ACK not receieved _hasReceivedHandshakeACK = false; @@ -858,37 +857,22 @@ void PendingReceivedMessage::enqueuePacket(std::unique_ptr packet) { "PendingReceivedMessage::enqueuePacket", "called with a packet that is not part of a message"); - if (_isComplete) { - qCDebug(networking) << "UNEXPECTED: Received packet for a message that is already complete"; - return; - } - - auto sequenceNumber = packet->getSequenceNumber(); - - if (packet->getPacketPosition() == Packet::PacketPosition::FIRST) { - _hasFirstSequenceNumber = true; - _firstSequenceNumber = sequenceNumber; - } else if (packet->getPacketPosition() == Packet::PacketPosition::LAST) { - _hasLastSequenceNumber = true; - _lastSequenceNumber = sequenceNumber; - } else if (packet->getPacketPosition() == Packet::PacketPosition::ONLY) { - _hasFirstSequenceNumber = true; - _hasLastSequenceNumber = true; - _firstSequenceNumber = sequenceNumber; - _lastSequenceNumber = sequenceNumber; + if (packet->getPacketPosition() == Packet::PacketPosition::LAST || + packet->getPacketPosition() == Packet::PacketPosition::ONLY) { + _hasLastPacket = true; + _numPackets = packet->getMessagePartNumber() + 1; } // Insert into the packets list in sorted order. Because we generally expect to receive packets in order, begin // searching from the end of the list. - auto it = find_if(_packets.rbegin(), _packets.rend(), - [&](const std::unique_ptr& packet) { return sequenceNumber > packet->getSequenceNumber(); }); + auto messagePartNumber = packet->getMessagePartNumber(); + auto it = std::find_if(_packets.rbegin(), _packets.rend(), + [&](const std::unique_ptr& value) { return messagePartNumber >= value->getMessagePartNumber(); }); - _packets.insert(it.base(), std::move(packet)); - - if (_hasFirstSequenceNumber && _hasLastSequenceNumber) { - auto numPackets = udt::seqlen(_firstSequenceNumber, _lastSequenceNumber); - if (uint64_t(numPackets) == _packets.size()) { - _isComplete = true; - } + if (it != _packets.rend() && ((*it)->getMessagePartNumber() == messagePartNumber)) { + qCDebug(networking) << "PendingReceivedMessage::enqueuePacket: This is a duplicate packet"; + return; } + + _packets.insert(it.base(), std::move(packet)); } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 13756c12f9..7f9978c326 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -37,16 +37,13 @@ class Socket; class PendingReceivedMessage { public: void enqueuePacket(std::unique_ptr packet); - bool isComplete() const { return _isComplete; } + bool isComplete() const { return _hasLastPacket && _numPackets == _packets.size(); } std::list> _packets; private: - bool _isComplete { false }; - bool _hasFirstSequenceNumber { false }; - bool _hasLastSequenceNumber { false }; - SequenceNumber _firstSequenceNumber; - SequenceNumber _lastSequenceNumber; + bool _hasLastPacket { false }; + unsigned int _numPackets { 0 }; }; class Connection : public QObject { diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index e04a836ca8..84cd6b2486 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -13,16 +13,13 @@ #define hifi_ConnectionStats_h #include -#include +#include namespace udt { class ConnectionStats { public: struct Stats { - std::chrono::microseconds startTime; - std::chrono::microseconds endTime; - enum Event { SentACK, ReceivedACK, @@ -41,8 +38,14 @@ public: NumEvents }; + using microseconds = std::chrono::microseconds; + using Events = std::array; + + microseconds startTime; + microseconds endTime; + // construct a vector for the events of the size of our Enum - default value is zero - std::vector events = std::vector((int) Event::NumEvents, 0); + Events events; // packet counts and sizes int sentPackets { 0 }; @@ -66,6 +69,9 @@ public: int rtt { 0 }; int congestionWindowSize { 0 }; int packetSendPeriod { 0 }; + + // TODO: Remove once Win build supports brace initialization: `Events events {{ 0 }};` + Stats() { events.fill(0); } }; ConnectionStats(); diff --git a/libraries/networking/src/udt/Constants.h b/libraries/networking/src/udt/Constants.h index 0152444f84..9f5f1db883 100644 --- a/libraries/networking/src/udt/Constants.h +++ b/libraries/networking/src/udt/Constants.h @@ -17,8 +17,9 @@ #include "SequenceNumber.h" namespace udt { + static const int UDP_IPV4_HEADER_SIZE = 28; static const int MAX_PACKET_SIZE_WITH_UDP_HEADER = 1500; - static const int MAX_PACKET_SIZE = MAX_PACKET_SIZE_WITH_UDP_HEADER - 28; + static const int MAX_PACKET_SIZE = MAX_PACKET_SIZE_WITH_UDP_HEADER - UDP_IPV4_HEADER_SIZE; static const int MAX_PACKETS_IN_FLIGHT = 25600; static const int CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS = 8192; static const int CONNECTION_SEND_BUFFER_SIZE_PACKETS = 8192; diff --git a/libraries/networking/src/udt/LossList.h b/libraries/networking/src/udt/LossList.h index f0f2b92988..3921364872 100644 --- a/libraries/networking/src/udt/LossList.h +++ b/libraries/networking/src/udt/LossList.h @@ -37,6 +37,7 @@ public: void remove(SequenceNumber start, SequenceNumber end); int getLength() const { return _length; } + bool isEmpty() const { return _length == 0; } SequenceNumber getFirstSequenceNumber() const; SequenceNumber popFirstSequenceNumber(); diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index 56c65e0657..d06ff9707e 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -15,7 +15,7 @@ using namespace udt; int Packet::localHeaderSize(bool isPartOfMessage) { return sizeof(Packet::SequenceNumberAndBitField) + - (isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) : 0); + (isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0); } int Packet::totalHeaderSize(bool isPartOfMessage) { @@ -109,9 +109,11 @@ Packet& Packet::operator=(Packet&& other) { return *this; } -void Packet::writeMessageNumber(MessageNumber messageNumber) { +void Packet::writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber) { _isPartOfMessage = true; _messageNumber = messageNumber; + _packetPosition = position; + _messagePartNumber = messagePartNumber; writeHeader(); } @@ -124,7 +126,8 @@ static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BIT static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 3); static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK; -static const uint32_t PACKET_POSITION_MASK = uint32_t(0x03) << 30; +static const uint8_t PACKET_POSITION_OFFSET = 30; +static const uint32_t PACKET_POSITION_MASK = uint32_t(0x03) << PACKET_POSITION_OFFSET; static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK; void Packet::readHeader() const { @@ -139,7 +142,10 @@ void Packet::readHeader() const { if (_isPartOfMessage) { MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1; _messageNumber = *messageNumberAndBitField & MESSAGE_NUMBER_MASK; - _packetPosition = static_cast(*messageNumberAndBitField >> 30); + _packetPosition = static_cast(*messageNumberAndBitField >> PACKET_POSITION_OFFSET); + + MessagePartNumber* messagePartNumber = messageNumberAndBitField + 1; + _messagePartNumber = *messagePartNumber; } } @@ -164,6 +170,9 @@ void Packet::writeHeader() const { MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1; *messageNumberAndBitField = _messageNumber; - *messageNumberAndBitField |= _packetPosition << 30; + *messageNumberAndBitField |= _packetPosition << PACKET_POSITION_OFFSET; + + MessagePartNumber* messagePartNumber = messageNumberAndBitField + 1; + *messagePartNumber = _messagePartNumber; } } diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index 565fc24616..24b9144672 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -28,9 +28,10 @@ public: // NOTE: The SequenceNumber is only actually 29 bits to leave room for a bit field using SequenceNumberAndBitField = uint32_t; - // NOTE: The MessageNumber is only actually 29 bits to leave room for a bit field + // NOTE: The MessageNumber is only actually 30 bits to leave room for a bit field using MessageNumber = uint32_t; using MessageNumberAndBitField = uint32_t; + using MessagePartNumber = uint32_t; // Use same size as MessageNumberAndBitField so we can use the enum with bitwise operations enum PacketPosition : MessageNumberAndBitField { @@ -55,14 +56,13 @@ public: bool isPartOfMessage() const { return _isPartOfMessage; } bool isReliable() const { return _isReliable; } - SequenceNumber getSequenceNumber() const { return _sequenceNumber; } - - MessageNumber getMessageNumber() const { return _messageNumber; } - - void setPacketPosition(PacketPosition position) { _packetPosition = position; } - PacketPosition getPacketPosition() const { return _packetPosition; } - void writeMessageNumber(MessageNumber messageNumber); + SequenceNumber getSequenceNumber() const { return _sequenceNumber; } + MessageNumber getMessageNumber() const { return _messageNumber; } + PacketPosition getPacketPosition() const { return _packetPosition; } + MessagePartNumber getMessagePartNumber() const { return _messagePartNumber; } + + void writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber); void writeSequenceNumber(SequenceNumber sequenceNumber) const; protected: @@ -83,9 +83,10 @@ private: // Simple holders to prevent multiple reading and bitwise ops mutable bool _isReliable { false }; mutable bool _isPartOfMessage { false }; - mutable SequenceNumber _sequenceNumber; - mutable PacketPosition _packetPosition { PacketPosition::ONLY }; + mutable SequenceNumber _sequenceNumber { 0 }; mutable MessageNumber _messageNumber { 0 }; + mutable PacketPosition _packetPosition { PacketPosition::ONLY }; + mutable MessagePartNumber _messagePartNumber { 0 }; }; } // namespace udt diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ca2a0f1a83..3c1d33deaf 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -41,9 +41,8 @@ PacketVersion versionForPacketType(PacketType packetType) { return VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP; case PacketType::AvatarData: case PacketType::BulkAvatarData: - return 15; default: - return 14; + return 16; } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 25a99d65f7..3654d5b5fa 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -141,6 +141,7 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_RADIUS_PROPERTIES = 41; const PacketVersion VERSION_ENTITIES_PARTICLE_COLOR_PROPERTIES = 42; const PacketVersion VERSION_ENTITIES_PROTOCOL_HEADER_SWAP = 43; const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; -const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 45; +const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; +const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; #endif // hifi_PacketHeaders_h diff --git a/libraries/networking/src/udt/PacketList.cpp b/libraries/networking/src/udt/PacketList.cpp index 53edfb32f1..ffe2b3eeba 100644 --- a/libraries/networking/src/udt/PacketList.cpp +++ b/libraries/networking/src/udt/PacketList.cpp @@ -105,7 +105,7 @@ std::unique_ptr PacketList::createPacketWithExtendedHeader() { } void PacketList::closeCurrentPacket(bool shouldSendEmpty) { - if (shouldSendEmpty && !_currentPacket) { + if (shouldSendEmpty && !_currentPacket && _packets.empty()) { _currentPacket = createPacketWithExtendedHeader(); } @@ -132,6 +132,24 @@ QByteArray PacketList::getMessage() { return data; } +void PacketList::preparePackets(MessageNumber messageNumber) { + Q_ASSERT(_packets.size() > 0); + + if (_packets.size() == 1) { + _packets.front()->writeMessageNumber(messageNumber, Packet::PacketPosition::ONLY, 0); + } else { + const auto second = ++_packets.begin(); + const auto last = --_packets.end(); + Packet::MessagePartNumber messagePartNumber = 0; + std::for_each(second, last, [&](const PacketPointer& packet) { + packet->writeMessageNumber(messageNumber, Packet::PacketPosition::MIDDLE, ++messagePartNumber); + }); + + _packets.front()->writeMessageNumber(messageNumber, Packet::PacketPosition::FIRST, 0); + _packets.back()->writeMessageNumber(messageNumber, Packet::PacketPosition::LAST, ++messagePartNumber); + } +} + qint64 PacketList::writeData(const char* data, qint64 maxSize) { auto sizeRemaining = maxSize; diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 7978e77ad7..5337094d1f 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -28,28 +28,29 @@ class Packet; class PacketList : public QIODevice { Q_OBJECT public: + using MessageNumber = uint32_t; + using PacketPointer = std::unique_ptr; + static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); static std::unique_ptr fromReceivedPackets(std::list>&& packets); + PacketType getType() const { return _packetType; } bool isReliable() const { return _isReliable; } bool isOrdered() const { return _isOrdered; } + int getNumPackets() const { return _packets.size() + (_currentPacket ? 1 : 0); } + size_t getDataSize() const; + size_t getMessageSize() const; + QByteArray getMessage(); + + QByteArray getExtendedHeader() const { return _extendedHeader; } + void startSegment(); void endSegment(); - PacketType getType() const { return _packetType; } - int getNumPackets() const { return _packets.size() + (_currentPacket ? 1 : 0); } - - QByteArray getExtendedHeader() const { return _extendedHeader; } - - size_t getDataSize() const; - size_t getMessageSize() const; - void closeCurrentPacket(bool shouldSendEmpty = false); - QByteArray getMessage(); - // QIODevice virtual functions virtual bool isSequential() const { return false; } virtual qint64 size() const { return getDataSize(); } @@ -60,6 +61,8 @@ public: protected: PacketList(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); PacketList(PacketList&& other); + + void preparePackets(MessageNumber messageNumber); virtual qint64 writeData(const char* data, qint64 maxSize); // Not implemented, added an assert so that it doesn't get used by accident @@ -70,6 +73,7 @@ protected: private: friend class ::LimitedNodeList; + friend class PacketQueue; friend class SendQueue; friend class Socket; diff --git a/libraries/networking/src/udt/PacketQueue.cpp b/libraries/networking/src/udt/PacketQueue.cpp new file mode 100644 index 0000000000..1c104e1427 --- /dev/null +++ b/libraries/networking/src/udt/PacketQueue.cpp @@ -0,0 +1,72 @@ +// +// PacketQueue.cpp +// libraries/networking/src/udt +// +// Created by Clement on 9/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PacketQueue.h" + +#include "PacketList.h" + +using namespace udt; + +MessageNumber PacketQueue::getNextMessageNumber() { + static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_BITS; + _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; + return _currentMessageNumber; +} + +bool PacketQueue::isEmpty() const { + LockGuard locker(_packetsLock); + // Only the main channel and it is empty + return (_channels.size() == 1) && _channels.front().empty(); +} + +PacketQueue::PacketPointer PacketQueue::takePacket() { + LockGuard locker(_packetsLock); + if (isEmpty()) { + return PacketPointer(); + } + + // Find next non empty channel + if (_channels[nextIndex()].empty()) { + nextIndex(); + } + auto& channel = _channels[_currentIndex]; + Q_ASSERT(!channel.empty()); + + // Take front packet + auto packet = std::move(channel.front()); + channel.pop_front(); + + // Remove now empty channel (Don't remove the main channel) + if (channel.empty() && _currentIndex != 0) { + channel.swap(_channels.back()); + _channels.pop_back(); + --_currentIndex; + } + + return std::move(packet); +} + +unsigned int PacketQueue::nextIndex() { + _currentIndex = (++_currentIndex) % _channels.size(); + return _currentIndex; +} + +void PacketQueue::queuePacket(PacketPointer packet) { + LockGuard locker(_packetsLock); + _channels.front().push_back(std::move(packet)); +} + +void PacketQueue::queuePacketList(PacketListPointer packetList) { + packetList->preparePackets(getNextMessageNumber()); + + LockGuard locker(_packetsLock); + _channels.push_back(std::move(packetList->_packets)); +} diff --git a/libraries/networking/src/udt/PacketQueue.h b/libraries/networking/src/udt/PacketQueue.h new file mode 100644 index 0000000000..69784fd8db --- /dev/null +++ b/libraries/networking/src/udt/PacketQueue.h @@ -0,0 +1,59 @@ +// +// PacketQueue.h +// libraries/networking/src/udt +// +// Created by Clement on 9/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PacketQueue_h +#define hifi_PacketQueue_h + +#include +#include +#include +#include + +#include "Packet.h" + +namespace udt { + +class PacketList; + +using MessageNumber = uint32_t; + +class PacketQueue { + using Mutex = std::recursive_mutex; + using LockGuard = std::lock_guard; + using PacketPointer = std::unique_ptr; + using PacketListPointer = std::unique_ptr; + using Channel = std::list; + using Channels = std::vector; + +public: + void queuePacket(PacketPointer packet); + void queuePacketList(PacketListPointer packetList); + + bool isEmpty() const; + PacketPointer takePacket(); + + Mutex& getLock() { return _packetsLock; } + +private: + MessageNumber getNextMessageNumber(); + unsigned int nextIndex(); + + MessageNumber _currentMessageNumber { 0 }; + + mutable Mutex _packetsLock; // Protects the packets to be sent. + Channels _channels = Channels(1); // One channel per packet list + Main channel + unsigned int _currentIndex { 0 }; +}; + +} + + +#endif // hifi_PacketQueue_h \ No newline at end of file diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index ac6c8238e8..d7e3bb2b65 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -28,9 +28,12 @@ using namespace udt; +template class DoubleLock { public: - DoubleLock(std::mutex& mutex1, std::mutex& mutex2) : _mutex1(mutex1), _mutex2(mutex2) { } + using Lock = std::unique_lock>; + + DoubleLock(Mutex1& mutex1, Mutex2& mutex2) : _mutex1(mutex1), _mutex2(mutex2) { } DoubleLock(const DoubleLock&) = delete; DoubleLock& operator=(const DoubleLock&) = delete; @@ -45,15 +48,15 @@ public: void unlock() { _mutex1.unlock(); _mutex2.unlock(); } private: - std::mutex& _mutex1; - std::mutex& _mutex2; + Mutex1& _mutex1; + Mutex2& _mutex2; }; std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destination) { - auto queue = std::unique_ptr(new SendQueue(socket, destination)); - Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*"); + auto queue = std::unique_ptr(new SendQueue(socket, destination)); + // Setup queue private thread QThread* thread = new QThread; thread->setObjectName("Networking: SendQueue " + destination.objectName()); // Name thread for easier debug @@ -68,28 +71,20 @@ std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destin thread->start(); - return std::move(queue); + return queue; } SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) : _socket(socket), _destination(dest) { - } void SendQueue::queuePacket(std::unique_ptr packet) { - { - std::unique_lock locker(_packetsLock); - - _packets.push_back(std::move(packet)); - - // unlock the mutex before we notify - locker.unlock(); - - // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets - _emptyCondition.notify_one(); - } + _packets.queuePacket(std::move(packet)); + + // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets + _emptyCondition.notify_one(); if (!this->thread()->isRunning() && _state == State::NotStarted) { this->thread()->start(); @@ -97,46 +92,10 @@ void SendQueue::queuePacket(std::unique_ptr packet) { } void SendQueue::queuePacketList(std::unique_ptr packetList) { - Q_ASSERT(packetList->_packets.size() > 0); - - { - auto messageNumber = getNextMessageNumber(); - - if (packetList->_packets.size() == 1) { - auto& packet = packetList->_packets.front(); - - packet->setPacketPosition(Packet::PacketPosition::ONLY); - packet->writeMessageNumber(messageNumber); - } else { - bool haveMarkedFirstPacket = false; - auto end = packetList->_packets.end(); - auto lastElement = --packetList->_packets.end(); - for (auto it = packetList->_packets.begin(); it != end; ++it) { - auto& packet = *it; - - if (!haveMarkedFirstPacket) { - packet->setPacketPosition(Packet::PacketPosition::FIRST); - haveMarkedFirstPacket = true; - } else if (it == lastElement) { - packet->setPacketPosition(Packet::PacketPosition::LAST); - } else { - packet->setPacketPosition(Packet::PacketPosition::MIDDLE); - } - - packet->writeMessageNumber(messageNumber); - } - } - - std::unique_lock locker(_packetsLock); - - _packets.splice(_packets.end(), packetList->_packets); - - // unlock the mutex so we can notify - locker.unlock(); - - // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets - _emptyCondition.notify_one(); - } + _packets.queuePacketList(std::move(packetList)); + + // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets + _emptyCondition.notify_one(); if (!this->thread()->isRunning() && _state == State::NotStarted) { this->thread()->start(); @@ -147,10 +106,8 @@ void SendQueue::stop() { _state = State::Stopped; - // in case we're waiting to send another handshake, release the condition_variable now so we cleanup sooner + // Notify all conditions in case we're waiting somewhere _handshakeACKCondition.notify_one(); - - // in case the empty condition is waiting for packets/loss release it now so that the queue is cleaned up _emptyCondition.notify_one(); } @@ -178,7 +135,7 @@ void SendQueue::ack(SequenceNumber ack) { { // remove any sequence numbers equal to or lower than this ACK in the loss list std::lock_guard nakLocker(_naksLock); - if (_naks.getLength() > 0 && _naks.getFirstSequenceNumber() <= ack) { + if (!_naks.isEmpty() && _naks.getFirstSequenceNumber() <= ack) { _naks.remove(_naks.getFirstSequenceNumber(), ack); } } @@ -191,12 +148,10 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) { _timeoutExpiryCount = 0; _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); - std::unique_lock nakLocker(_naksLock); - - _naks.insert(start, end); - - // unlock the locked mutex before we notify - nakLocker.unlock(); + { + std::lock_guard nakLocker(_naksLock); + _naks.insert(start, end); + } // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for losses to re-send _emptyCondition.notify_one(); @@ -207,36 +162,47 @@ void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) { _timeoutExpiryCount = 0; _lastReceiverResponse = uint64_t(QDateTime::currentMSecsSinceEpoch()); - std::unique_lock nakLocker(_naksLock); - _naks.clear(); - - SequenceNumber first, second; - while (packet.bytesLeftToRead() >= (qint64)(2 * sizeof(SequenceNumber))) { - packet.readPrimitive(&first); - packet.readPrimitive(&second); + { + std::lock_guard nakLocker(_naksLock); + _naks.clear(); - if (first == second) { - _naks.append(first); - } else { - _naks.append(first, second); + SequenceNumber first, second; + while (packet.bytesLeftToRead() >= (qint64)(2 * sizeof(SequenceNumber))) { + packet.readPrimitive(&first); + packet.readPrimitive(&second); + + if (first == second) { + _naks.append(first); + } else { + _naks.append(first, second); + } } } - // unlock the mutex before we notify - nakLocker.unlock(); - // call notify_one on the condition_variable_any in case the send thread is sleeping waiting for losses to re-send _emptyCondition.notify_one(); } +void SendQueue::sendHandshake() { + std::unique_lock handshakeLock { _handshakeMutex }; + if (!_hasReceivedHandshakeACK) { + // we haven't received a handshake ACK from the client, send another now + static const auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, 0); + _socket->writeBasePacket(*handshakePacket, _destination); + + // we wait for the ACK or the re-send interval to expire + static const auto HANDSHAKE_RESEND_INTERVAL = std::chrono::milliseconds(100); + _handshakeACKCondition.wait_for(handshakeLock, HANDSHAKE_RESEND_INTERVAL); + } +} + void SendQueue::handshakeACK() { - std::unique_lock locker { _handshakeMutex }; - - _hasReceivedHandshakeACK = true; - - // unlock the mutex and notify on the handshake ACK condition - locker.unlock(); + { + std::lock_guard locker { _handshakeMutex }; + _hasReceivedHandshakeACK = true; + } + // Notify on the handshake ACK condition _handshakeACKCondition.notify_one(); } @@ -245,12 +211,6 @@ SequenceNumber SendQueue::getNextSequenceNumber() { return _currentSequenceNumber; } -uint32_t SendQueue::getNextMessageNumber() { - static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_BITS; - _currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER; - return _currentMessageNumber; -} - void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber) { // write the sequence number and send the packet newPacket->writeSequenceNumber(sequenceNumber); @@ -287,207 +247,88 @@ void SendQueue::run() { _state = State::Running; + // Wait for handshake to be complete + while (_state == State::Running && !_hasReceivedHandshakeACK) { + sendHandshake(); + + // Keep processing events + QCoreApplication::sendPostedEvents(this); + + // Once we're here we've either received the handshake ACK or it's going to be time to re-send a handshake. + // Either way let's continue processing - no packets will be sent if no handshake ACK has been received. + } + while (_state == State::Running) { // Record how long the loop takes to execute - auto loopStartTimestamp = p_high_resolution_clock::now(); - - std::unique_lock handshakeLock { _handshakeMutex }; - - if (!_hasReceivedHandshakeACK) { - // we haven't received a handshake ACK from the client - // if it has been at least 100ms since we last sent a handshake, send another now - - static const auto HANDSHAKE_RESEND_INTERVAL_MS = std::chrono::milliseconds(100); - - // hold the time of last send in a static - static auto lastSendHandshake = p_high_resolution_clock::now() - HANDSHAKE_RESEND_INTERVAL_MS; - - if (p_high_resolution_clock::now() - lastSendHandshake >= HANDSHAKE_RESEND_INTERVAL_MS) { - - // it has been long enough since last handshake, send another - static auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, 0); - _socket->writeBasePacket(*handshakePacket, _destination); - - lastSendHandshake = p_high_resolution_clock::now(); - } - - // we wait for the ACK or the re-send interval to expire - _handshakeACKCondition.wait_until(handshakeLock, p_high_resolution_clock::now() + HANDSHAKE_RESEND_INTERVAL_MS); - - // Once we're here we've either received the handshake ACK or it's going to be time to re-send a handshake. - // Either way let's continue processing - no packets will be sent if no handshake ACK has been received. - } - - handshakeLock.unlock(); + const auto loopStartTimestamp = p_high_resolution_clock::now(); bool sentAPacket = maybeResendPacket(); // if we didn't find a packet to re-send AND we think we can fit a new packet on the wire // (this is according to the current flow window size) then we send out a new packet - if (_hasReceivedHandshakeACK && !sentAPacket) { - if (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) <= _flowWindowSize) { - sentAPacket = maybeSendNewPacket(); - } + if (!sentAPacket) { + sentAPacket = maybeSendNewPacket(); } // since we're a while loop, give the thread a chance to process events - QCoreApplication::sendPostedEvents(this, 0); + QCoreApplication::sendPostedEvents(this); // we just processed events so check now if we were just told to stop - if (_state != State::Running) { + // If the send queue has been innactive, skip the sleep for + // Either _isRunning will have been set to false and we'll break + // Or something happened and we'll keep going + if (_state != State::Running || isInactive(sentAPacket)) { return; } - if (_hasReceivedHandshakeACK && !sentAPacket) { - // check if it is time to break this connection - - // that will be the case if we have had 16 timeouts since hearing back from the client, and it has been - // at least 5 seconds - - static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16; - static const int MIN_SECONDS_BEFORE_INACTIVE_MS = 5 * 1000; - - auto sinceEpochNow = QDateTime::currentMSecsSinceEpoch(); - - if (_timeoutExpiryCount >= NUM_TIMEOUTS_BEFORE_INACTIVE - && (sinceEpochNow - _lastReceiverResponse) > MIN_SECONDS_BEFORE_INACTIVE_MS) { - // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, - // then signal the queue is inactive and return so it can be cleaned up - -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "SendQueue to" << _destination << "reached" << NUM_TIMEOUTS_BEFORE_INACTIVE << "timeouts" - << "and 10s before receiving any ACK/NAK and is now inactive. Stopping."; -#endif - - deactivate(); - - return; - } else { - // During our processing above we didn't send any packets - - // If that is still the case we should use a condition_variable_any to sleep until we have data to handle. - // To confirm that the queue of packets and the NAKs list are still both empty we'll need to use the DoubleLock - DoubleLock doubleLock(_packetsLock, _naksLock); - - if (doubleLock.try_lock()) { - // The packets queue and loss list mutexes are now both locked - check if they're still both empty - - if (_packets.empty() && _naks.getLength() == 0) { - if (uint32_t(_lastACKSequenceNumber) == uint32_t(_currentSequenceNumber)) { - // we've sent the client as much data as we have (and they've ACKed it) - // either wait for new data to send or 5 seconds before cleaning up the queue - static const auto EMPTY_QUEUES_INACTIVE_TIMEOUT = std::chrono::seconds(5); - - // use our condition_variable_any to wait - auto cvStatus = _emptyCondition.wait_for(doubleLock, EMPTY_QUEUES_INACTIVE_TIMEOUT); - - // we have the double lock again - Make sure to unlock it - doubleLock.unlock(); - - if (cvStatus == std::cv_status::timeout) { -#ifdef UDT_CONNECTION_DEBUG - qCDebug(networking) << "SendQueue to" << _destination << "has been empty for" - << EMPTY_QUEUES_INACTIVE_TIMEOUT.count() - << "seconds and receiver has ACKed all packets." - << "The queue is now inactive and will be stopped."; -#endif - - deactivate(); - - return; - } - } else { - // We think the client is still waiting for data (based on the sequence number gap) - // Let's wait either for a response from the client or until the estimated timeout - auto waitDuration = std::chrono::microseconds(_estimatedTimeout); - - // use our condition_variable_any to wait - auto cvStatus = _emptyCondition.wait_for(doubleLock, waitDuration); - - if (cvStatus == std::cv_status::timeout) { - // increase the number of timeouts - ++_timeoutExpiryCount; - - if (SequenceNumber(_lastACKSequenceNumber) < _currentSequenceNumber) { - // after a timeout if we still have sent packets that the client hasn't ACKed we - // add them to the loss list - - // Note that thanks to the DoubleLock we have the _naksLock right now - _naks.append(SequenceNumber(_lastACKSequenceNumber) + 1, _currentSequenceNumber); - } - } - - // we have the double lock again - Make sure to unlock it - doubleLock.unlock(); - - // skip to the next iteration - continue; - } - } else { - // we got the try_lock but failed the other conditionals so we need to unlock - doubleLock.unlock(); - } - } - } - } - - auto loopEndTimestamp = p_high_resolution_clock::now(); - // sleep as long as we need until next packet send, if we can - auto timeToSleep = (loopStartTimestamp + std::chrono::microseconds(_packetSendPeriod)) - loopEndTimestamp; - if (timeToSleep > timeToSleep.zero()) { - std::this_thread::sleep_for(timeToSleep); - } + const auto loopEndTimestamp = p_high_resolution_clock::now(); + const auto timeToSleep = (loopStartTimestamp + std::chrono::microseconds(_packetSendPeriod)) - loopEndTimestamp; + std::this_thread::sleep_for(timeToSleep); } } bool SendQueue::maybeSendNewPacket() { - // we didn't re-send a packet, so time to send a new one - std::unique_lock locker(_packetsLock); - - if (_packets.size() > 0) { - SequenceNumber nextNumber = getNextSequenceNumber(); + if (seqlen(SequenceNumber { (uint32_t) _lastACKSequenceNumber }, _currentSequenceNumber) <= _flowWindowSize) { + // we didn't re-send a packet, so time to send a new one - // grab the first packet we will send - std::unique_ptr firstPacket; - firstPacket.swap(_packets.front()); - _packets.pop_front(); - std::unique_ptr secondPacket; - bool shouldSendPairTail = false; - - if (((uint32_t) nextNumber & 0xF) == 0) { - // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets - // pull off a second packet if we can before we unlock - shouldSendPairTail = true; + if (!_packets.isEmpty()) { + SequenceNumber nextNumber = getNextSequenceNumber(); - if (_packets.size() > 0) { - secondPacket.swap(_packets.front()); - _packets.pop_front(); + // grab the first packet we will send + std::unique_ptr firstPacket = _packets.takePacket(); + Q_ASSERT(firstPacket); + + std::unique_ptr secondPacket; + bool shouldSendPairTail = false; + + if (((uint32_t) nextNumber & 0xF) == 0) { + // the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets + // pull off a second packet if we can before we unlock + shouldSendPairTail = true; + + secondPacket = _packets.takePacket(); } + + // definitely send the first packet + sendNewPacketAndAddToSentList(move(firstPacket), nextNumber); + + // do we have a second in a pair to send as well? + if (secondPacket) { + sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); + } else if (shouldSendPairTail) { + // we didn't get a second packet to send in the probe pair + // send a control packet of type ProbePairTail so the receiver can still do + // proper bandwidth estimation + static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); + _socket->writeBasePacket(*pairTailPacket, _destination); + } + + // We sent our packet(s), return here + return true; } - - // unlock the packets, we're done pulling - locker.unlock(); - - // definitely send the first packet - sendNewPacketAndAddToSentList(move(firstPacket), nextNumber); - - // do we have a second in a pair to send as well? - if (secondPacket) { - sendNewPacketAndAddToSentList(move(secondPacket), getNextSequenceNumber()); - } else if (shouldSendPairTail) { - // we didn't get a second packet to send in the probe pair - // send a control packet of type ProbePairTail so the receiver can still do - // proper bandwidth estimation - static auto pairTailPacket = ControlPacket::create(ControlPacket::ProbeTail); - _socket->writeBasePacket(*pairTailPacket, _destination); - } - - // We sent our packet(s), return here - return true; } - // No packets were sent return false; } @@ -499,7 +340,7 @@ bool SendQueue::maybeResendPacket() { std::unique_lock naksLocker(_naksLock); - if (_naks.getLength() > 0) { + if (!_naks.isEmpty()) { // pull the sequence number we need to re-send SequenceNumber resendNumber = _naks.popFirstSequenceNumber(); naksLocker.unlock(); @@ -538,6 +379,89 @@ bool SendQueue::maybeResendPacket() { return false; } +bool SendQueue::isInactive(bool sentAPacket) { + if (!sentAPacket) { + // check if it is time to break this connection + + // that will be the case if we have had 16 timeouts since hearing back from the client, and it has been + // at least 5 seconds + static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16; + static const int MIN_SECONDS_BEFORE_INACTIVE_MS = 5 * 1000; + if (_timeoutExpiryCount >= NUM_TIMEOUTS_BEFORE_INACTIVE && + (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse) > MIN_SECONDS_BEFORE_INACTIVE_MS) { + // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, + // then signal the queue is inactive and return so it can be cleaned up + +#ifdef UDT_CONNECTION_DEBUG + qCDebug(networking) << "SendQueue to" << _destination << "reached" << NUM_TIMEOUTS_BEFORE_INACTIVE << "timeouts" + << "and 5s before receiving any ACK/NAK and is now inactive. Stopping."; +#endif + + deactivate(); + return true; + } + + // During our processing above we didn't send any packets + + // If that is still the case we should use a condition_variable_any to sleep until we have data to handle. + // To confirm that the queue of packets and the NAKs list are still both empty we'll need to use the DoubleLock + using DoubleLock = DoubleLock; + DoubleLock doubleLock(_packets.getLock(), _naksLock); + DoubleLock::Lock locker(doubleLock, std::try_to_lock); + + if (locker.owns_lock() && _packets.isEmpty() && _naks.isEmpty()) { + // The packets queue and loss list mutexes are now both locked and they're both empty + + if (uint32_t(_lastACKSequenceNumber) == uint32_t(_currentSequenceNumber)) { + // we've sent the client as much data as we have (and they've ACKed it) + // either wait for new data to send or 5 seconds before cleaning up the queue + static const auto EMPTY_QUEUES_INACTIVE_TIMEOUT = std::chrono::seconds(5); + + // use our condition_variable_any to wait + auto cvStatus = _emptyCondition.wait_for(locker, EMPTY_QUEUES_INACTIVE_TIMEOUT); + + // we have the lock again - Make sure to unlock it + locker.unlock(); + + if (cvStatus == std::cv_status::timeout) { +#ifdef UDT_CONNECTION_DEBUG + qCDebug(networking) << "SendQueue to" << _destination << "has been empty for" + << EMPTY_QUEUES_INACTIVE_TIMEOUT.count() + << "seconds and receiver has ACKed all packets." + << "The queue is now inactive and will be stopped."; +#endif + + // Deactivate queue + deactivate(); + return true; + } + } else { + // We think the client is still waiting for data (based on the sequence number gap) + // Let's wait either for a response from the client or until the estimated timeout + auto waitDuration = std::chrono::microseconds(_estimatedTimeout); + + // use our condition_variable_any to wait + auto cvStatus = _emptyCondition.wait_for(locker, waitDuration); + + if (cvStatus == std::cv_status::timeout) { + // increase the number of timeouts + ++_timeoutExpiryCount; + + if (SequenceNumber(_lastACKSequenceNumber) < _currentSequenceNumber) { + // after a timeout if we still have sent packets that the client hasn't ACKed we + // add them to the loss list + + // Note that thanks to the DoubleLock we have the _naksLock right now + _naks.append(SequenceNumber(_lastACKSequenceNumber) + 1, _currentSequenceNumber); + } + } + } + } + } + + return false; +} + void SendQueue::deactivate() { // this queue is inactive - emit that signal and stop the while emit queueInactive(); diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 88b6b045b0..96de12a971 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -28,6 +28,7 @@ #include "../HifiSockAddr.h" #include "Constants.h" +#include "PacketQueue.h" #include "SequenceNumber.h" #include "LossList.h" @@ -38,8 +39,6 @@ class ControlPacket; class Packet; class PacketList; class Socket; - -using MessageNumber = uint32_t; class SendQueue : public QObject { Q_OBJECT @@ -87,29 +86,29 @@ private: SendQueue(SendQueue& other) = delete; SendQueue(SendQueue&& other) = delete; + void sendHandshake(); + void sendPacket(const Packet& packet); void sendNewPacketAndAddToSentList(std::unique_ptr newPacket, SequenceNumber sequenceNumber); bool maybeSendNewPacket(); // Figures out what packet to send next bool maybeResendPacket(); // Determines whether to resend a packet and which one + bool isInactive(bool sentAPacket); void deactivate(); // makes the queue inactive and cleans it up // Increments current sequence number and return it SequenceNumber getNextSequenceNumber(); - MessageNumber getNextMessageNumber(); - mutable std::mutex _packetsLock; // Protects the packets to be sent list. - std::list> _packets; // List of packets to be sent + PacketQueue _packets; Socket* _socket { nullptr }; // Socket to send packet on HifiSockAddr _destination; // Destination addr std::atomic _lastACKSequenceNumber { 0 }; // Last ACKed sequence number - MessageNumber _currentMessageNumber { 0 }; SequenceNumber _currentSequenceNumber; // Last sequence number sent out - std::atomic _atomicCurrentSequenceNumber { 0 };// Atomic for last sequence number sent out + std::atomic _atomicCurrentSequenceNumber { 0 }; // Atomic for last sequence number sent out std::atomic _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC std::atomic _state { State::NotStarted }; diff --git a/libraries/plugins/src/plugins/PluginContainer.cpp b/libraries/plugins/src/plugins/PluginContainer.cpp index b27f076eb6..8afac745f3 100644 --- a/libraries/plugins/src/plugins/PluginContainer.cpp +++ b/libraries/plugins/src/plugins/PluginContainer.cpp @@ -9,7 +9,17 @@ static PluginContainer* INSTANCE{ nullptr }; +PluginContainer& PluginContainer::getInstance() { + Q_ASSERT(INSTANCE); + return *INSTANCE; +} + PluginContainer::PluginContainer() { Q_ASSERT(!INSTANCE); INSTANCE = this; }; + +PluginContainer::~PluginContainer() { + Q_ASSERT(INSTANCE == this); + INSTANCE = nullptr; +}; diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index f938758161..f013bfe3bf 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -13,10 +13,13 @@ class QAction; class QGLWidget; class QScreen; +class DisplayPlugin; class PluginContainer { public: + static PluginContainer& getInstance(); PluginContainer(); + virtual ~PluginContainer(); virtual void addMenu(const QString& menuName) = 0; virtual void removeMenu(const QString& menuName) = 0; virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; @@ -29,4 +32,5 @@ public: virtual void requestReset() = 0; virtual QGLWidget* getPrimarySurface() = 0; virtual bool isForeground() = 0; + virtual const DisplayPlugin* getActiveDisplayPlugin() const = 0; }; diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index a9cd7db20c..b65289933c 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -29,10 +29,6 @@ class EnvironmentData; /// Interface provided by Application to other objects that need access to the current view state details class AbstractViewStateInterface { public: - - /// Returns the shadow distances for the current view state - virtual const glm::vec3& getShadowDistances() const = 0; - /// gets the current view frustum for rendering the view state virtual ViewFrustum* getCurrentViewFrustum() = 0; @@ -44,8 +40,7 @@ public: virtual ViewFrustum* getShadowViewFrustum() = 0; virtual QThread* getMainThread() = 0; - - virtual bool shouldRenderMesh(float largestDimension, float distanceToCamera) = 0; + virtual float getSizeScale() const = 0; virtual int getBoundaryLevelAdjust() const = 0; virtual PickRay computePickRay(float x, float y) const = 0; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index dbcbe3c05e..e9d2cf4061 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -887,7 +887,7 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL)); - _spotLightMesh->makeBufferStream(); + _spotLightMesh->getVertexStream(); } return _spotLightMesh; } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 7846d95ec4..3fcd556dbb 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -59,7 +59,8 @@ static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(glm::vec3) * 2; // vertices and normals static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3); - +static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT16; +static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint16); void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const VertexVector& vertices) { vertexBuffer->append(vertices); @@ -73,13 +74,13 @@ void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, c void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, const IndexVector& indices, const IndexVector& wireIndices) { _indices = indexBuffer; if (!indices.empty()) { - _indexOffset = indexBuffer->getSize(); + _indexOffset = indexBuffer->getSize() / SHAPE_INDEX_SIZE; _indexCount = indices.size(); indexBuffer->append(indices); } if (!wireIndices.empty()) { - _wireIndexOffset = indexBuffer->getSize(); + _wireIndexOffset = indexBuffer->getSize() / SHAPE_INDEX_SIZE; _wireIndexCount = wireIndices.size(); indexBuffer->append(wireIndices); } @@ -88,7 +89,7 @@ void GeometryCache::ShapeData::setupIndices(gpu::BufferPointer& indexBuffer, con void GeometryCache::ShapeData::setupBatch(gpu::Batch& batch) const { batch.setInputBuffer(gpu::Stream::POSITION, _positionView); batch.setInputBuffer(gpu::Stream::NORMAL, _normalView); - batch.setIndexBuffer(gpu::UINT16, _indices, 0); + batch.setIndexBuffer(SHAPE_INDEX_TYPE, _indices, 0); } void GeometryCache::ShapeData::draw(gpu::Batch& batch) const { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp new file mode 100644 index 0000000000..d1db4e6c7f --- /dev/null +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -0,0 +1,349 @@ +// +// MeshPartPayload.cpp +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MeshPartPayload.h" + +#include + +#include "DeferredLightingEffect.h" + +#include "Model.h" + +namespace render { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); + } + // Return opaque for lack of a better idea + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); + } +} + +using namespace render; + +MeshPartPayload::MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex) : + model(model), meshIndex(meshIndex), partIndex(partIndex), _shapeID(shapeIndex) +{ + initCache(); +} + +void MeshPartPayload::initCache() { + const std::vector>& networkMeshes = model->_geometry->getMeshes(); + const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); + _drawMesh = networkMesh._mesh; + + const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); + const FBXMesh& mesh = geometry.meshes.at(meshIndex); + _hasColorAttrib = !mesh.colors.isEmpty(); + _isBlendShaped = !mesh.blendshapes.isEmpty(); + _isSkinned = !mesh.clusterIndices.isEmpty(); + + + _drawPart = _drawMesh->getPartBuffer().get(partIndex); + + auto networkMaterial = model->_geometry->getShapeMaterial(_shapeID); + if (networkMaterial) { + _drawMaterial = networkMaterial->_material; + }; + +} + +render::ItemKey MeshPartPayload::getKey() const { + ItemKey::Builder builder; + builder.withTypeShape(); + + if (!model->isVisible()) { + builder.withInvisible(); + } + + if (_isBlendShaped || _isSkinned) { + builder.withDeformed(); + } + + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTransparent() || matKey.isTransparentMap()) { + builder.withTransparent(); + } + } + + return builder.build(); +} + +render::Item::Bound MeshPartPayload::getBound() const { + if (_isBoundInvalid) { + model->getPartBounds(meshIndex, partIndex); + _isBoundInvalid = false; + } + return _bound; +} + +void MeshPartPayload::drawCall(gpu::Batch& batch) const { + batch.drawIndexed(gpu::TRIANGLES, _drawPart._numIndices, _drawPart._startIndex); +} + +void MeshPartPayload::bindMesh(gpu::Batch& batch) const { + if (!_isBlendShaped) { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputStream(0, _drawMesh->getVertexStream()); + } else { + batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); + + batch.setInputFormat((_drawMesh->getVertexFormat())); + + batch.setInputBuffer(0, model->_blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, model->_blendedVertexBuffers[meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); + } + + // TODO: Get rid of that extra call + if (!_hasColorAttrib) { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } +} + +void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const { + if (!_drawMaterial) { + return; + } + + auto textureCache = DependencyManager::get(); + + batch.setUniformBuffer(ModelRender::MATERIAL_GPU_SLOT, _drawMaterial->getSchemaBuffer()); + + auto materialKey = _drawMaterial->getKey(); + auto textureMaps = _drawMaterial->getTextureMaps(); + glm::mat4 texcoordTransform[2]; + + // Diffuse + if (materialKey.isDiffuseMap()) { + auto diffuseMap = textureMaps[model::MaterialKey::DIFFUSE_MAP]; + if (diffuseMap && diffuseMap->isDefined()) { + batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, diffuseMap->getTextureView()); + + if (!diffuseMap->getTextureTransform().isIdentity()) { + diffuseMap->getTextureTransform().getMatrix(texcoordTransform[0]); + } + } else { + batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); + } + } else { + batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); + } + + // Normal map + if (materialKey.isNormalMap()) { + auto normalMap = textureMaps[model::MaterialKey::NORMAL_MAP]; + if (normalMap && normalMap->isDefined()) { + batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, normalMap->getTextureView()); + + // texcoord are assumed to be the same has diffuse + } else { + batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, textureCache->getBlueTexture()); + } + } else { + batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, nullptr); + } + + // TODO: For now gloss map is used as the "specular map in the shading, we ll need to fix that + if (materialKey.isGlossMap()) { + auto specularMap = textureMaps[model::MaterialKey::GLOSS_MAP]; + if (specularMap && specularMap->isDefined()) { + batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, specularMap->getTextureView()); + + // texcoord are assumed to be the same has diffuse + } else { + batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, textureCache->getBlackTexture()); + } + } else { + batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, nullptr); + } + + // TODO: For now lightmaop is piped into the emissive map unit, we need to fix that and support for real emissive too + if (materialKey.isLightmapMap()) { + auto lightmapMap = textureMaps[model::MaterialKey::LIGHTMAP_MAP]; + + if (lightmapMap && lightmapMap->isDefined()) { + batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, lightmapMap->getTextureView()); + + auto lightmapOffsetScale = lightmapMap->getLightmapOffsetScale(); + batch._glUniform2f(locations->emissiveParams, lightmapOffsetScale.x, lightmapOffsetScale.y); + + if (!lightmapMap->getTextureTransform().isIdentity()) { + lightmapMap->getTextureTransform().getMatrix(texcoordTransform[1]); + } + } else { + batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, textureCache->getGrayTexture()); + } + } else { + batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, nullptr); + } + + // Texcoord transforms ? + if (locations->texcoordMatrices >= 0) { + batch._glUniformMatrix4fv(locations->texcoordMatrices, 2, false, (const float*)&texcoordTransform); + } +} + +void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { + // Still relying on the raw data from the model + const Model::MeshState& state = model->_meshStates.at(meshIndex); + + Transform transform; + if (state.clusterBuffer) { + batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); + } else { + if (model->_cauterizeBones) { + transform = Transform(state.cauterizedClusterMatrices[0]); + } else { + transform = Transform(state.clusterMatrices[0]); + } + } + transform.preTranslate(model->_translation); + batch.setModelTransform(transform); +} + + +void MeshPartPayload::render(RenderArgs* args) const { + PerformanceTimer perfTimer("MeshPartPayload::render"); + if (!model->_readyWhenAdded) { + return; // bail asap + } + + gpu::Batch& batch = *(args->_batch); + auto mode = args->_renderMode; + + auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME + + const FBXGeometry& geometry = model->_geometry->getFBXGeometry(); + const std::vector>& networkMeshes = model->_geometry->getMeshes(); + + // guard against partially loaded meshes + if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)model->_meshStates.size() ) { + return; + } + + // Back to model to update the cluster matrices right now + model->updateClusterMatrices(); + + const FBXMesh& mesh = geometry.meshes.at(meshIndex); + + // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown + // to false to rebuild out mesh groups. + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { + model->_meshGroupsKnown = false; // regenerate these lists next time around. + model->_readyWhenAdded = false; // in case any of our users are using scenes + model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + return; // FIXME! + } + + + int vertexCount = mesh.vertices.size(); + if (vertexCount == 0) { + // sanity check + return; // FIXME! + } + + + // guard against partially loaded meshes + if (partIndex >= mesh.parts.size()) { + return; + } + + model::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); + + bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); + bool hasSpecular = drawMaterialKey.isGlossMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + bool isSkinned = _isSkinned; + bool wireframe = model->isWireframe(); + + // render the part bounding box +#ifdef DEBUG_BOUNDING_PARTS + { + AABox partBounds = getPartBounds(meshIndex, partIndex); + bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; + + glm::vec4 cubeColor; + if (isSkinned) { + cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); + } else if (inView) { + cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); + } else { + cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + } + + Transform transform; + transform.setTranslation(partBounds.calcCenter()); + transform.setScale(partBounds.getDimensions()); + batch.setModelTransform(transform); + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } +#endif //def DEBUG_BOUNDING_PARTS + + if (wireframe) { + translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; + } + + ModelRender::Locations* locations = nullptr; + ModelRender::pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, + args, locations); + + + // Bind the model transform and the skinCLusterMatrices if needed + bindTransform(batch, locations); + + //Bind the index buffer and vertex buffer and Blend shapes if needed + bindMesh(batch); + + // apply material properties + bindMaterial(batch, locations); + + + // TODO: We should be able to do that just in the renderTransparentJob + if (translucentMesh && locations->lightBufferUnit >= 0) { + PerformanceTimer perfTimer("DLE->setupTransparent()"); + + DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); + } + if (args) { + args->_details._materialSwitches++; + } + + // Draw! + { + PerformanceTimer perfTimer("batch.drawIndexed()"); + drawCall(batch); + } + + if (args) { + const int INDICES_PER_TRIANGLE = 3; + args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; + } +} + diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h new file mode 100644 index 0000000000..7e476445e6 --- /dev/null +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -0,0 +1,69 @@ +// +// MeshPartPayload.h +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MeshPartPayload_h +#define hifi_MeshPartPayload_h + +#include + +#include + +#include + +#include "ModelRender.h" + +class Model; + +class MeshPartPayload { +public: + MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex); + + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + Model* model; + int meshIndex; + int partIndex; + int _shapeID; + + // Render Item interface + render::ItemKey getKey() const; + render::Item::Bound getBound() const; + void render(RenderArgs* args) const; + + // MeshPartPayload functions to perform render + void drawCall(gpu::Batch& batch) const; + void bindMesh(gpu::Batch& batch) const; + void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; + void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; + + + void initCache(); + + // Payload resource cached values + model::MeshPointer _drawMesh; + model::Mesh::Part _drawPart; + model::MaterialPointer _drawMaterial; + bool _hasColorAttrib = false; + bool _isSkinned = false; + bool _isBlendShaped = false; + + mutable render::Item::Bound _bound; + mutable bool _isBoundInvalid = true; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload); + template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); + template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); +} + +#endif // hifi_MeshPartPayload_h \ No newline at end of file diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e05ac15ca2..2fe95ef64f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -20,33 +20,11 @@ #include #include #include -#include -#include #include "AbstractViewStateInterface.h" #include "AnimationHandle.h" -#include "DeferredLightingEffect.h" #include "Model.h" - -#include "model_vert.h" -#include "model_shadow_vert.h" -#include "model_normal_map_vert.h" -#include "model_lightmap_vert.h" -#include "model_lightmap_normal_map_vert.h" -#include "skin_model_vert.h" -#include "skin_model_shadow_vert.h" -#include "skin_model_normal_map_vert.h" - -#include "model_frag.h" -#include "model_shadow_frag.h" -#include "model_normal_map_frag.h" -#include "model_normal_specular_map_frag.h" -#include "model_specular_map_frag.h" -#include "model_lightmap_frag.h" -#include "model_lightmap_normal_map_frag.h" -#include "model_lightmap_normal_specular_map_frag.h" -#include "model_lightmap_specular_map_frag.h" -#include "model_translucent_frag.h" +#include "MeshPartPayload.h" #include "RenderUtilsLogging.h" @@ -93,112 +71,6 @@ Model::~Model() { deleteGeometry(); } -Model::RenderPipelineLib Model::_renderPipelineLib; -const int MATERIAL_GPU_SLOT = 3; -const int DIFFUSE_MAP_SLOT = 0; -const int NORMAL_MAP_SLOT = 1; -const int SPECULAR_MAP_SLOT = 2; -const int LIGHTMAP_MAP_SLOT = 3; -const int LIGHT_BUFFER_SLOT = 4; - -void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, - gpu::ShaderPointer& vertexShader, - gpu::ShaderPointer& pixelShader ) { - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), DIFFUSE_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), NORMAL_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), SPECULAR_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), LIGHTMAP_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_BUFFER_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); - - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); - gpu::Shader::makeProgram(*program, slotBindings); - - - auto locations = std::make_shared(); - initLocations(program, *locations); - - - auto state = std::make_shared(); - - // Backface on shadow - if (key.isShadow()) { - state->setCullMode(gpu::State::CULL_FRONT); - state->setDepthBias(1.0f); - state->setDepthBiasSlopeScale(4.0f); - } else { - state->setCullMode(gpu::State::CULL_BACK); - } - - // Z test depends if transparent or not - state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(key.isTranslucent(), - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - // Good to go add the brand new pipeline - auto pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); - insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations))); - - - if (!key.isWireFrame()) { - - RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME); - auto wireframeState = std::make_shared(state->getValues()); - - wireframeState->setFillMode(gpu::State::FILL_LINE); - - // create a new RenderPipeline with the same shader side and the mirrorState - auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); - insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); - } - - // If not a shadow pass, create the mirror version from the same state, just change the FrontFace - if (!key.isShadow()) { - - RenderKey mirrorKey(key.getRaw() | RenderKey::IS_MIRROR); - auto mirrorState = std::make_shared(state->getValues()); - - // create a new RenderPipeline with the same shader side and the mirrorState - auto mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, mirrorState)); - insert(value_type(mirrorKey.getRaw(), RenderPipeline(mirrorPipeline, locations))); - - if (!key.isWireFrame()) { - RenderKey wireframeKey(key.getRaw() | RenderKey::IS_MIRROR | RenderKey::IS_WIREFRAME); - auto wireframeState = std::make_shared(state->getValues()); - - wireframeState->setFillMode(gpu::State::FILL_LINE); - - // create a new RenderPipeline with the same shader side and the mirrorState - auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); - insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); - } - } -} - - -void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model::Locations& locations) { - locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold"); - locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); - locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); - locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); - locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); - locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); - locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); - locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); - locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); - locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); - locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); - locations.clusterMatrices = program->getUniforms().findLocation("clusterMatrices"); - locations.clusterIndices = program->getInputs().findLocation("inSkinClusterIndex"); - locations.clusterWeights = program->getInputs().findLocation("inSkinClusterWeight"); -} - AbstractViewStateInterface* Model::_viewState = NULL; void Model::setTranslation(const glm::vec3& translation) { @@ -256,128 +128,6 @@ void Model::initJointTransforms() { } void Model::init() { - if (_renderPipelineLib.empty()) { - // Vertex shaders - auto modelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert))); - auto modelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_normal_map_vert))); - auto modelLightmapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_vert))); - auto modelLightmapNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert))); - auto modelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_shadow_vert))); - auto skinModelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_vert))); - auto skinModelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_normal_map_vert))); - auto skinModelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_shadow_vert))); - - // Pixel shaders - auto modelPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag))); - auto modelNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_map_frag))); - auto modelSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_specular_map_frag))); - auto modelNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_specular_map_frag))); - auto modelTranslucentPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_translucent_frag))); - auto modelShadowPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_shadow_frag))); - auto modelLightmapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_frag))); - auto modelLightmapNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag))); - auto modelLightmapSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag))); - auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag))); - - // Fill the renderPipelineLib - - _renderPipelineLib.addRenderPipeline( - RenderKey(0), - modelVertex, modelPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS), - modelNormalMapVertex, modelNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_SPECULAR), - modelVertex, modelSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - modelNormalMapVertex, modelNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_TRANSLUCENT), - modelVertex, modelTranslucentPixel); - // FIXME Ignore lightmap for translucents meshpart - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), - modelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), - modelNormalMapVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - modelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - modelNormalMapVertex, modelTranslucentPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP), - modelLightmapVertex, modelLightmapPixel); - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS), - modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_SPECULAR), - modelLightmapVertex, modelLightmapSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED), - skinModelVertex, modelPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS), - skinModelNormalMapVertex, modelNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR), - skinModelVertex, modelSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - skinModelNormalMapVertex, modelNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_TRANSLUCENT), - skinModelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), - skinModelNormalMapVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - skinModelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - skinModelNormalMapVertex, modelTranslucentPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), - modelShadowVertex, modelShadowPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), - skinModelShadowVertex, modelShadowPixel); - } } void Model::reset() { @@ -417,6 +167,9 @@ bool Model::updateGeometry() { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); state.cauterizedClusterMatrices.resize(mesh.clusters.size()); + if (mesh.clusters.size() > 1) { + state.clusterBuffer = std::make_shared(mesh.clusters.size() * sizeof(glm::mat4), nullptr); + } _meshStates.append(state); auto buffer = std::make_shared(); @@ -744,61 +497,6 @@ void Model::renderSetup(RenderArgs* args) { } } - -class MeshPartPayload { -public: - MeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex) : - model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex), _shapeID(shapeIndex) { } - - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - - Model* model; - QUrl url; - int meshIndex; - int partIndex; - int _shapeID; -}; - -namespace render { - template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { - if (!payload->model->isVisible()) { - return ItemKey::Builder().withInvisible().build(); - } - auto geometry = payload->model->getGeometry(); - if (!geometry.isNull()) { - auto drawMaterial = geometry->getShapeMaterial(payload->_shapeID); - if (drawMaterial) { - auto matKey = drawMaterial->_material->getKey(); - if (matKey.isTransparent() || matKey.isTransparentMap()) { - return ItemKey::Builder::transparentShape(); - } else { - return ItemKey::Builder::opaqueShape(); - } - } - } - - // Return opaque for lack of a better idea - return ItemKey::Builder::opaqueShape(); - } - - template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { - if (payload) { - return payload->model->getPartBounds(payload->meshIndex, payload->partIndex); - } - return render::Item::Bound(); - } - template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { - if (args) { - return payload->model->renderPart(args, payload->meshIndex, payload->partIndex, payload->_shapeID); - } - } - - /* template <> const model::MaterialKey& shapeGetMaterialKey(const MeshPartPayload::Pointer& payload) { - return payload->model->getPartMaterial(payload->meshIndex, payload->partIndex); - }*/ -} - void Model::setVisibleInScene(bool newValue, std::shared_ptr scene) { if (_isVisible != newValue) { _isVisible = newValue; @@ -1293,6 +991,7 @@ void Model::updateClusterMatrices() { for (int i = 0; i < _meshStates.size(); i++) { MeshState& state = _meshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); auto jointMatrix = _rig->getJointTransform(cluster.jointIndex); @@ -1306,6 +1005,17 @@ void Model::updateClusterMatrices() { state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix; } } + + // Once computed the cluster matrices, update the buffer + if (state.clusterBuffer) { + const float* bones; + if (_cauterizeBones) { + bones = (const float*)state.cauterizedClusterMatrices.constData(); + } else { + bones = (const float*)state.clusterMatrices.constData(); + } + state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) bones); + } } // post the blender if we're not currently waiting for one to finish @@ -1429,276 +1139,6 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { return AABox(); } -void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, int shapeID) { -// PROFILE_RANGE(__FUNCTION__); - PerformanceTimer perfTimer("Model::renderPart"); - if (!_readyWhenAdded) { - return; // bail asap - } - - auto textureCache = DependencyManager::get(); - - gpu::Batch& batch = *(args->_batch); - auto mode = args->_renderMode; - - - // Capture the view matrix once for the rendering of this model - if (_transforms.empty()) { - _transforms.push_back(Transform()); - } - - auto alphaThreshold = args->_alphaThreshold; //translucent ? TRANSPARENT_ALPHA_THRESHOLD : OPAQUE_ALPHA_THRESHOLD; // FIX ME - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const std::vector>& networkMeshes = _geometry->getMeshes(); - - auto networkMaterial = _geometry->getShapeMaterial(shapeID); - if (!networkMaterial) { - return; - }; - auto material = networkMaterial->_material; - if (!material) { - return; - } - - // TODO: Not yet - // auto drawMesh = _geometry->getShapeMesh(shapeID); - // auto drawPart = _geometry->getShapePart(shapeID); - - // guard against partially loaded meshes - if (meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)_meshStates.size() ) { - return; - } - - updateClusterMatrices(); - - const NetworkMesh& networkMesh = *(networkMeshes.at(meshIndex).get()); - const FBXMesh& mesh = geometry.meshes.at(meshIndex); - const MeshState& state = _meshStates.at(meshIndex); - - auto drawMaterialKey = material->getKey(); - bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); - - bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); - bool hasSpecular = drawMaterialKey.isGlossMap(); // !drawMaterial->specularTextureName.isEmpty(); //mesh.hasSpecularTexture(); - bool hasLightmap = drawMaterialKey.isLightmapMap(); // !drawMaterial->emissiveTextureName.isEmpty(); //mesh.hasEmissiveTexture(); - bool isSkinned = state.clusterMatrices.size() > 1; - bool wireframe = isWireframe(); - - // render the part bounding box - #ifdef DEBUG_BOUNDING_PARTS - { - AABox partBounds = getPartBounds(meshIndex, partIndex); - bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; - - glm::vec4 cubeColor; - if (isSkinned) { - cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); - } else if (inView) { - cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); - } else { - cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); - } - - Transform transform; - transform.setTranslation(partBounds.calcCenter()); - transform.setScale(partBounds.getDimensions()); - batch.setModelTransform(transform); - DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); - } - #endif //def DEBUG_BOUNDING_PARTS - - if (wireframe) { - translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; - } - - Locations* locations = nullptr; - pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, - args, locations); - - // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown - // to false to rebuild out mesh groups. - if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) { - _meshGroupsKnown = false; // regenerate these lists next time around. - _readyWhenAdded = false; // in case any of our users are using scenes - invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - return; // FIXME! - } - - batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0); - int vertexCount = mesh.vertices.size(); - if (vertexCount == 0) { - // sanity check - return; // FIXME! - } - - // Transform stage - if (_transforms.empty()) { - _transforms.push_back(Transform()); - } - - if (isSkinned) { - const float* bones; - if (_cauterizeBones) { - bones = (const float*)state.cauterizedClusterMatrices.constData(); - } else { - bones = (const float*)state.clusterMatrices.constData(); - } - batch._glUniformMatrix4fv(locations->clusterMatrices, state.clusterMatrices.size(), false, bones); - _transforms[0] = Transform(); - _transforms[0].preTranslate(_translation); - } else { - if (_cauterizeBones) { - _transforms[0] = Transform(state.cauterizedClusterMatrices[0]); - } else { - _transforms[0] = Transform(state.clusterMatrices[0]); - } - _transforms[0].preTranslate(_translation); - } - batch.setModelTransform(_transforms[0]); - - if (mesh.blendshapes.isEmpty()) { - batch.setInputFormat(networkMesh._vertexFormat); - batch.setInputStream(0, *networkMesh._vertexStream); - } else { - batch.setInputFormat(networkMesh._vertexFormat); - batch.setInputBuffer(0, _blendedVertexBuffers[meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, _blendedVertexBuffers[meshIndex], vertexCount * sizeof(glm::vec3), sizeof(glm::vec3)); - batch.setInputStream(2, *networkMesh._vertexStream); - } - - if (mesh.colors.isEmpty()) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - } - - // guard against partially loaded meshes - if (partIndex >= mesh.parts.size()) { - return; - } - - const FBXMeshPart& part = mesh.parts.at(partIndex); - - - #ifdef WANT_DEBUG - if (material == nullptr) { - qCDebug(renderutils) << "WARNING: material == nullptr!!!"; - } - #endif - - { - - // apply material properties - if (mode != RenderArgs::SHADOW_RENDER_MODE) { - #ifdef WANT_DEBUG - qCDebug(renderutils) << "Material Changed ---------------------------------------------"; - qCDebug(renderutils) << "part INDEX:" << partIndex; - qCDebug(renderutils) << "NEW part.materialID:" << part.materialID; - #endif //def WANT_DEBUG - - if (locations->materialBufferUnit >= 0) { - batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer()); - } - - auto materialKey = material->getKey(); - auto textureMaps = material->getTextureMaps(); - glm::mat4 texcoordTransform[2]; - - // Diffuse - if (materialKey.isDiffuseMap()) { - auto diffuseMap = textureMaps[model::MaterialKey::DIFFUSE_MAP]; - if (diffuseMap && diffuseMap->isDefined()) { - batch.setResourceTexture(DIFFUSE_MAP_SLOT, diffuseMap->getTextureView()); - - if (!diffuseMap->getTextureTransform().isIdentity()) { - diffuseMap->getTextureTransform().getMatrix(texcoordTransform[0]); - } - } else { - batch.setResourceTexture(DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); - } - } else { - batch.setResourceTexture(DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); - } - - // Normal map - if ((locations->normalTextureUnit >= 0) && hasTangents) { - auto normalMap = textureMaps[model::MaterialKey::NORMAL_MAP]; - if (normalMap && normalMap->isDefined()) { - batch.setResourceTexture(NORMAL_MAP_SLOT, normalMap->getTextureView()); - - // texcoord are assumed to be the same has diffuse - } else { - batch.setResourceTexture(NORMAL_MAP_SLOT, textureCache->getBlueTexture()); - } - } else { - batch.setResourceTexture(NORMAL_MAP_SLOT, nullptr); - } - - // TODO: For now gloss map is used as the "specular map in the shading, we ll need to fix that - if ((locations->specularTextureUnit >= 0) && materialKey.isGlossMap()) { - auto specularMap = textureMaps[model::MaterialKey::GLOSS_MAP]; - if (specularMap && specularMap->isDefined()) { - batch.setResourceTexture(SPECULAR_MAP_SLOT, specularMap->getTextureView()); - - // texcoord are assumed to be the same has diffuse - } else { - batch.setResourceTexture(SPECULAR_MAP_SLOT, textureCache->getBlackTexture()); - } - } else { - batch.setResourceTexture(SPECULAR_MAP_SLOT, nullptr); - } - - // TODO: For now lightmaop is piped into the emissive map unit, we need to fix that and support for real emissive too - if ((locations->emissiveTextureUnit >= 0) && materialKey.isLightmapMap()) { - auto lightmapMap = textureMaps[model::MaterialKey::LIGHTMAP_MAP]; - - if (lightmapMap && lightmapMap->isDefined()) { - batch.setResourceTexture(LIGHTMAP_MAP_SLOT, lightmapMap->getTextureView()); - - auto lightmapOffsetScale = lightmapMap->getLightmapOffsetScale(); - batch._glUniform2f(locations->emissiveParams, lightmapOffsetScale.x, lightmapOffsetScale.y); - - if (!lightmapMap->getTextureTransform().isIdentity()) { - lightmapMap->getTextureTransform().getMatrix(texcoordTransform[1]); - } - } - else { - batch.setResourceTexture(LIGHTMAP_MAP_SLOT, textureCache->getGrayTexture()); - } - } else { - batch.setResourceTexture(LIGHTMAP_MAP_SLOT, nullptr); - } - - // Texcoord transforms ? - if (locations->texcoordMatrices >= 0) { - batch._glUniformMatrix4fv(locations->texcoordMatrices, 2, false, (const float*)&texcoordTransform); - } - - // TODO: We should be able to do that just in the renderTransparentJob - if (translucentMesh && locations->lightBufferUnit >= 0) { - PerformanceTimer perfTimer("DLE->setupTransparent()"); - - DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); - } - - - if (args) { - args->_details._materialSwitches++; - } - } - } - - { - PerformanceTimer perfTimer("batch.drawIndexed()"); - batch.setIndexBuffer(gpu::UINT32, part.getMergedTriangles(), 0); - batch.drawIndexed(gpu::TRIANGLES, part.mergedTrianglesIndicesCount, 0); - } - - if (args) { - const int INDICES_PER_TRIANGLE = 3; - args->_details._trianglesRendered += part.mergedTrianglesIndicesCount / INDICES_PER_TRIANGLE; - } -} - void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const std::vector>& networkMeshes = _geometry->getMeshes(); @@ -1727,46 +1167,6 @@ void Model::segregateMeshGroups() { _meshGroupsKnown = true; } -void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, - Locations*& locations) { - - PerformanceTimer perfTimer("Model::pickPrograms"); - - - RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - if (mode == RenderArgs::MIRROR_RENDER_MODE) { - key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR); - } - auto pipeline = _renderPipelineLib.find(key.getRaw()); - if (pipeline == _renderPipelineLib.end()) { - qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); - locations = 0; - return; - } - - gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram(); - locations = (*pipeline).second._locations.get(); - - - // Setup the One pipeline - batch.setPipeline((*pipeline).second._pipeline); - - if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - batch._glUniform1f(locations->alphaThreshold, alphaThreshold); - } - - if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { - const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed - batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); - } - - if ((locations->normalFittingMapUnit > -1)) { - batch.setResourceTexture(locations->normalFittingMapUnit, - DependencyManager::get()->getNormalFittingTexture()); - } -} - bool Model::initWhenReady(render::ScenePointer scene) { if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) { segregateMeshGroups(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 093c22938e..98341e1a3d 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -24,9 +24,7 @@ #include #include #include -#include #include -#include #include #include @@ -47,6 +45,7 @@ namespace render { typedef unsigned int ItemID; } class MeshPartPayload; +class ModelRenderLocations; inline uint qHash(const std::shared_ptr& a, uint seed) { return qHash(a.get(), seed); @@ -88,7 +87,6 @@ public: bool isVisible() const { return _isVisible; } AABox getPartBounds(int meshIndex, int partIndex); - void renderPart(RenderArgs* args, int meshIndex, int partIndex, int shapeID); bool maybeStartBlender(); @@ -257,6 +255,7 @@ protected: public: QVector clusterMatrices; QVector cauterizedClusterMatrices; + gpu::BufferPointer clusterBuffer; }; QVector _meshStates; @@ -323,8 +322,6 @@ private: bool _isVisible; gpu::Buffers _blendedVertexBuffers; - std::vector _transforms; - gpu::Batch _renderBatch; QVector > > _dilatedTextures; @@ -332,25 +329,6 @@ private: int _blendNumber; int _appliedBlendNumber; - class Locations { - public: - int tangent; - int alphaThreshold; - int texcoordMatrices; - int diffuseTextureUnit; - int normalTextureUnit; - int specularTextureUnit; - int emissiveTextureUnit; - int emissiveParams; - int glowIntensity; - int normalFittingMapUnit; - int materialBufferUnit; - int clusterMatrices; - int clusterIndices; - int clusterWeights; - int lightBufferUnit; - }; - QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes bool _calculatedMeshPartBoxesValid; @@ -373,118 +351,9 @@ private: void renderDebugMeshBoxes(gpu::Batch& batch); int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; - // helper functions used by render() or renderInScene() - - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, - Locations*& locations); static AbstractViewStateInterface* _viewState; - class RenderKey { - public: - enum FlagBit { - IS_TRANSLUCENT_FLAG = 0, - HAS_LIGHTMAP_FLAG, - HAS_TANGENTS_FLAG, - HAS_SPECULAR_FLAG, - HAS_EMISSIVE_FLAG, - IS_SKINNED_FLAG, - IS_STEREO_FLAG, - IS_DEPTH_ONLY_FLAG, - IS_SHADOW_FLAG, - IS_MIRROR_FLAG, //THis means that the mesh is rendered mirrored, not the same as "Rear view mirror" - IS_WIREFRAME_FLAG, - - NUM_FLAGS, - }; - - enum Flag { - IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG), - HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG), - HAS_TANGENTS = (1 << HAS_TANGENTS_FLAG), - HAS_SPECULAR = (1 << HAS_SPECULAR_FLAG), - HAS_EMISSIVE = (1 << HAS_EMISSIVE_FLAG), - IS_SKINNED = (1 << IS_SKINNED_FLAG), - IS_STEREO = (1 << IS_STEREO_FLAG), - IS_DEPTH_ONLY = (1 << IS_DEPTH_ONLY_FLAG), - IS_SHADOW = (1 << IS_SHADOW_FLAG), - IS_MIRROR = (1 << IS_MIRROR_FLAG), - IS_WIREFRAME = (1 << IS_WIREFRAME_FLAG), - }; - typedef unsigned short Flags; - - - - bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } - - bool isTranslucent() const { return isFlag(IS_TRANSLUCENT); } - bool hasLightmap() const { return isFlag(HAS_LIGHTMAP); } - bool hasTangents() const { return isFlag(HAS_TANGENTS); } - bool hasSpecular() const { return isFlag(HAS_SPECULAR); } - bool hasEmissive() const { return isFlag(HAS_EMISSIVE); } - bool isSkinned() const { return isFlag(IS_SKINNED); } - bool isStereo() const { return isFlag(IS_STEREO); } - bool isDepthOnly() const { return isFlag(IS_DEPTH_ONLY); } - bool isShadow() const { return isFlag(IS_SHADOW); } // = depth only but with back facing - bool isMirror() const { return isFlag(IS_MIRROR); } - bool isWireFrame() const { return isFlag(IS_WIREFRAME); } - - Flags _flags = 0; - short _spare = 0; - - int getRaw() { return *reinterpret_cast(this); } - - - RenderKey( - bool translucent, bool hasLightmap, - bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey( (translucent ? IS_TRANSLUCENT : 0) - | (hasLightmap ? HAS_LIGHTMAP : 0) - | (hasTangents ? HAS_TANGENTS : 0) - | (hasSpecular ? HAS_SPECULAR : 0) - | (isSkinned ? IS_SKINNED : 0) - | (isWireframe ? IS_WIREFRAME : 0) - ) {} - - RenderKey(RenderArgs::RenderMode mode, - bool translucent, float alphaThreshold, bool hasLightmap, - bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey( ((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) - | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly - | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) - | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) - | (isSkinned ? IS_SKINNED : 0) - | (isWireframe ? IS_WIREFRAME : 0) - | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_DEPTH_ONLY : 0) - | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_SHADOW : 0) - | ((mode == RenderArgs::MIRROR_RENDER_MODE) ? IS_MIRROR :0) - ) {} - - RenderKey(int bitmask) : _flags(bitmask) {} - }; - - - class RenderPipeline { - public: - gpu::PipelinePointer _pipeline; - std::shared_ptr _locations; - RenderPipeline(gpu::PipelinePointer pipeline, std::shared_ptr locations) : - _pipeline(pipeline), _locations(locations) {} - }; - - typedef std::unordered_map BaseRenderPipelineMap; - class RenderPipelineLib : public BaseRenderPipelineMap { - public: - typedef RenderKey Key; - - - void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader); - - void initLocations(gpu::ShaderPointer& program, Locations& locations); - }; - static RenderPipelineLib _renderPipelineLib; - bool _renderCollisionHull; @@ -494,6 +363,7 @@ private: bool _needsReload = true; bool _needsUpdateClusterMatrices = true; + friend class MeshPartPayload; protected: RigPointer _rig; }; diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp new file mode 100644 index 0000000000..c614fae67a --- /dev/null +++ b/libraries/render-utils/src/ModelRender.cpp @@ -0,0 +1,282 @@ +// +// ModelRender.cpp +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ModelRender.h" + +#include + +#include + +#include "DeferredLightingEffect.h" + +#include "model_vert.h" +#include "model_shadow_vert.h" +#include "model_normal_map_vert.h" +#include "model_lightmap_vert.h" +#include "model_lightmap_normal_map_vert.h" +#include "skin_model_vert.h" +#include "skin_model_shadow_vert.h" +#include "skin_model_normal_map_vert.h" + +#include "model_frag.h" +#include "model_shadow_frag.h" +#include "model_normal_map_frag.h" +#include "model_normal_specular_map_frag.h" +#include "model_specular_map_frag.h" +#include "model_lightmap_frag.h" +#include "model_lightmap_normal_map_frag.h" +#include "model_lightmap_normal_specular_map_frag.h" +#include "model_lightmap_specular_map_frag.h" +#include "model_translucent_frag.h" + +ModelRender::RenderPipelineLib ModelRender::_renderPipelineLib; + +const ModelRender::RenderPipelineLib& ModelRender::getRenderPipelineLib() { + if (_renderPipelineLib.empty()) { + // Vertex shaders + auto modelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert))); + auto modelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_normal_map_vert))); + auto modelLightmapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_vert))); + auto modelLightmapNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert))); + auto modelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_shadow_vert))); + auto skinModelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_vert))); + auto skinModelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_normal_map_vert))); + auto skinModelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_shadow_vert))); + + // Pixel shaders + auto modelPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag))); + auto modelNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_map_frag))); + auto modelSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_specular_map_frag))); + auto modelNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_specular_map_frag))); + auto modelTranslucentPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_translucent_frag))); + auto modelShadowPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_shadow_frag))); + auto modelLightmapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_frag))); + auto modelLightmapNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag))); + auto modelLightmapSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag))); + auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag))); + + // Fill the renderPipelineLib + + _renderPipelineLib.addRenderPipeline( + RenderKey(0), + modelVertex, modelPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_TANGENTS), + modelNormalMapVertex, modelNormalMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_SPECULAR), + modelVertex, modelSpecularMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), + modelNormalMapVertex, modelNormalSpecularMapPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_TRANSLUCENT), + modelVertex, modelTranslucentPixel); + // FIXME Ignore lightmap for translucents meshpart + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), + modelVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), + modelNormalMapVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), + modelVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), + modelNormalMapVertex, modelTranslucentPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_LIGHTMAP), + modelLightmapVertex, modelLightmapPixel); + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS), + modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_SPECULAR), + modelLightmapVertex, modelLightmapSpecularMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), + modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED), + skinModelVertex, modelPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS), + skinModelNormalMapVertex, modelNormalMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR), + skinModelVertex, modelSpecularMapPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), + skinModelNormalMapVertex, modelNormalSpecularMapPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_TRANSLUCENT), + skinModelVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), + skinModelNormalMapVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), + skinModelVertex, modelTranslucentPixel); + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), + skinModelNormalMapVertex, modelTranslucentPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), + modelShadowVertex, modelShadowPixel); + + + _renderPipelineLib.addRenderPipeline( + RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), + skinModelShadowVertex, modelShadowPixel); + } + + return _renderPipelineLib; +} + + +void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey key, + gpu::ShaderPointer& vertexShader, + gpu::ShaderPointer& pixelShader) { + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), ModelRender::SKINNING_GPU_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), ModelRender::MATERIAL_GPU_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), ModelRender::DIFFUSE_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), ModelRender::NORMAL_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), ModelRender::SPECULAR_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), ModelRender::LIGHTMAP_MAP_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ModelRender::LIGHT_BUFFER_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); + gpu::Shader::makeProgram(*program, slotBindings); + + + auto locations = std::make_shared(); + initLocations(program, *locations); + + + auto state = std::make_shared(); + + // Backface on shadow + if (key.isShadow()) { + state->setCullMode(gpu::State::CULL_FRONT); + state->setDepthBias(1.0f); + state->setDepthBiasSlopeScale(4.0f); + } else { + state->setCullMode(gpu::State::CULL_BACK); + } + + // Z test depends if transparent or not + state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(key.isTranslucent(), + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + // Good to go add the brand new pipeline + auto pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations))); + + + if (!key.isWireFrame()) { + + RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME); + auto wireframeState = std::make_shared(state->getValues()); + + wireframeState->setFillMode(gpu::State::FILL_LINE); + + // create a new RenderPipeline with the same shader side and the wireframe state + auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState)); + insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); + } +} + + +void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) { + locations.alphaThreshold = program->getUniforms().findLocation("alphaThreshold"); + locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); + locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); + locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); + locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); + locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); + locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); + locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); + locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); + locations.skinClusterBufferUnit = program->getBuffers().findLocation("skinClusterBuffer"); + locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); + locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); + +} + + +void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, + Locations*& locations) { + + PerformanceTimer perfTimer("Model::pickPrograms"); + getRenderPipelineLib(); + + RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + auto pipeline = _renderPipelineLib.find(key.getRaw()); + if (pipeline == _renderPipelineLib.end()) { + qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); + locations = 0; + return; + } + + gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram(); + locations = (*pipeline).second._locations.get(); + + + // Setup the One pipeline + batch.setPipeline((*pipeline).second._pipeline); + + if ((locations->alphaThreshold > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { + batch._glUniform1f(locations->alphaThreshold, alphaThreshold); + } + + if ((locations->glowIntensity > -1) && (mode != RenderArgs::SHADOW_RENDER_MODE)) { + const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed + batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); + } + + if ((locations->normalFittingMapUnit > -1)) { + batch.setResourceTexture(locations->normalFittingMapUnit, + DependencyManager::get()->getNormalFittingTexture()); + } +} diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h new file mode 100644 index 0000000000..1528dcfc87 --- /dev/null +++ b/libraries/render-utils/src/ModelRender.h @@ -0,0 +1,154 @@ +// +// ModelRender.h +// interface/src/renderer +// +// Created by Sam Gateau on 10/3/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelRender_h +#define hifi_ModelRender_h + +#include + +#include + +class ModelRender { +public: + + static const int SKINNING_GPU_SLOT = 2; + static const int MATERIAL_GPU_SLOT = 3; + static const int DIFFUSE_MAP_SLOT = 0; + static const int NORMAL_MAP_SLOT = 1; + static const int SPECULAR_MAP_SLOT = 2; + static const int LIGHTMAP_MAP_SLOT = 3; + static const int LIGHT_BUFFER_SLOT = 4; + + class Locations { + public: + int alphaThreshold; + int texcoordMatrices; + int diffuseTextureUnit; + int normalTextureUnit; + int specularTextureUnit; + int emissiveTextureUnit; + int emissiveParams; + int glowIntensity; + int normalFittingMapUnit; + int skinClusterBufferUnit; + int materialBufferUnit; + int lightBufferUnit; + }; + + static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, float alphaThreshold, + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, + Locations*& locations); + + class RenderKey { + public: + enum FlagBit { + IS_TRANSLUCENT_FLAG = 0, + HAS_LIGHTMAP_FLAG, + HAS_TANGENTS_FLAG, + HAS_SPECULAR_FLAG, + HAS_EMISSIVE_FLAG, + IS_SKINNED_FLAG, + IS_STEREO_FLAG, + IS_DEPTH_ONLY_FLAG, + IS_SHADOW_FLAG, + IS_WIREFRAME_FLAG, + + NUM_FLAGS, + }; + + enum Flag { + IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG), + HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG), + HAS_TANGENTS = (1 << HAS_TANGENTS_FLAG), + HAS_SPECULAR = (1 << HAS_SPECULAR_FLAG), + HAS_EMISSIVE = (1 << HAS_EMISSIVE_FLAG), + IS_SKINNED = (1 << IS_SKINNED_FLAG), + IS_STEREO = (1 << IS_STEREO_FLAG), + IS_DEPTH_ONLY = (1 << IS_DEPTH_ONLY_FLAG), + IS_SHADOW = (1 << IS_SHADOW_FLAG), + IS_WIREFRAME = (1 << IS_WIREFRAME_FLAG), + }; + typedef unsigned short Flags; + + + + bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } + + bool isTranslucent() const { return isFlag(IS_TRANSLUCENT); } + bool hasLightmap() const { return isFlag(HAS_LIGHTMAP); } + bool hasTangents() const { return isFlag(HAS_TANGENTS); } + bool hasSpecular() const { return isFlag(HAS_SPECULAR); } + bool hasEmissive() const { return isFlag(HAS_EMISSIVE); } + bool isSkinned() const { return isFlag(IS_SKINNED); } + bool isStereo() const { return isFlag(IS_STEREO); } + bool isDepthOnly() const { return isFlag(IS_DEPTH_ONLY); } + bool isShadow() const { return isFlag(IS_SHADOW); } // = depth only but with back facing + bool isWireFrame() const { return isFlag(IS_WIREFRAME); } + + Flags _flags = 0; + short _spare = 0; + + int getRaw() { return *reinterpret_cast(this); } + + + RenderKey( + bool translucent, bool hasLightmap, + bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : + RenderKey((translucent ? IS_TRANSLUCENT : 0) + | (hasLightmap ? HAS_LIGHTMAP : 0) + | (hasTangents ? HAS_TANGENTS : 0) + | (hasSpecular ? HAS_SPECULAR : 0) + | (isSkinned ? IS_SKINNED : 0) + | (isWireframe ? IS_WIREFRAME : 0) + ) {} + + RenderKey(RenderArgs::RenderMode mode, + bool translucent, float alphaThreshold, bool hasLightmap, + bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : + RenderKey(((translucent && (alphaThreshold == 0.0f) && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) + | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly + | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) + | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) + | (isSkinned ? IS_SKINNED : 0) + | (isWireframe ? IS_WIREFRAME : 0) + | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_DEPTH_ONLY : 0) + | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_SHADOW : 0) + ) {} + + RenderKey(int bitmask) : _flags(bitmask) {} + }; + + + class RenderPipeline { + public: + gpu::PipelinePointer _pipeline; + std::shared_ptr _locations; + RenderPipeline(gpu::PipelinePointer pipeline, std::shared_ptr locations) : + _pipeline(pipeline), _locations(locations) {} + }; + + typedef std::unordered_map BaseRenderPipelineMap; + class RenderPipelineLib : public BaseRenderPipelineMap { + public: + typedef RenderKey Key; + + + void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader); + + void initLocations(gpu::ShaderPointer& program, Locations& locations); + }; + static RenderPipelineLib _renderPipelineLib; + + static const RenderPipelineLib& getRenderPipelineLib(); + +}; + +#endif // hifi_ModelRender_h \ No newline at end of file diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 3f940d8569..6c68b60f42 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -497,9 +497,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec } QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget) { - QPointF transformedPos = _mouseTranslator(originalPoint); - transformedPos = mapWindowToUi(transformedPos, originalWidget); - return transformedPos; + return _mouseTranslator(originalPoint); } diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh new file mode 100644 index 0000000000..b34ed3ed2b --- /dev/null +++ b/libraries/render-utils/src/Skinning.slh @@ -0,0 +1,70 @@ + +<@if not SKINNING_SLH@> +<@def SKINNING_SLH@> + +const int MAX_TEXCOORDS = 2; +const int MAX_CLUSTERS = 128; +const int INDICES_PER_VERTEX = 4; + +layout(std140) uniform skinClusterBuffer { + mat4 clusterMatrices[MAX_CLUSTERS]; +}; + +void skinPosition(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + } + + skinnedPosition = newPosition; +} + +void skinPositionNormal(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; +} + +void skinPositionNormalTangent(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; +} + + +<@endif@> \ No newline at end of file diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index 53e68727fb..bb2058b5f8 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -18,11 +18,8 @@ <$declareStandardTransform()$> -const int MAX_TEXCOORDS = 2; -const int MAX_CLUSTERS = 128; -const int INDICES_PER_VERTEX = 4; +<@include Skinning.slh@> -uniform mat4 clusterMatrices[MAX_CLUSTERS]; uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; out vec4 _position; @@ -32,13 +29,9 @@ out vec3 _color; void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(inSkinClusterIndex[i])]; - float clusterWeight = inSkinClusterWeight[i]; - position += clusterMatrix * inPosition * clusterWeight; - interpolatedNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } + vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0); + + skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the diffuse color _color = inColor.rgb; diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index 4c558939d8..f198cb5f77 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -18,11 +18,8 @@ <$declareStandardTransform()$> -const int MAX_TEXCOORDS = 2; -const int MAX_CLUSTERS = 128; -const int INDICES_PER_VERTEX = 4; +<@include Skinning.slh@> -uniform mat4 clusterMatrices[MAX_CLUSTERS]; uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; out vec4 _position; @@ -35,13 +32,8 @@ void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(inSkinClusterIndex[i])]; - float clusterWeight = inSkinClusterWeight[i]; - position += clusterMatrix * inPosition * clusterWeight; - interpolatedNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - interpolatedTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } + + skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the diffuse color _color = inColor.rgb; diff --git a/libraries/render-utils/src/skin_model_shadow.slv b/libraries/render-utils/src/skin_model_shadow.slv index 6cb4da25f4..9c00f9065b 100755 --- a/libraries/render-utils/src/skin_model_shadow.slv +++ b/libraries/render-utils/src/skin_model_shadow.slv @@ -16,18 +16,11 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -const int MAX_CLUSTERS = 128; -const int INDICES_PER_VERTEX = 4; - -uniform mat4 clusterMatrices[MAX_CLUSTERS]; +<@include Skinning.slh@> void main(void) { vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(inSkinClusterIndex[i])]; - float clusterWeight = inSkinClusterWeight[i]; - position += clusterMatrix * inPosition * clusterWeight; - } + skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index d57ff7a874..32ab1780e0 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -282,7 +282,7 @@ void UDTTest::sendPacket() { packetList->write(randomPaddedData); } - packetList->closeCurrentPacket(false); + packetList->closeCurrentPacket(); _totalQueuedBytes += packetList->getDataSize(); _totalQueuedPackets += packetList->getNumPackets(); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index c2f1d8d393..4815439091 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -8,7 +8,8 @@ /*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ //per script -/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLightSwitches */ + +/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */ var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js"); Script.include(utilitiesScript); @@ -85,13 +86,13 @@ function createAllToys() { createBasketballHoop(); + createBasketballRack(); + createGates(); createFire(); - // //Handles toggling of all sconce lights - createLightSwitches(); - - + // Handles toggling of all sconce lights + createLights(); } @@ -107,6 +108,224 @@ function deleteAllToys() { }); } +function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; + + var scriptURL = Script.resolvePath("../examples/toys/lightSwitch.js"); + + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: scriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + } + }); + setEntityCustomData(resetKey, lightSwitchHall, { + resetMe: true, + on: true, + type: "Hall Light" + }); + + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + } + }); + + setEntityCustomData(resetKey, sconceLight1, { + resetMe: true, + type: "Hall Light", + }); + + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + } + }); + + setEntityCustomData(resetKey, sconceLight2, { + resetMe: true, + type: "Hall Light", + }); + + + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: scriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + } + }); + + setEntityCustomData(resetKey, lightSwitchGarage, { + resetMe: true, + on: true, + type: "Garage Light" + }); + + + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, + + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + } + }); + + + + setEntityCustomData(resetKey, sconceLight3, { + resetMe: true, + type: "Garage Light", + }); + + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + } + }); + + setEntityCustomData(resetKey, sconceLight4, { + resetMe: true, + type: "Garage Light", + }); + + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + } + }); + + setEntityCustomData(resetKey, sconceLight5, { + resetMe: true, + type: "Garage Light", + }); + +} + + function createFire() { @@ -242,68 +461,6 @@ function createFlashlight(position) { } -function createLightSwitches() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx?v1"; - var scriptURL = Script.resolvePath("../examples/toys/lightSwitchHall.js"); - - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: scriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - } - }); - - setEntityCustomData(resetKey, lightSwitchHall, { - resetMe: true - }); - - scriptURL = Script.resolvePath("../examples/toys/lightSwitchGarage.js"); - - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: scriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - } - }); - - setEntityCustomData(resetKey, lightSwitchGarage, { - resetMe: true - }); - -} - function createDice() { var diceProps = { type: "Model", @@ -508,6 +665,147 @@ function createBasketballHoop() { }); } +function createBasketballRack() { + var NUMBER_OF_BALLS = 4; + var DIAMETER = 0.30; + var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; + var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; + + var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); + + var rackStartPosition = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var rack = Entities.addEntity({ + name: 'Basketball Rack', + type: "Model", + modelURL: rackURL, + position: rackStartPosition, + rotation: rackRotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + linearDamping: 1, + dimensions: { + x: 0.4, + y: 1.37, + z: 1.73 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + compoundShapeURL: rackCollisionHullURL + }); + + setEntityCustomData(resetKey, rack, { + resetMe: true + }); + + setEntityCustomData(GRABBABLE_DATA_KEY, rack, { + grabbable: false + }); + + var collidingBalls = []; + var originalBallPositions = []; + + function createCollidingBalls() { + var position = rackStartPosition; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Colliding Basketball', + shapeType: 'Sphere', + position: { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + }); + + collidingBalls.push(collidingBall); + originalBallPositions.push(position); + } + } + + function testBallDistanceFromStart() { + var resetCount = 0; + + collidingBalls.forEach(function(ball, index) { + var currentPosition = Entities.getEntityProperties(ball, "position").position; + var originalPosition = originalBallPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(ball, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + resetCount++; + if (resetCount === NUMBER_OF_BALLS) { + deleteCollidingBalls(); + createCollidingBalls(); + } + } + }, 200); + } + }); + } + + function deleteEntity(entityID) { + if (entityID === rack) { + deleteCollidingBalls(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } + } + + function deleteCollidingBalls() { + while (collidingBalls.length > 0) { + Entities.deleteEntity(collidingBalls.pop()); + } + } + + createCollidingBalls(); + Entities.deletingEntity.connect(deleteEntity); + + var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); + +} + + + function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj';