From 24dd06b65e9068ce4b3b8913266a703983e88d7a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 23 Feb 2017 09:09:54 -0500 Subject: [PATCH 001/128] * address FIXME regarding jasmine.done() * support disabling tests / reporting as pending --- .../developer/libraries/jasmine/hifi-boot.js | 12 +++++++++-- .../tests/unit_tests/avatarUnitTests.js | 11 +++++++++- .../tests/unit_tests/entityUnitTests.js | 8 +++++++ .../developer/tests/unit_tests/testRunner.js | 21 ++++++++++++++++--- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index f490a3618f..49d7fadd29 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -6,7 +6,7 @@ var lastSpecStartTime; function ConsoleReporter(options) { var startTime = new Date().getTime(); - var errorCount = 0; + var errorCount = 0, pending = []; this.jasmineStarted = function (obj) { print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; @@ -15,11 +15,15 @@ var endTime = new Date().getTime(); print('
'); if (errorCount === 0) { - print ('All tests passed!'); + print ('All enabled tests passed!'); } else { print('Tests completed with ' + errorCount + ' ' + ERROR + '.'); } + if (pending.length) { + print ('disabled:
   '+ + pending.join('
   ')+'
'); + } print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { @@ -32,6 +36,10 @@ lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { + if (obj.status === 'pending') { + pending.push(obj.fullName); + return print('...(pending ' + obj.fullName +')'); + } var specEndTime = new Date().getTime(); var symbol = obj.status === PASSED ? '' + CHECKMARK + '' : diff --git a/scripts/developer/tests/unit_tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js index 7032b5f5e6..fc2801f83e 100644 --- a/scripts/developer/tests/unit_tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -8,6 +8,15 @@ var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1}; describe("MyAvatar", function () { + // backup/restore current skeletonModelURL + beforeAll(function() { + this.oldURL = MyAvatar.skeletonModelURL; + }); + + afterAll(function() { + MyAvatar.skeletonModelURL = this.oldURL; + }); + // reload the avatar from scratch before each test. beforeEach(function (done) { MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL; @@ -25,7 +34,7 @@ describe("MyAvatar", function () { }, 500); } }, 500); - }); + }, 10000 /*timeout -- allow time to download avatar*/); // makes the assumption that there is solid ground somewhat underneath the avatar. it("position and orientation getters", function () { diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 033a484663..612bf6cdcd 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -19,6 +19,14 @@ describe('Entity', function() { }, }; + it('serversExist', function() { + expect(Entities.serversExist()).toBe(true); + }); + + it('canRezTmp', function() { + expect(Entities.canRezTmp()).toBe(true); + }); + beforeEach(function() { boxEntity = Entities.addEntity(boxProps); }); diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js index 31d83cd986..545bb89600 100644 --- a/scripts/developer/tests/unit_tests/testRunner.js +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -3,11 +3,26 @@ Script.include('../../libraries/jasmine/jasmine.js'); Script.include('../../libraries/jasmine/hifi-boot.js') // Include unit tests -// FIXME: Figure out why jasmine done() is not working. -// Script.include('avatarUnitTests.js'); +Script.include('avatarUnitTests.js'); Script.include('bindUnitTest.js'); Script.include('entityUnitTests.js'); +describe("jasmine internal tests", function() { + it('should support async .done()', function(done) { + var start = new Date; + Script.setTimeout(function() { + expect((new Date - start)/1000).toBeCloseTo(0.5, 1); + done(); + }, 500); + }); + // jasmine pending test + xit('disabled test', function() { + expect(false).toBe(true); + }); +}); + +// invoke Script.stop (after any async tests complete) +jasmine.getEnv().addReporter({ jasmineDone: Script.stop }); + // Run the tests jasmine.getEnv().execute(); -Script.stop(); From 524806b2d99568f68b12b790d169ccee5fce05f4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 23 Feb 2017 11:22:13 -0500 Subject: [PATCH 002/128] Add initial eslint config comments --- scripts/developer/tests/unit_tests/avatarUnitTests.js | 6 ++++-- scripts/developer/tests/unit_tests/bindUnitTest.js | 2 ++ scripts/developer/tests/unit_tests/entityUnitTests.js | 4 +++- scripts/developer/tests/unit_tests/testRunner.js | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/developer/tests/unit_tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js index fc2801f83e..4ab8556ab7 100644 --- a/scripts/developer/tests/unit_tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -1,5 +1,7 @@ +/* eslint-env jasmine */ // Art3mis +// eslint-disable-next-line max-len var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758"; var ORIGIN = {x: 0, y: 0, z: 0}; @@ -29,12 +31,12 @@ describe("MyAvatar", function () { MyAvatar.position = ORIGIN; MyAvatar.orientation = ROT_IDENT; // give the avatar 1/2 a second to settle on the ground in the idle pose. - Script.setTimeout(function () { + Script.setTimeout(function () { done(); }, 500); } }, 500); - }, 10000 /*timeout -- allow time to download avatar*/); + }, 10000 /* timeout -- allow time to download avatar*/); // makes the assumption that there is solid ground somewhat underneath the avatar. it("position and orientation getters", function () { diff --git a/scripts/developer/tests/unit_tests/bindUnitTest.js b/scripts/developer/tests/unit_tests/bindUnitTest.js index 609487a30b..3407490a04 100644 --- a/scripts/developer/tests/unit_tests/bindUnitTest.js +++ b/scripts/developer/tests/unit_tests/bindUnitTest.js @@ -1,3 +1,5 @@ +/* eslint-env jasmine */ + Script.include('../../../system/libraries/utils.js'); describe('Bind', function() { diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 612bf6cdcd..730fceb144 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -1,3 +1,5 @@ +/* eslint-env jasmine */ + describe('Entity', function() { var center = Vec3.sum( MyAvatar.position, @@ -70,4 +72,4 @@ describe('Entity', function() { props = Entities.getEntityProperties(boxEntity); expect(props.lastEdited).toBeGreaterThan(prevLastEdited); }); -}); \ No newline at end of file +}); diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js index 545bb89600..7ce55b1874 100644 --- a/scripts/developer/tests/unit_tests/testRunner.js +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -1,6 +1,8 @@ +/* eslint-env jasmine */ + // Include testing library Script.include('../../libraries/jasmine/jasmine.js'); -Script.include('../../libraries/jasmine/hifi-boot.js') +Script.include('../../libraries/jasmine/hifi-boot.js'); // Include unit tests Script.include('avatarUnitTests.js'); From 6b96cfad42f2ab88dd07dad1fb3880d217de96ee Mon Sep 17 00:00:00 2001 From: Trevor Berninger Date: Thu, 13 Apr 2017 13:04:28 -0700 Subject: [PATCH 003/128] Just the interaction JS scripts --- unpublishedScripts/interaction/Interaction.js | 179 ++++++++++++++++++ unpublishedScripts/interaction/NPCHelpers.js | 179 ++++++++++++++++++ unpublishedScripts/interaction/NPC_AC.js | 102 ++++++++++ unpublishedScripts/interaction/Sphinx.json | 159 ++++++++++++++++ 4 files changed, 619 insertions(+) create mode 100644 unpublishedScripts/interaction/Interaction.js create mode 100644 unpublishedScripts/interaction/NPCHelpers.js create mode 100644 unpublishedScripts/interaction/NPC_AC.js create mode 100644 unpublishedScripts/interaction/Sphinx.json diff --git a/unpublishedScripts/interaction/Interaction.js b/unpublishedScripts/interaction/Interaction.js new file mode 100644 index 0000000000..a488e9f279 --- /dev/null +++ b/unpublishedScripts/interaction/Interaction.js @@ -0,0 +1,179 @@ +// +// Interaction.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + print("loading interaction script"); + + var Avatar = false; + var NPC = false; + var previousNPC = false; + var hasCenteredOnNPC = false; + var distance = 10; + var r = 8; + var player = false; + + var baselineX = 0; + var baselineY = 0; + var nodRange = 20; + var shakeRange = 20; + + var ticker = false; + var heartbeatTimer = false; + + function callOnNPC(message) { + if(NPC) + Messages.sendMessage("interactionComs", NPC + ":" + message); + else + Messages.sendMessage("interactionComs", previousNPC + ":" + message); + } + + LimitlessSpeechRecognition.onFinishedSpeaking.connect(function(speech) { + print("Got: " + speech); + callOnNPC("voiceData:" + speech); + }); + + LimitlessSpeechRecognition.onReceivedTranscription.connect(function(speech) { + callOnNPC("speaking"); + }); + + function setBaselineRotations(rot) { + baselineX = rot.x; + baselineY = rot.y; + } + + function findLookedAtNPC() { + var intersection = AvatarList.findRayIntersection({origin: MyAvatar.position, direction: Quat.getFront(Camera.getOrientation())}, true); + if (intersection.intersects && intersection.distance <= distance){ + var npcAvatar = AvatarList.getAvatar(intersection.avatarID); + if (npcAvatar.displayName.search("NPC") != -1) { + setBaselineRotations(Quat.safeEulerAngles(Camera.getOrientation())); + return intersection.avatarID; + } + } + return false; + } + + function isStillFocusedNPC() { + var avatar = AvatarList.getAvatar(NPC); + if (avatar) { + var avatarPosition = avatar.position; + return Vec3.distance(MyAvatar.position, avatarPosition) <= distance && Math.abs(Quat.dot(Camera.getOrientation(), Quat.lookAtSimple(MyAvatar.position, avatarPosition))) > 0.6; + } + return false; // NPC reference died. Maybe it crashed or we teleported to a new world? + } + + function onWeLostFocus() { + print("lost NPC: " + NPC); + callOnNPC("onLostFocused"); + var baselineX = 0; + var baselineY = 0; + } + + function onWeGainedFocus() { + print("found NPC: " + NPC); + callOnNPC("onFocused"); + var rotation = Quat.safeEulerAngles(Camera.getOrientation()); + baselineX = rotation.x; + baselineY = rotation.y; + LimitlessSpeechRecognition.setListeningToVoice(true); + } + + function checkFocus() { + var newNPC = findLookedAtNPC(); + + if (NPC && newNPC != NPC && !isStillFocusedNPC()) { + onWeLostFocus(); + previousNPC = NPC; + NPC = false; + } + if (!NPC && newNPC != false) { + NPC = newNPC; + onWeGainedFocus(); + } + } + + function checkGesture() { + var rotation = Quat.safeEulerAngles(Camera.getOrientation()); + + var deltaX = Math.abs(rotation.x - baselineX); + if (deltaX > 180) { + deltaX -= 180; + } + var deltaY = Math.abs(rotation.y - baselineY); + if (deltaY > 180) { + deltaY -= 180; + } + + if (deltaX >= nodRange && deltaY <= shakeRange) { + callOnNPC("onNodReceived"); + } else if (deltaY >= shakeRange && deltaX <= nodRange) { + callOnNPC("onShakeReceived"); + } + } + + function tick() { + checkFocus(); + if (NPC) { + checkGesture(); + } + } + + function heartbeat() { + callOnNPC("beat"); + } + + Messages.subscribe("interactionComs"); + + Messages.messageReceived.connect(function (channel, message, sender) { + if(channel === "interactionComs" && player) { + var codeIndex = message.search('clientexec'); + if(codeIndex != -1) { + var code = message.substr(codeIndex+11); + Script.evaluate(code, ''); + } + } + }); + + this.enterEntity = function(id) { + player = true; + print("Something entered me: " + id); + LimitlessSpeechRecognition.setAuthKey("testKey"); + if (!ticker) { + ticker = Script.setInterval(tick, 333); + } + if(!heartbeatTimer) { + heartbeatTimer = Script.setInterval(heartbeat, 1000); + } + }; + this.leaveEntity = function(id) { + LimitlessSpeechRecognition.setListeningToVoice(false); + player = false; + print("Something left me: " + id); + if (previousNPC) + Messages.sendMessage("interactionComs", previousNPC + ":leftArea"); + if (ticker) { + ticker.stop(); + ticker = false; + } + if (heartbeatTimer) { + heartbeatTimer.stop(); + heartbeatTimer = false; + } + }; + this.unload = function() { + print("Okay. I'm Unloading!"); + if (ticker) { + ticker.stop(); + ticker = false; + } + }; + print("finished loading interaction script"); +}); diff --git a/unpublishedScripts/interaction/NPCHelpers.js b/unpublishedScripts/interaction/NPCHelpers.js new file mode 100644 index 0000000000..d36a71ea93 --- /dev/null +++ b/unpublishedScripts/interaction/NPCHelpers.js @@ -0,0 +1,179 @@ +// +// NPCHelpers.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var audioInjector = false; +var blocked = false; +var playingResponseAnim = false; +var storyURL = ""; +var _qid = "start"; + +print("TESTTEST"); + +function strContains(str, sub) { + return str.search(sub) != -1; +} + +function callbackOnCondition(conditionFunc, ms, callback, count) { + var thisCount = 0; + if (typeof count !== 'undefined') { + thisCount = count; + } + if (conditionFunc()) { + callback(); + } else if (thisCount < 10) { + Script.setTimeout(function() { + callbackOnCondition(conditionFunc, ms, callback, thisCount + 1); + }, ms); + } else { + print("callbackOnCondition timeout"); + } +} + +function playAnim(animURL, looping, onFinished) { + print("got anim: " + animURL); + print("looping: " + looping); + // Start caching the animation if not already cached. + AnimationCache.getAnimation(animURL); + + // Tell the avatar to animate so that we can tell if the animation is ready without crashing + Avatar.startAnimation(animURL, 30, 1, false, false, 0, 1); + + // Continually check if the animation is ready + callbackOnCondition(function(){ + var details = Avatar.getAnimationDetails(); + // if we are running the request animation and are past the first frame, the anim is loaded properly + print("running: " + details.running); + print("url and animURL: " + details.url.trim().replace(/ /g, "%20") + " | " + animURL.trim().replace(/ /g, "%20")); + print("currentFrame: " + details.currentFrame); + return details.running && details.url.trim().replace(/ /g, "%20") == animURL.trim().replace(/ /g, "%20") && details.currentFrame > 0; + }, 250, function(){ + var timeOfAnim = ((AnimationCache.getAnimation(animURL).frames.length / 30) * 1000) + 100; // frames to miliseconds plus a small buffer + print("animation loaded. length: " + timeOfAnim); + // Start the animation again but this time with frame information + Avatar.startAnimation(animURL, 30, 1, looping, true, 0, AnimationCache.getAnimation(animURL).frames.length); + if (typeof onFinished !== 'undefined') { + print("onFinished defined. setting the timeout with timeOfAnim"); + timers.push(Script.setTimeout(onFinished, timeOfAnim)); + } + }); +} + +function playSound(soundURL, onFinished) { + callbackOnCondition(function() { + return SoundCache.getSound(soundURL).downloaded; + }, 250, function() { + if (audioInjector) { + audioInjector.stop(); + } + audioInjector = Audio.playSound(SoundCache.getSound(soundURL), {position: Avatar.position, volume: 1.0}); + if (typeof onFinished !== 'undefined') { + audioInjector.finished.connect(onFinished); + } + }); +} + +function npcRespond(soundURL, animURL, onFinished) { + if (typeof soundURL !== 'undefined' && soundURL != '') { + print("npcRespond got soundURL!"); + playSound(soundURL, function(){ + print("sound finished"); + var animDetails = Avatar.getAnimationDetails(); + print("animDetails.lastFrame: " + animDetails.lastFrame); + print("animDetails.currentFrame: " + animDetails.currentFrame); + if (animDetails.lastFrame < animDetails.currentFrame + 1 || !playingResponseAnim) { + onFinished(); + } + audioInjector = false; + }); + } + if (typeof animURL !== 'undefined' && animURL != '') { + print("npcRespond got animURL!"); + playingResponseAnim = true; + playAnim(animURL, false, function() { + print("anim finished"); + playingResponseAnim = false; + print("injector: " + audioInjector); + if (!audioInjector || !audioInjector.isPlaying()) { + print("resetting Timer"); + print("about to call onFinished"); + onFinished(); + } + }); + } +} + +function npcRespondBlocking(soundURL, animURL, onFinished) { + print("blocking response requested"); + if (!blocked) { + print("not already blocked"); + blocked = true; + npcRespond(soundURL, animURL, function(){ + if (onFinished){ + onFinished(); + }blocked = false; + }); + } +} + +function npcContinueStory(soundURL, animURL, nextID, onFinished) { + if (!nextID) { + nextID = _qid; + } + npcRespondBlocking(soundURL, animURL, function(){ + if (onFinished){ + onFinished(); + }setQid(nextID); + }); +} + +function setQid(newQid) { + print("setting quid"); + print("_qid: " + _qid); + _qid = newQid; + print("_qid: " + _qid); + doActionFromServer("init"); +} + +function runOnClient(code) { + Messages.sendMessage("interactionComs", "clientexec:" + code); +} + +function doActionFromServer(action, data, useServerCache) { + if (action == "start") { + ignoreCount = 0; + _qid = "start"; + } + var xhr = new XMLHttpRequest(); + xhr.open("POST", "http://gserv_devel.studiolimitless.com/story", true); + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4){ + if (xhr.status == 200) { + print("200!"); + print("evaluating: " + xhr.responseText); + Script.evaluate(xhr.responseText, ""); + } else if (xhr.status == 444) { + print("Limitless Serv 444: API error: " + xhr.responseText); + } else { + print("HTTP Code: " + xhr.status + ": " + xhr.responseText); + } + } + }; + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + var postData = "url=" + storyURL + "&action=" + action + "&qid=" + _qid; + if (typeof data !== 'undefined' && data != '') { + postData += "&data=" + data; + } + if (typeof useServerCache !== 'undefined' && !useServerCache) { + postData += "&nocache=true"; + } + print("Sending: " + postData); + xhr.send(postData); +} diff --git a/unpublishedScripts/interaction/NPC_AC.js b/unpublishedScripts/interaction/NPC_AC.js new file mode 100644 index 0000000000..6eaee6a6e3 --- /dev/null +++ b/unpublishedScripts/interaction/NPC_AC.js @@ -0,0 +1,102 @@ +// +// NPC_AC.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var currentlyUsedIndices = []; +var timers = []; +var currentlyEngaged = false; +var questionNumber = 0; +var heartbeatTimeout = false; +function getRandomRiddle() { + var randIndex = null; + do { + randIndex = Math.floor(Math.random() * 15) + 1; + } while (randIndex in currentlyUsedIndices); + + currentlyUsedIndices.push(randIndex); + return randIndex.toString(); +} + +Script.include("https://raw.githubusercontent.com/Delamare2112/hifi/Interaction/unpublishedScripts/interaction/NPCHelpers.js", function(){ + print("NPCHelpers included.");main(); +}); + +var idleAnim = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/idle.fbx"; +var FST = "https://s3.amazonaws.com/hifi-public/tony/fixed-sphinx/sphinx.fst"; + +Agent.isAvatar = true; +Avatar.skeletonModelURL = FST; +Avatar.displayName = "NPC"; +Avatar.position = {x: 0.3, y: -23.4, z: 8.0}; +Avatar.orientation = {x: 0, y: 1, z: 0, w: 0}; +// Avatar.position = {x: 1340.3555, y: 4.078, z: -420.1562}; +// Avatar.orientation = {x: 0, y: -0.707, z: 0, w: 0.707}; +Avatar.scale = 2; + +Messages.subscribe("interactionComs"); + +function endInteraction() { + print("ending interaction"); + blocked = false; + currentlyEngaged = false; + if(audioInjector) + audioInjector.stop(); + for (var t in timers) { + Script.clearTimeout(timers[t]); + } + if(_qid != "Restarting") { + npcRespondBlocking( + 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/EarlyExit_0' + (Math.floor(Math.random() * 2) + 1).toString() + '.wav', + 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx', + function(){ + Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0); + } + ); + } +} + +function main() { + storyURL = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Sphinx.json"; + Messages.messageReceived.connect(function (channel, message, sender) { + if(!strContains(message, 'beat')) + print(sender + " -> NPC @" + Agent.sessionUUID + ": " + message); + if (channel === "interactionComs" && strContains(message, Agent.sessionUUID)) { + if (strContains(message, 'beat')) { + if(heartbeatTimeout) { + Script.clearTimeout(heartbeatTimeout); + heartbeatTimeout = false; + } + heartbeatTimeout = Script.setTimeout(endInteraction, 1500); + } + else if (strContains(message, "onFocused") && !currentlyEngaged) { + blocked = false; + currentlyEngaged = true; + currentlyUsedIndices = []; + doActionFromServer("start"); + } else if (strContains(message, "leftArea")) { + + } else if (strContains(message, "speaking")) { + + } else { + var voiceDataIndex = message.search("voiceData"); + if (voiceDataIndex != -1) { + var words = message.substr(voiceDataIndex+10); + if (!isNaN(_qid) && (strContains(words, "repeat") || (strContains(words, "say") && strContains(words, "again")))) { + doActionFromServer("init"); + } else { + doActionFromServer("words", words); + } + } + } + } + }); + // Script.update.connect(updateGem); + Avatar.startAnimation("https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx", 0); +} diff --git a/unpublishedScripts/interaction/Sphinx.json b/unpublishedScripts/interaction/Sphinx.json new file mode 100644 index 0000000000..2a76417fd7 --- /dev/null +++ b/unpublishedScripts/interaction/Sphinx.json @@ -0,0 +1,159 @@ +{ + "Name": "10 Questions", + "Defaults": + { + "Actions": + { + "positive": "var x=function(){if(questionNumber>=2){setQid('Finished');return;}var suffix=['A', 'B'][questionNumber++] + '_0' + (Math.floor(Math.random() * 2) + 2).toString() + '.wav';npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/RightAnswer'+suffix, 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/RightAnswerB_02.fbx', getRandomRiddle());};x();", + "unknown": "var suffix=(Math.floor(Math.random() * 3) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/WrongAnswer_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/WrongAnswer_0' + suffix + '.fbx', getRandomRiddle());", + "hint": "var suffix=(Math.floor(Math.random() * 2) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Hint_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hint_0' + suffix + '.fbx')" + }, + "Responses": + { + "positive": ["yes","yup","yeah","yahoo","sure","affirmative","okay","aye","right","exactly","course","naturally","unquestionably","positively","yep","definitely","certainly","fine","absolutely","positive","love","fantastic"], + "thinking": ["oh", "think about", "i know", "what was", "well", "not sure", "one before", "hold", "one moment", "one second", "1 second", "1 sec", "one sec"], + "hint": ["hint", "heads"] + } + }, + "Story": + [ + { + "QID": "start", + "init": "questionNumber=0;npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/HiFi_Sphinx_Anim_Combined_Entrance_Audio.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', getRandomRiddle());" + }, + { + "QID": "1", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Blackboard.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Blackboard.fbx');", + "responses": + { + "positive": ["blackboard", "chalkboard", "chalk board", "slate"] + } + }, + { + "QID": "2", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Breath.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Breath.fbx');", + "responses": + { + "positive": ["breath", "death"] + } + }, + { + "QID": "3", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Clock.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Clock.fbx');", + "responses": + { + "positive": ["clock", "cock"] + } + }, + { + "QID": "4", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coffin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coffin.fbx');", + "responses": + { + "positive": ["coffin", "casket", "possum"] + } + }, + { + "QID": "5", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coin.fbx');", + "responses": + { + "positive": ["coin", "boing", "coinage", "coin piece", "change", "join"] + } + }, + { + "QID": "6", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Corn.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Corn.fbx');", + "responses": + { + "positive": ["corn", "born", "maize", "maze", "means", "torn", "horn", "worn", "porn"] + } + }, + { + "QID": "7", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Darkness.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Darkness.fbx');", + "responses": + { + "positive": ["darkness", "dark", "blackness"] + } + }, + { + "QID": "8", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gloves.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gloves.fbx');", + "responses": + { + "positive": ["gloves", "love"] + } + }, + { + "QID": "9", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gold.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gold.fbx');", + "responses": + { + "positive": ["gold", "old", "bold", "cold", "told"] + } + }, + { + "QID": "10", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_River.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_River.fbx');", + "responses": + { + "positive": ["river", "bigger", "stream", "creek", "brook"] + } + }, + { + "QID": "11", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Secret.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Secret.fbx');", + "responses": + { + "positive": ["secret"] + } + }, + { + "QID": "12", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Shadow.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Shadow.fbx');", + "responses": + { + "positive": ["shadow"] + } + }, + { + "QID": "13", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Silence.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Silence.fbx');", + "responses": + { + "positive": ["silence", "lance", "quiet"] + } + }, + { + "QID": "14", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Stairs.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Stairs.fbx');", + "responses": + { + "positive": ["stairs", "steps", "stair", "stairwell", "there's", "stairway"] + } + }, + { + "QID": "15", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Umbrella.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Umbrella.fbx');", + "responses": + { + "positive": ["umbrella"] + } + }, + { + "QID": "Finished", + "init": "Script.clearTimeout(heartbeatTimeout);heartbeatTimeout = false;npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/ConclusionRight_02.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/ConclusionRight_02.fbx', function(){runOnClient('MyAvatar.goToLocation({x: 5, y: -29, z: -63}, true, true);');setQid('Restarting');});", + "positive": "", + "negative": "", + "unknown": "" + }, + { + "QID": "Restarting", + "init": "npcRespondBlocking('', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx', function(){Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0);_qid='';});", + "positive": "", + "negative": "", + "unknown": "" + } + ] +} From 1e6b2682c96ce8da9fb1c1a2140c8cf21b4711bc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Apr 2017 10:17:57 -0700 Subject: [PATCH 004/128] use timers rather than relying on update calls --- scripts/system/controllers/handControllerGrab.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 464101a4e3..1091906de7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3984,7 +3984,7 @@ var updateTotalWork = 0; var UPDATE_PERFORMANCE_DEBUGGING = false; -function updateWrapper(){ +var updateWrapper = function () { intervalCount++; var thisInterval = Date.now(); @@ -4032,12 +4032,14 @@ function updateWrapper(){ updateTotalWork = 0; } + Script.setTimeout(updateWrapper, 16); } -Script.update.connect(updateWrapper); +// Script.update.connect(updateWrapper); +Script.setTimeout(updateWrapper, 16); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - Script.update.disconnect(updateWrapper); + // Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From ec687bdb4b90ea6ec673dc212581618b3e9a7cf6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Apr 2017 11:03:43 -0700 Subject: [PATCH 005/128] cleanups --- scripts/system/controllers/handControllerGrab.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1091906de7..a724f19be7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -31,6 +31,8 @@ var WANT_DEBUG = false; var WANT_DEBUG_STATE = false; var WANT_DEBUG_SEARCH_NAME = null; +var UPDATE_SLEEP_MS = 16; // how many milliseconds to wait between "update" calls + var FORCE_IGNORE_IK = false; var SHOW_GRAB_POINT_SPHERE = false; @@ -4032,14 +4034,12 @@ var updateWrapper = function () { updateTotalWork = 0; } - Script.setTimeout(updateWrapper, 16); + Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); } -// Script.update.connect(updateWrapper); -Script.setTimeout(updateWrapper, 16); +Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - // Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From 37233b9fdb8e2a2d68a3cba1d6afd95d8857f85c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Apr 2017 10:19:50 -0700 Subject: [PATCH 006/128] avoid a rare script-crash --- scripts/system/controllers/grab.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 10f477b3af..f96ad81719 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -447,6 +447,10 @@ Grabber.prototype.moveEvent = function(event) { // see if something added/restored gravity var entityProperties = Entities.getEntityProperties(this.entityID); + if (!entityProperties || !entityProperties.gravity) { + return; + } + if (Vec3.length(entityProperties.gravity) !== 0.0) { this.originalGravity = entityProperties.gravity; } From 186b86e3d49994ccee9a441c1135bbee050ebc09 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Apr 2017 10:23:20 -0700 Subject: [PATCH 007/128] load stylus model before it's needed so that it will appear quickly --- scripts/system/tablet-ui/tabletUI.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index dbfd3e632e..cacd9663ab 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,7 +13,7 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu */ + MyAvatar, Menu, Vec3 */ (function() { // BEGIN LOCAL_SCOPE var tabletRezzed = false; @@ -214,6 +214,23 @@ closeTabletUI(); rezTablet(); tabletShown = false; + + // also cause the stylus model to be loaded + var tmpStylusID = Overlays.addOverlay("model", { + name: "stylus", + url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), + dimensions: { x: 0.01, y: 0.01, z: 0.2 }, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false, + lifetime: 3 + }); + Script.setTimeout(function() { + Overlays.deleteOverlay(tmpStylusID); + }, 300); + } else if (!tabletShown) { hideTabletUI(); } From 15661987be71f732859a39cdb91a4c4bd55a02f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 10:16:08 -0700 Subject: [PATCH 008/128] load tablet overlay models with higher priority --- interface/src/ui/overlays/ModelOverlay.cpp | 11 ++++++++++- interface/src/ui/overlays/ModelOverlay.h | 3 +++ scripts/system/controllers/handControllerGrab.js | 1 + scripts/system/libraries/WebTablet.js | 1 + scripts/system/tablet-ui/tabletUI.js | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ccaa1d4fbc..07fc2a2b9d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -22,6 +22,7 @@ ModelOverlay::ModelOverlay() _modelTextures(QVariantMap()) { _model->init(); + _model->setLoadingPriority(_loadPriority); _isLoaded = false; } @@ -30,9 +31,11 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), - _updateModel(false) + _updateModel(false), + _loadPriority(modelOverlay->getLoadPriority()) { _model->init(); + _model->setLoadingPriority(_loadPriority); if (_url.isValid()) { _updateModel = true; _isLoaded = false; @@ -113,6 +116,12 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { _updateModel = true; } + auto loadPriorityProperty = properties["loadPriority"]; + if (loadPriorityProperty.isValid()) { + _loadPriority = loadPriorityProperty.toFloat(); + _model->setLoadingPriority(_loadPriority); + } + auto urlValue = properties["url"]; if (urlValue.isValid() && urlValue.canConvert()) { _url = urlValue.toString(); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index a3ddeed480..e389181879 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -41,6 +41,8 @@ public: void locationChanged(bool tellPhysics) override; + float getLoadPriority() const { return _loadPriority; } + private: ModelPointer _model; @@ -49,6 +51,7 @@ private: QUrl _url; bool _updateModel = { false }; bool _scaleToFit = { false }; + float _loadPriority { 0.0f }; }; #endif // hifi_ModelOverlay_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 8a1ee8dff4..992c20ef72 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1336,6 +1336,7 @@ function MyController(hand) { var stylusProperties = { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, localPosition: Vec3.sum({ x: 0.0, y: WEB_TOUCH_Y_OFFSET, z: 0.0 }, diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index e71aefc51e..6aa3c4de8a 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -120,6 +120,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location) { modelURL: modelURL, url: modelURL, // for overlay grabbable: true, // for overlay + loadPriority: 10.0, // for overlay userData: JSON.stringify({ "grabbableKey": {"grabbable": true} }), diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index cacd9663ab..8a7d0a2ec4 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -219,6 +219,7 @@ var tmpStylusID = Overlays.addOverlay("model", { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), dimensions: { x: 0.01, y: 0.01, z: 0.2 }, solid: true, From ef27d30e85cf36c3847f30bc25cd64b18689d077 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:37 -0700 Subject: [PATCH 009/128] Fix NetworkTexture self possibly being null when attempting request --- .../model-networking/src/model-networking/TextureCache.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 55704236e3..c2d947baab 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -394,12 +394,17 @@ void NetworkTexture::startRequestForNextMipLevel() { } if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { + auto self = _self.lock(); + if (!self) { + return; + } + _ktxResourceState = PENDING_MIP_REQUEST; init(); setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); - TextureCache::attemptRequest(_self); + TextureCache::attemptRequest(self); } } From 314e2558a465ec1f5f78336950549d86c0f6baa7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:55 -0700 Subject: [PATCH 010/128] Fix style in NetworkTexture --- .../src/model-networking/TextureCache.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c2d947baab..680550d9a1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -473,19 +473,16 @@ void NetworkTexture::ktxMipRequestFinished() { texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); - } - else { + } else { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } - else { + } else { finishedLoading(false); if (handleFailedRequest(_ktxMipRequest->getResult())) { _ktxResourceState = PENDING_MIP_REQUEST; - } - else { + } else { qWarning(networking) << "Failed to load mip: " << _url; _ktxResourceState = FAILED_TO_LOAD; } @@ -497,8 +494,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - } - else { + } else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } } From ae2dc385c325a87ea1c504ad9aac0e10283053c1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:04 -0700 Subject: [PATCH 011/128] Fix gpu access of ktx file not being thread-safe --- libraries/gpu/src/gpu/Texture.h | 8 ++++---- libraries/gpu/src/gpu/Texture_ktx.cpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9b23b4e695..3b8ae508eb 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -324,11 +324,11 @@ public: void reset() override { } protected: - std::shared_ptr maybeOpenFile(); + std::shared_ptr maybeOpenFile() const; - std::mutex _cacheFileCreateMutex; - std::mutex _cacheFileWriteMutex; - std::weak_ptr _cacheFile; + mutable std::mutex _cacheFileCreateMutex; + mutable std::mutex _cacheFileWriteMutex; + mutable std::weak_ptr _cacheFile; std::string _filename; std::atomic _minMipLevelAvailable; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index efff6c7afe..d2f93c0036 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -128,7 +128,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } } -std::shared_ptr KtxStorage::maybeOpenFile() { +std::shared_ptr KtxStorage::maybeOpenFile() const { std::shared_ptr file = _cacheFile.lock(); if (file) { return file; @@ -154,7 +154,8 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); if (faceSize != 0 && faceOffset != 0) { - result = std::make_shared(_filename.c_str())->createView(faceSize, faceOffset)->toMemoryStorage(); + auto file = maybeOpenFile(); + result = file->createView(faceSize, faceOffset)->toMemoryStorage(); } return result; } From 16af62b15f733ace81ab111fbd94c3527a5eda3b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:42 -0700 Subject: [PATCH 012/128] Fix NetworkTexture not cleaning itself up on destruction --- .../src/model-networking/TextureCache.cpp | 16 ++++++++++++++++ .../src/model-networking/TextureCache.h | 1 + libraries/networking/src/ResourceCache.h | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 680550d9a1..7aee95c758 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -330,6 +330,22 @@ private: int _maxNumPixels; }; +NetworkTexture::~NetworkTexture() { + if (_ktxHeaderRequest || _ktxMipRequest) { + if (_ktxHeaderRequest) { + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + } + if (_ktxMipRequest) { + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + } + ResourceCache::requestCompleted(_self); + } +} + const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max(); void NetworkTexture::makeRequest() { if (!_sourceIsKTX) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1e61b9ecee..c7a7799216 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture { public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); + ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d4c7d63ee5..51c8d8554a 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -344,7 +344,7 @@ class Resource : public QObject { public: Resource(const QUrl& url); - ~Resource(); + virtual ~Resource(); virtual QString getType() const { return "Resource"; } From 40e9f9025fb6ac2f7d2e0301ec7e7ed94d63eb80 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 27 Apr 2017 10:47:07 -0700 Subject: [PATCH 013/128] Load High Mips before Fbx after skybox --- .../src/model-networking/TextureCache.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7aee95c758..961b3b3e7b 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,7 +50,8 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.k const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; -static const int SKYBOX_LOAD_PRIORITY { 10 }; // Make sure skybox loads first +static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads first +static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models TextureCache::TextureCache() : _ktxCache(KTX_DIRNAME, KTX_EXT) { @@ -261,9 +262,6 @@ QSharedPointer TextureCache::createResource(const QUrl& url, const QSh auto content = textureExtra ? textureExtra->content : QByteArray(); auto maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; NetworkTexture* texture = new NetworkTexture(url, type, content, maxNumPixels); - if (type == image::TextureUsage::CUBE_TEXTURE) { - texture->setLoadPriority(this, SKYBOX_LOAD_PRIORITY); - } return QSharedPointer(texture, &Resource::deleter); } @@ -276,6 +274,12 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _textureSource = std::make_shared(); _lowestRequestedMipLevel = 0; + if (type == image::TextureUsage::CUBE_TEXTURE) { + setLoadPriority(this, SKYBOX_LOAD_PRIORITY); + } else if (_sourceIsKTX) { + setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY); + } + if (!url.isValid()) { _loaded = true; } @@ -418,7 +422,8 @@ void NetworkTexture::startRequestForNextMipLevel() { _ktxResourceState = PENDING_MIP_REQUEST; init(); - setLoadPriority(this, -static_cast(_originalKtxDescriptor->header.numberOfMipmapLevels) + _lowestKnownPopulatedMip); + float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; + setLoadPriority(this, priority); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); TextureCache::attemptRequest(self); } From 4bc92c025df48782659a247183df2ab50c86ef5d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 14:01:55 -0700 Subject: [PATCH 014/128] Fix ResourceCache warning on OSX --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 961b3b3e7b..7745766177 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -346,7 +346,7 @@ NetworkTexture::~NetworkTexture() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); } } From 945952d51190dd300a62960940d68dd4e007393c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:32:06 -0700 Subject: [PATCH 015/128] don't save ATP requests to cache if they are range requests --- libraries/networking/src/AssetRequest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 341c3b45da..9c756b0060 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -113,8 +113,10 @@ void AssetRequest::start() { _data = data; _totalReceived += data.size(); emit progress(_totalReceived, data.size()); - - saveToCache(getUrl(), data); + + if (!_byteRange.isSet()) { + saveToCache(getUrl(), data); + } } } From bc89af28423cb55d11e4f9d7a6f60082e59dcbb8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:26:03 -0700 Subject: [PATCH 016/128] Fix stuck ATP downloads Occasionally ATP requests would get stuck because of a race inside AssetClient::handleAssetReply. This was more likely to happen to small resources. --- libraries/networking/src/AssetClient.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 15e0b8c9b5..054557e920 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -348,18 +348,19 @@ void AssetClient::handleAssetGetReply(QSharedPointer message, S // Store message in case we need to disconnect from it later. callbacks.message = message; + + auto weakNode = senderNode.toWeakRef(); + connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { + handleProgressCallback(weakNode, messageID, size, length); + }); + connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { + handleCompleteCallback(weakNode, messageID); + }); + if (message->isComplete()) { + disconnect(message.data(), nullptr, this, nullptr); callbacks.completeCallback(true, error, message->readAll()); messageCallbackMap.erase(requestIt); - } else { - auto weakNode = senderNode.toWeakRef(); - - connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) { - handleProgressCallback(weakNode, messageID, size, length); - }); - connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() { - handleCompleteCallback(weakNode, messageID); - }); } } From 46d4504f1540a5c8ccb5ac1034f09d7ffe5f8fa1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:27:44 -0700 Subject: [PATCH 017/128] Fix poorly indented if in AssetRequest --- libraries/networking/src/AssetRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 9c756b0060..920c7ae036 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -77,7 +77,7 @@ void AssetRequest::start() { _assetRequestID = assetClient->getAsset(_hash, _byteRange.fromInclusive, _byteRange.toExclusive, [this, that, hash](bool responseReceived, AssetServerError serverError, const QByteArray& data) { - if (!that) { + if (!that) { qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; // If the request is dead, return return; From 074a11306c02e850774a237f588d75296f63e099 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 13:21:52 -0700 Subject: [PATCH 018/128] Add support for atp and file urls in OBJReader --- libraries/fbx/src/OBJReader.cpp | 314 +++++++++--------- libraries/fbx/src/OBJReader.h | 1 - .../networking/src/AssetResourceRequest.cpp | 6 +- .../networking/src/AssetResourceRequest.h | 2 +- libraries/networking/src/ResourceManager.cpp | 2 +- 5 files changed, 167 insertions(+), 158 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 7b46556530..167cb8caac 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "FBXReader.h" #include "ModelFormatLogging.h" @@ -165,6 +166,7 @@ bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, } return true; } + QVector OBJFace::triangulate() { QVector newFaces; const int nVerticesInATriangle = 3; @@ -183,6 +185,7 @@ QVector OBJFace::triangulate() { } return newFaces; } + void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i vertexIndices.append(face->vertexIndices[index]); if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. @@ -193,24 +196,13 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } -static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous - return (netReply && netReply->isFinished() && - (url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes - netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() : - (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200))); -} - bool OBJReader::isValidTexture(const QByteArray &filename) { if (_url.isEmpty()) { return false; } QUrl candidateUrl = _url.resolved(QUrl(filename)); - QNetworkReply *netReply = request(candidateUrl, true); - bool isValid = replyOK(netReply, candidateUrl); - if (netReply) { - netReply->deleteLater(); - } - return isValid; + + return ResourceManager::resourceExists(candidateUrl); } void OBJReader::parseMaterialLibrary(QIODevice* device) { @@ -274,7 +266,28 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } } -QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { +std::tuple requestData(QUrl& url) { + auto request = ResourceManager::createResourceRequest(nullptr, url); + + if (!request) { + return std::make_tuple(false, QByteArray()); + } + + request->send(); + + QEventLoop loop; + QObject::connect(request, &ResourceRequest::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (request->getResult() == ResourceRequest::Success) { + return std::make_tuple(true, request->getData()); + } else { + return std::make_tuple(false, QByteArray()); + } +} + + +QNetworkReply* request(QUrl& url, bool isTest) { if (!qApp) { return nullptr; } @@ -293,10 +306,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); // Nothing is going to happen on this whole run thread until we get this - static const int WAIT_TIMEOUT_MS = 500; - while (!aboutToQuit && qApp && !netReply->isReadable()) { - netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than - } + QObject::disconnect(connection); return netReply; // trying to sync later on. } @@ -446,142 +456,142 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // add a new meshPart to the geometry's single mesh. while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {} - FBXMesh& mesh = geometry.meshes[0]; - mesh.meshIndex = 0; + FBXMesh& mesh = geometry.meshes[0]; + mesh.meshIndex = 0; - geometry.joints.resize(1); - geometry.joints[0].isFree = false; - geometry.joints[0].parentIndex = -1; - geometry.joints[0].distanceToParent = 0; - geometry.joints[0].translation = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[0].name = "OBJ"; - geometry.joints[0].isSkeletonJoint = true; + geometry.joints.resize(1); + geometry.joints[0].isFree = false; + geometry.joints[0].parentIndex = -1; + geometry.joints[0].distanceToParent = 0; + geometry.joints[0].translation = glm::vec3(0, 0, 0); + geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); + geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); + geometry.joints[0].name = "OBJ"; + geometry.joints[0].isSkeletonJoint = true; - geometry.jointIndices["x"] = 1; + geometry.jointIndices["x"] = 1; - FBXCluster cluster; - cluster.jointIndex = 0; - cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - mesh.clusters.append(cluster); + FBXCluster cluster; + cluster.jointIndex = 0; + cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + mesh.clusters.append(cluster); - QMap materialMeshIdMap; - QVector fbxMeshParts; - for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { - FBXMeshPart& meshPart = mesh.parts[i]; - FaceGroup faceGroup = faceGroups[meshPartCount]; + QMap materialMeshIdMap; + QVector fbxMeshParts; + for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { + FBXMeshPart& meshPart = mesh.parts[i]; + FaceGroup faceGroup = faceGroups[meshPartCount]; bool specifiesUV = false; - foreach(OBJFace face, faceGroup) { - // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). - // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. - if (!materialMeshIdMap.contains(face.materialName)) { - // Create a new FBXMesh for this material mapping. - materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); + foreach(OBJFace face, faceGroup) { + // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). + // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. + if (!materialMeshIdMap.contains(face.materialName)) { + // Create a new FBXMesh for this material mapping. + materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - fbxMeshParts.append(FBXMeshPart()); - FBXMeshPart& meshPartNew = fbxMeshParts.last(); - meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. + fbxMeshParts.append(FBXMeshPart()); + FBXMeshPart& meshPartNew = fbxMeshParts.last(); + meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. - // Do some of the material logic (which previously lived below) now. - // All the faces in the same group will have the same name and material. - QString groupMaterialName = face.materialName; - if (groupMaterialName.isEmpty() && specifiesUV) { + // Do some of the material logic (which previously lived below) now. + // All the faces in the same group will have the same name and material. + QString groupMaterialName = face.materialName; + if (groupMaterialName.isEmpty() && specifiesUV) { #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader WARNING: " << url - << " needs a texture that isn't specified. Using default mechanism."; + qCDebug(modelformat) << "OBJ Reader WARNING: " << url + << " needs a texture that isn't specified. Using default mechanism."; #endif - groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; - } - if (!groupMaterialName.isEmpty()) { - OBJMaterial& material = materials[groupMaterialName]; - if (specifiesUV) { - material.userSpecifiesUV = true; // Note might not be true in a later usage. - } - if (specifiesUV || (groupMaterialName.compare("none", Qt::CaseInsensitive) != 0)) { - // Blender has a convention that a material named "None" isn't really used (or defined). - material.used = true; - needsMaterialLibrary = groupMaterialName != SMART_DEFAULT_MATERIAL_NAME; - } - materials[groupMaterialName] = material; - meshPartNew.materialID = groupMaterialName; - } - } - } - } - - // clean up old mesh parts. - int unmodifiedMeshPartCount = mesh.parts.count(); - mesh.parts.clear(); - mesh.parts = QVector(fbxMeshParts); - - for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { - FaceGroup faceGroup = faceGroups[meshPartCount]; - - // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). - foreach(OBJFace face, faceGroup) { - FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; - - glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); - glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); - glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); - - // Scale the vertices if the OBJ file scale is specified as non-one. - if (scaleGuess != 1.0f) { - v0 *= scaleGuess; - v1 *= scaleGuess; - v2 *= scaleGuess; - } - - // Add the vertices. - meshPart.triangleIndices.append(mesh.vertices.count()); // not face.vertexIndices into vertices - mesh.vertices << v0; - meshPart.triangleIndices.append(mesh.vertices.count()); - mesh.vertices << v1; - meshPart.triangleIndices.append(mesh.vertices.count()); - mesh.vertices << v2; - - glm::vec3 n0, n1, n2; - if (face.normalIndices.count()) { - n0 = checked_at(normals, face.normalIndices[0]); - n1 = checked_at(normals, face.normalIndices[1]); - n2 = checked_at(normals, face.normalIndices[2]); - } else { - // generate normals from triangle plane if not provided - n0 = n1 = n2 = glm::cross(v1 - v0, v2 - v0); - } - - mesh.normals.append(n0); - mesh.normals.append(n1); - mesh.normals.append(n2); - - if (face.textureUVIndices.count()) { - mesh.texCoords << - checked_at(textureUVs, face.textureUVIndices[0]) << - checked_at(textureUVs, face.textureUVIndices[1]) << - checked_at(textureUVs, face.textureUVIndices[2]); - } else { - glm::vec2 corner(0.0f, 1.0f); - mesh.texCoords << corner << corner << corner; - } - } + groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; + } + if (!groupMaterialName.isEmpty()) { + OBJMaterial& material = materials[groupMaterialName]; + if (specifiesUV) { + material.userSpecifiesUV = true; // Note might not be true in a later usage. + } + if (specifiesUV || (groupMaterialName.compare("none", Qt::CaseInsensitive) != 0)) { + // Blender has a convention that a material named "None" isn't really used (or defined). + material.used = true; + needsMaterialLibrary = groupMaterialName != SMART_DEFAULT_MATERIAL_NAME; + } + materials[groupMaterialName] = material; + meshPartNew.materialID = groupMaterialName; + } + } + } } - mesh.meshExtents.reset(); - foreach(const glm::vec3& vertex, mesh.vertices) { - mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); - } + // clean up old mesh parts. + int unmodifiedMeshPartCount = mesh.parts.count(); + mesh.parts.clear(); + mesh.parts = QVector(fbxMeshParts); - // Build the single mesh. - FBXReader::buildModelMesh(mesh, url.toString()); + for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { + FaceGroup faceGroup = faceGroups[meshPartCount]; - // fbxDebugDump(geometry); + // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). + foreach(OBJFace face, faceGroup) { + FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; + + glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); + glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); + glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); + + // Scale the vertices if the OBJ file scale is specified as non-one. + if (scaleGuess != 1.0f) { + v0 *= scaleGuess; + v1 *= scaleGuess; + v2 *= scaleGuess; + } + + // Add the vertices. + meshPart.triangleIndices.append(mesh.vertices.count()); // not face.vertexIndices into vertices + mesh.vertices << v0; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v1; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v2; + + glm::vec3 n0, n1, n2; + if (face.normalIndices.count()) { + n0 = checked_at(normals, face.normalIndices[0]); + n1 = checked_at(normals, face.normalIndices[1]); + n2 = checked_at(normals, face.normalIndices[2]); + } else { + // generate normals from triangle plane if not provided + n0 = n1 = n2 = glm::cross(v1 - v0, v2 - v0); + } + + mesh.normals.append(n0); + mesh.normals.append(n1); + mesh.normals.append(n2); + + if (face.textureUVIndices.count()) { + mesh.texCoords << + checked_at(textureUVs, face.textureUVIndices[0]) << + checked_at(textureUVs, face.textureUVIndices[1]) << + checked_at(textureUVs, face.textureUVIndices[2]); + } else { + glm::vec2 corner(0.0f, 1.0f); + mesh.texCoords << corner << corner << corner; + } + } + } + + mesh.meshExtents.reset(); + foreach(const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + geometry.meshExtents.addPoint(vertex); + } + + // Build the single mesh. + FBXReader::buildModelMesh(mesh, url.toString()); + + // fbxDebugDump(geometry); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } @@ -624,15 +634,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // Throw away any path part of libraryName, and merge against original url. QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName()); qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url; - QNetworkReply* netReply = request(libraryUrl, false); - if (replyOK(netReply, libraryUrl)) { - parseMaterialLibrary(netReply); + bool success; + QByteArray data; + std::tie(success, data) = requestData(libraryUrl); + if (success) { + QBuffer buffer { &data }; + buffer.open(QIODevice::ReadOnly); + parseMaterialLibrary(&buffer); } else { - qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got" - << (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()); - } - if (netReply) { - netReply->deleteLater(); + qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer"; } } } @@ -655,9 +665,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } - if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; - } + if (!objMaterial.specularTextureFilename.isEmpty()) { + fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 4be5705f9a..18a4b89f1e 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -72,7 +72,6 @@ public: QString currentMaterialName; QHash materials; - QNetworkReply* request(QUrl& url, bool isTest); FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 092e0ccb3d..a4d5d66923 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -40,16 +40,16 @@ AssetResourceRequest::~AssetResourceRequest() { } } -bool AssetResourceRequest::urlIsAssetHash() const { +bool AssetResourceRequest::urlIsAssetHash(const QUrl& url) { static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" }; QRegExp hashRegex { ATP_HASH_REGEX_STRING }; - return hashRegex.exactMatch(_url.toString()); + return hashRegex.exactMatch(url.toString()); } void AssetResourceRequest::doSend() { // We'll either have a hash or an ATP path to a file (that maps to a hash) - if (urlIsAssetHash()) { + if (urlIsAssetHash(_url)) { // We've detected that this is a hash - simply use AssetClient to request that asset auto parts = _url.path().split(".", QString::SkipEmptyParts); auto hash = parts.length() > 0 ? parts[0] : ""; diff --git a/libraries/networking/src/AssetResourceRequest.h b/libraries/networking/src/AssetResourceRequest.h index 3f110fae17..18b82f2573 100644 --- a/libraries/networking/src/AssetResourceRequest.h +++ b/libraries/networking/src/AssetResourceRequest.h @@ -32,7 +32,7 @@ private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); private: - bool urlIsAssetHash() const; + static bool urlIsAssetHash(const QUrl& url); void requestMappingForPath(const AssetPath& path); void requestHash(const AssetHash& hash); diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 439d44c940..6492e9171e 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -14,10 +14,10 @@ #include #include #include +#include #include - #include "AssetResourceRequest.h" #include "FileResourceRequest.h" #include "HTTPResourceRequest.h" From c839118c6be5de2ac17366a97f42175a83e91e7a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 13:22:23 -0700 Subject: [PATCH 019/128] Add ResourceManager::resourceExists --- libraries/networking/src/ResourceManager.cpp | 48 ++++++++++++++++++++ libraries/networking/src/ResourceManager.h | 4 ++ 2 files changed, 52 insertions(+) diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 6492e9171e..e2c1cf2431 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -116,3 +116,51 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q request->moveToThread(&_thread); return request; } + + +bool ResourceManager::resourceExists(const QUrl& url) { + auto scheme = url.scheme(); + if (scheme == URL_SCHEME_FILE) { + QFileInfo file { url.toString() }; + return file.exists(); + } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { + auto& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest request { url }; + + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + + auto reply = networkAccessManager.head(request); + + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + reply->deleteLater(); + + return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200; + } else if (scheme == URL_SCHEME_ATP) { + auto request = new AssetResourceRequest(url); + ByteRange range; + range.fromInclusive = 1; + range.toExclusive = 1; + request->setByteRange(range); + request->setCacheEnabled(false); + + QEventLoop loop; + + QObject::connect(request, &AssetResourceRequest::finished, &loop, &QEventLoop::quit); + + request->send(); + + loop.exec(); + + request->deleteLater(); + + return request->getResult() == ResourceRequest::Success; + } + + qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); + return false; +} + diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index d193c39cae..41da892701 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -36,6 +36,10 @@ public: static void init(); static void cleanup(); + // Blocking call to check if a resource exists. This function uses a QEventLoop internally + // to return to the calling thread so that events can still be processed. + static bool resourceExists(const QUrl& url); + private: static QThread _thread; From 3d6778e9cdd722ed1bef438179abb0f9014706a3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 15:02:23 -0700 Subject: [PATCH 020/128] Add tutorial Changelog.md --- tutorial/Changelog.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tutorial/Changelog.md diff --git a/tutorial/Changelog.md b/tutorial/Changelog.md new file mode 100644 index 0000000000..bd923b6841 --- /dev/null +++ b/tutorial/Changelog.md @@ -0,0 +1,3 @@ + * home-tutorial-34 + * Update tutorial to only start if `HMD.active` + * Update builder's grid to use "Good - Sub-meshes" for collision shape type From dd739d73a01d8d4a178c4a7659a04914262d9e28 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 2 May 2017 12:01:55 +1200 Subject: [PATCH 021/128] Don't warn that recording didn't start playing if you stop it --- scripts/system/record.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/system/record.js b/scripts/system/record.js index 9b231f64f1..3db82696ef 100644 --- a/scripts/system/record.js +++ b/scripts/system/record.js @@ -273,10 +273,11 @@ PLAYER_COMMAND_PLAY = "play", PLAYER_COMMAND_STOP = "stop", - playerIDs = [], // UUIDs of AC player scripts. - playerIsPlayings = [], // True if AC player script is playing a recording. - playerRecordings = [], // Assignment client mappings of recordings being played. - playerTimestamps = [], // Timestamps of last heartbeat update from player script. + playerIDs = [], // UUIDs of AC player scripts. + playerIsPlayings = [], // True if AC player script is playing a recording. + playerRecordings = [], // Assignment client mappings of recordings being played. + playerTimestamps = [], // Timestamps of last heartbeat update from player script. + playerStartupTimeouts = [], // Timers that check that recording has started playing. updateTimer, UPDATE_INTERVAL = 5000; // Must be > player's HEARTBEAT_INTERVAL. @@ -297,6 +298,7 @@ playerIsPlayings.splice(i, 1); playerRecordings.splice(i, 1); playerTimestamps.splice(i, 1); + playerStartupTimeouts.splice(i, 1); } } @@ -333,16 +335,25 @@ orientation: orientation })); - Script.setTimeout(function () { - if (!playerIsPlayings[index] || playerRecordings[index] !== recording) { + playerStartupTimeouts[index] = Script.setTimeout(function () { + if ((!playerIsPlayings[index] || playerRecordings[index] !== recording) && playerStartupTimeouts[index]) { error("Didn't start playing recording " + recording.slice(4) + "!"); // Remove leading "atp:" from recording. } + playerStartupTimeouts[index] = null; }, CHECK_PLAYING_TIMEOUT); - } function stopPlayingRecording(playerID) { + var index; + + // Cancel check that recording started playing. + index = playerIDs.indexOf(playerID); + if (index !== -1 && playerStartupTimeouts[index] !== null) { + // Cannot clearTimeout() without program log error, so just set null. + playerStartupTimeouts[index] = null; + } + Messages.sendMessage(HIFI_PLAYER_CHANNEL, JSON.stringify({ player: playerID, command: PLAYER_COMMAND_STOP @@ -375,6 +386,7 @@ playerIsPlayings = []; playerRecordings = []; playerTimestamps = []; + playerStartupTimeouts = []; Dialog.updatePlayerDetails(playerIsPlayings, playerRecordings, playerIDs); } From 71deac49c6311d26c79511a2597c4f1e986c490f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 May 2017 17:06:15 -0700 Subject: [PATCH 022/128] reset tablet to homescreen when going from 2d to vr --- interface/resources/qml/hifi/tablet/Tablet.qml | 6 +++++- libraries/script-engine/src/TabletScriptingInterface.cpp | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 8ad6339d88..18f88b7718 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -65,7 +65,11 @@ Item { }); // pass a reference to the tabletRoot object to the button. - button.tabletRoot = parent.parent; + if (tabletRoot) { + button.tabletRoot = tabletRoot; + } else { + button.tabletRoot = parent.parent; + } sortButtons(); diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index bffe318c11..b8dae0954a 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -216,8 +216,10 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); }); } else { + _state = State::Home; removeButtonsFromToolbar(); addButtonsToHomeScreen(); + emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); // destroy desktop window if (_desktopWindow) { @@ -627,8 +629,8 @@ void TabletProxy::addButtonsToHomeScreen() { for (auto& buttonProxy : _tabletButtonProxies) { addButtonProxyToQmlTablet(tablet, buttonProxy.data()); } - auto loader = _qmlTabletRoot->findChild("loader"); - QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); + auto loader = _qmlTabletRoot->findChild("loader"); + QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen())); } QObject* TabletProxy::getTabletSurface() { From cb8f75394d51282ebea2508de615573742a51fbe Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 2 May 2017 12:08:39 +1200 Subject: [PATCH 023/128] Don't sort recordings --- scripts/system/html/js/record.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/system/html/js/record.js b/scripts/system/html/js/record.js index c78500307d..aae4d1c89a 100644 --- a/scripts/system/html/js/record.js +++ b/scripts/system/html/js/record.js @@ -53,10 +53,6 @@ function updatePlayersUnused() { elPlayersUnused.innerHTML = numberOfPlayers - recordingsBeingPlayed.length; } -function orderRecording(a, b) { - return a.filename > b.filename ? 1 : -1; -} - function updateRecordings() { var tbody, tr, @@ -68,8 +64,6 @@ function updateRecordings() { i, HIFI_GLYPH_CLOSE = "w"; - recordingsBeingPlayed.sort(orderRecording); - tbody = document.createElement("tbody"); tbody.id = "recordings-list"; From 9b117165bd21d7a3ff21c47fa5190ab4dcd2590d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 2 May 2017 11:44:16 -0700 Subject: [PATCH 024/128] expire our announcements when we leave, and turn off messagesWaiting when everything is expired. --- scripts/system/tablet-goto.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index fec7a6de90..b9ca6f7407 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -143,9 +143,22 @@ button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); - var stories = {}; - var DEBUG = false; + var stories = {}, pingPong = false; + var DEBUG = true; //fixme + function expire(id) { + request({ + uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, + method: 'PUT', + body: {expired: true} + }, function (error, response) { + if (error || (response.status !== 'success')) { + print("ERROR expiring story: ", error || response.status); + return; + } + }); + } function pollForAnnouncements() { + // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? var actions = DEBUG ? 'snapshot' : 'announcement'; var count = DEBUG ? 10 : 100; var options = [ @@ -164,9 +177,16 @@ print("Error: unable to get", url, error || data.status); return; } - var didNotify = false; + var didNotify = false, key; + pingPong = !pingPong; data.user_stories.forEach(function (story) { - if (stories[story.id]) { // already seen + var stored = stories[story.id], storedOrNew = stored || story; + if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { + expire(story.id); + return; // before marking + } + storedOrNew.pingPong = pingPong; + if (stored) { // already seen return; } stories[story.id] = story; @@ -174,12 +194,19 @@ Window.displayAnnouncement(message); didNotify = true; }); + for (key in stories) { // Any story we were tracking that was not marked, has expired. + if (stories[key].pingPong !== pingPong) { + delete stories[key]; + } + } if (didNotify) { messagesWaiting(true); if (HMD.isHandControllerAvailable()) { var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND); } + } else if (!Object.keys(stories).length) { // If there's nothing being tracked, then any messageWaiting has expired. + messagesWaiting(false); } }); } From 7f1db38390f03a2f8dfcf53c2c0e1c511285ecbb Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 27 Apr 2017 09:30:00 +0200 Subject: [PATCH 025/128] Renamed EDIT to CREATE --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6fabeb2ec6..6fae4df64e 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -343,7 +343,7 @@ var toolBar = (function () { activeButton = tablet.addButton({ icon: "icons/tablet-icons/edit-i.svg", activeIcon: "icons/tablet-icons/edit-a.svg", - text: "EDIT", + text: "CREATE", sortOrder: 10 }); tablet.screenChanged.connect(function (type, url) { From b3e078eb7401df1554d5997ca3f0c22af139d5cd Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 27 Apr 2017 16:02:47 +0200 Subject: [PATCH 026/128] Trigger rebuild --- scripts/system/edit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6fae4df64e..a6d2d165f7 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2094,3 +2094,4 @@ entityListTool.webView.webEventReceived.connect(function (data) { }); }()); // END LOCAL_SCOPE + From 43f846770813312a3b165ac32265fcdbbcf379df Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 13:58:20 -0700 Subject: [PATCH 027/128] lint --- scripts/system/makeUserConnection.js | 422 ++++++++++++++------------- 1 file changed, 212 insertions(+), 210 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0ffea0c568..eda461f541 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -1,4 +1,6 @@ "use strict"; +/*jslint vars:true, plusplus:true, forin:true*/ +/*global Window, Script, Controller, MyAvatar, AvatarList, Entities, Messages, Audio, SoundCache, Account, UserActivityLogger, Vec3, Quat, XMLHttpRequest, location, print*/ // // makeUserConnection.js // scripts/system @@ -9,7 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m @@ -27,7 +29,7 @@ var MAKING_CONNECTION_TIMEOUT = 800; // ms var CONNECTING_TIME = 1600; // ms var PARTICLE_RADIUS = 0.15; // m - var PARTICLE_ANGLE_INCREMENT = 360/45; // 1hz + var PARTICLE_ANGLE_INCREMENT = 360 / 45; // 1hz var HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/4beat_sweep.wav"; var SUCCESSFUL_HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/3rdbeat_success_bell.wav"; var PREFERRER_HAND_JOINT_POSTFIX_ORDER = ['Middle1', 'Index1', '']; @@ -39,7 +41,7 @@ var PARTICLE_EFFECT_PROPS = { "alpha": 0.8, "azimuthFinish": Math.PI, - "azimuthStart": -1*Math.PI, + "azimuthStart": -1 * Math.PI, "emitRate": 500, "emitSpeed": 0.0, "emitterShouldTrail": 1, @@ -56,10 +58,10 @@ "color": {"red": 255, "green": 255, "blue": 255}, "colorFinish": {"red": 0, "green": 164, "blue": 255}, "colorStart": {"red": 255, "green": 255, "blue": 255}, - "emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71}, + "emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71}, "emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0}, "accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0}, - "dimensions": {"x":0.05, "y": 0.05, "z": 0.05}, + "dimensions": {"x": 0.05, "y": 0.05, "z": 0.05}, "type": "ParticleEffect" }; var MAKING_CONNECTION_PARTICLE_PROPS = { @@ -68,7 +70,7 @@ "alphaSpread": 0, "alphaFinish": 0, "azimuthFinish": Math.PI, - "azimuthStart": -1*Math.PI, + "azimuthStart": -1 * Math.PI, "emitRate": 2000, "emitSpeed": 0.0, "emitterShouldTrail": 1, @@ -86,14 +88,14 @@ "color": {"red": 200, "green": 170, "blue": 255}, "colorFinish": {"red": 0, "green": 134, "blue": 255}, "colorStart": {"red": 185, "green": 222, "blue": 255}, - "emitOrientation": {"w": -0.71, "x":0.0, "y":0.0, "z": 0.71}, + "emitOrientation": {"w": -0.71, "x": 0.0, "y": 0.0, "z": 0.71}, "emitAcceleration": {"x": 0.0, "y": 0.0, "z": 0.0}, "accelerationSpread": {"x": 0.0, "y": 0.0, "z": 0.0}, - "dimensions": {"x":0.05, "y": 0.05, "z": 0.05}, + "dimensions": {"x": 0.05, "y": 0.05, "z": 0.05}, "type": "ParticleEffect" }; - var currentHand = undefined; + var currentHand; var currentHandJointIndex = -1; var state = STATES.INACTIVE; var connectingInterval; @@ -183,7 +185,8 @@ function handToString(hand) { if (hand === Controller.Standard.RightHand) { return "RightHand"; - } else if (hand === Controller.Standard.LeftHand) { + } + if (hand === Controller.Standard.LeftHand) { return "LeftHand"; } debug("handToString called without valid hand! value: ", hand); @@ -193,7 +196,8 @@ function stringToHand(hand) { if (hand === "RightHand") { return Controller.Standard.RightHand; - } else if (hand === "LeftHand") { + } + if (hand === "LeftHand") { return Controller.Standard.LeftHand; } debug("stringToHand called with bad hand string:", hand); @@ -203,7 +207,8 @@ function handToHaptic(hand) { if (hand === Controller.Standard.RightHand) { return 1; - } else if (hand === Controller.Standard.LeftHand) { + } + if (hand === Controller.Standard.LeftHand) { return 0; } debug("handToHaptic called without a valid hand!"); @@ -231,11 +236,11 @@ // This returns the ideal hand joint index for the avatar. // [hand]middle1 -> [hand]index1 -> [hand] function getIdealHandJointIndex(avatar, hand) { - debug("got hand " + hand + " for avatar " + avatar.sessionUUID); - var handString = handToString(hand); - for (var i = 0; i < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; i++) { - var jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[i]; - var jointIndex = avatar.getJointIndex(jointName); + debug("get hand " + hand + " for avatar " + avatar.sessionUUID); + var suffixIndex, jointName, jointIndex, handString = handToString(hand); + for (suffixIndex = 0; suffixIndex < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; suffixIndex++) { + jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex]; + jointIndex = avatar.getJointIndex(jointName); if (jointIndex !== -1) { debug('found joint ' + jointName + ' (' + jointIndex + ')'); return jointIndex; @@ -255,7 +260,7 @@ return avatar.getJointPosition(handJointIndex); } - function shakeHandsAnimation(animationProperties) { + function shakeHandsAnimation() { // all we are doing here is moving the right hand to a spot // that is in front of and a bit above the hips. Basing how // far in front as scaling with the avatar's height (say hips @@ -325,58 +330,58 @@ } switch (state) { - case STATES.WAITING: - // no visualization while waiting - deleteParticleEffect(); - deleteMakeConnectionParticleEffect(); - stopHandshakeSound(); - break; - case STATES.CONNECTING: - var particleProps = {}; - // put the position between the 2 hands, if we have a connectingId. This - // helps define the plane in which the particles move. - positionFractionallyTowards(myHandPosition, otherHand, 0.5); - // now manage the rest of the entity - if (!particleEffect) { - particleRotationAngle = 0.0; - particleEmitRate = 500; - particleProps = PARTICLE_EFFECT_PROPS; - particleProps.isEmitting = 0; - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); - particleProps.parentID = MyAvatar.sessionUUID; - particleEffect = Entities.addEntity(particleProps, true); - } else { - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); - particleProps.isEmitting = 1; - Entities.editEntity(particleEffect, particleProps); - } - if (!makingConnectionParticleEffect) { - var props = MAKING_CONNECTION_PARTICLE_PROPS; - props.parentID = MyAvatar.sessionUUID; - makingConnectionEmitRate = 2000; - props.emitRate = makingConnectionEmitRate; - props.position = myHandPosition; - makingConnectionParticleEffect = Entities.addEntity(props, true); - } else { - makingConnectionEmitRate *= 0.5; - Entities.editEntity(makingConnectionParticleEffect, { - emitRate: makingConnectionEmitRate, - position: myHandPosition, - isEmitting: true - }); - } - break; - case STATES.MAKING_CONNECTION: - particleEmitRate = Math.max(50, particleEmitRate * 0.5); - Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); - Entities.editEntity(particleEffect, { - position: calcParticlePos(myHandPosition, otherHand, otherOrientation), - emitRate: particleEmitRate + case STATES.WAITING: + // no visualization while waiting + deleteParticleEffect(); + deleteMakeConnectionParticleEffect(); + stopHandshakeSound(); + break; + case STATES.CONNECTING: + var particleProps = {}; + // put the position between the 2 hands, if we have a connectingId. This + // helps define the plane in which the particles move. + positionFractionallyTowards(myHandPosition, otherHand, 0.5); + // now manage the rest of the entity + if (!particleEffect) { + particleRotationAngle = 0.0; + particleEmitRate = 500; + particleProps = PARTICLE_EFFECT_PROPS; + particleProps.isEmitting = 0; + particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.parentID = MyAvatar.sessionUUID; + particleEffect = Entities.addEntity(particleProps, true); + } else { + particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.isEmitting = 1; + Entities.editEntity(particleEffect, particleProps); + } + if (!makingConnectionParticleEffect) { + var props = MAKING_CONNECTION_PARTICLE_PROPS; + props.parentID = MyAvatar.sessionUUID; + makingConnectionEmitRate = 2000; + props.emitRate = makingConnectionEmitRate; + props.position = myHandPosition; + makingConnectionParticleEffect = Entities.addEntity(props, true); + } else { + makingConnectionEmitRate *= 0.5; + Entities.editEntity(makingConnectionParticleEffect, { + emitRate: makingConnectionEmitRate, + position: myHandPosition, + isEmitting: true }); - break; - default: - debug("unexpected state", state); - break; + } + break; + case STATES.MAKING_CONNECTION: + particleEmitRate = Math.max(50, particleEmitRate * 0.5); + Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); + Entities.editEntity(particleEffect, { + position: calcParticlePos(myHandPosition, otherHand, otherOrientation), + emitRate: particleEmitRate + }); + break; + default: + debug("unexpected state", state); + break; } } @@ -412,8 +417,42 @@ }); return nearestAvatar; } + function messageSend(message) { + Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); + } + function lookForWaitingAvatar() { + // we started with nobody close enough, but maybe I've moved + // or they did. Note that 2 people doing this race, so stop + // as soon as you have a connectingId (which means you got their + // message before noticing they were in range in this loop) + // just in case we re-enter before stopping + stopWaiting(); + debug("started looking for waiting avatars"); + waitingInterval = Script.setInterval(function () { + if (state === STATES.WAITING && !connectingId) { + // find the closest in-range avatar, and send connection request + var nearestAvatar = findNearestWaitingAvatar(); + if (nearestAvatar.avatar) { + connectingId = nearestAvatar.avatar; + connectingHandString = handToString(nearestAvatar.hand); + debug("sending connectionRequest to", connectingId); + messageSend({ + key: "connectionRequest", + id: connectingId, + hand: handToString(currentHand) + }); + } + } else { + // something happened, stop looking for avatars to connect + stopWaiting(); + debug("stopped looking for waiting avatars"); + } + }, WAITING_INTERVAL); + } + + var pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request'; // As currently implemented, we select the closest waiting avatar (if close enough) and send // them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a // connectionRequest. If the 2 people who want to connect are both somewhat out of range when they @@ -510,9 +549,8 @@ debug("updateTriggers called - gripping", handToString(hand)); if (state !== STATES.INACTIVE) { return; - } else { - startHandshake(fromKeyboard); } + startHandshake(fromKeyboard); } else { // TODO: should we end handshake even when inactive? Ponder debug("updateTriggers called -- no longer gripping", handToString(hand)); @@ -524,47 +562,12 @@ } } - function messageSend(message) { - Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); - } - - function lookForWaitingAvatar() { - // we started with nobody close enough, but maybe I've moved - // or they did. Note that 2 people doing this race, so stop - // as soon as you have a connectingId (which means you got their - // message before noticing they were in range in this loop) - - // just in case we re-enter before stopping - stopWaiting(); - debug("started looking for waiting avatars"); - waitingInterval = Script.setInterval(function () { - if (state === STATES.WAITING && !connectingId) { - // find the closest in-range avatar, and send connection request - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - debug("sending connectionRequest to", connectingId); - messageSend({ - key: "connectionRequest", - id: connectingId, - hand: handToString(currentHand) - }); - } - } else { - // something happened, stop looking for avatars to connect - stopWaiting(); - debug("stopped looking for waiting avatars"); - } - }, WAITING_INTERVAL); - } - /* There is a mini-state machine after entering STATES.makingConnection. We make a request (which might immediately succeed, fail, or neither. If we immediately fail, we tell the user. Otherwise, we wait MAKING_CONNECTION_TIMEOUT. At that time, we poll until success or fail. */ - var result, requestBody, pollCount = 0, requestUrl = location.metaverseServerUrl + '/api/v1/user/connection_request'; + var result, requestBody; function connectionRequestCompleted() { // Final result is in. Do effects. if (result.status === 'success') { // set earlier if (!successfulHandshakeInjector) { @@ -580,10 +583,15 @@ handToHaptic(currentHand)); // don't change state (so animation continues while gripped) // but do send a notification, by calling the slot that emits the signal for it - Window.makeConnection(true, result.connection.new_connection ? - "You and " + result.connection.username + " are now connected!" : result.connection.username); - UserActivityLogger.makeUserConnection(connectingId, true, result.connection.new_connection ? - "new connection" : "already connected"); + Window.makeConnection(true, + result.connection.new_connection ? + "You and " + result.connection.username + " are now connected!" : + result.connection.username); + UserActivityLogger.makeUserConnection(connectingId, + true, + result.connection.new_connection ? + "new connection" : + "already connected"); return; } // failed endHandshake(); @@ -658,13 +666,16 @@ // This will immediately set response if successful (e.g., the other guy got his request in first), // or immediate failure, and will otherwise poll (using the requestBody we just set). - request({ // + request({ uri: requestUrl, method: 'POST', json: true, body: {'user_connection_request': requestBody} }, handleConnectionResponseAndMaybeRepeat); } + function getConnectingHandJointIndex() { + return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; + } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate @@ -676,8 +687,7 @@ // do we need to do this? connectingId = id; connectingHandString = hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; + connectingHandJointIndex = getConnectingHandJointIndex(); state = STATES.CONNECTING; // play sound @@ -714,7 +724,7 @@ key: "done" }); startHandshake(); - } else if (count > CONNECTING_TIME/CONNECTING_INTERVAL) { + } else if (count > CONNECTING_TIME / CONNECTING_INTERVAL) { debug("made connection with " + id); makeConnection(id); stopConnecting(); @@ -753,127 +763,119 @@ debug(e); } switch (message.key) { - case "waiting": - // add this guy to waiting object. Any other message from this person will - // remove it from the list - waitingList[senderID] = message.hand; - break; - case "connectionRequest": - delete waitingList[senderID]; - if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && - (!connectingId || connectingId === senderID)) { - // you were waiting for a connection request, so send the ack. Or, you and the other - // guy raced and both send connectionRequests. Handle that too + case "waiting": + // add this guy to waiting object. Any other message from this person will + // remove it from the list + waitingList[senderID] = message.hand; + break; + case "connectionRequest": + delete waitingList[senderID]; + if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { + // you were waiting for a connection request, so send the ack. Or, you and the other + // guy raced and both send connectionRequests. Handle that too + connectingId = senderID; + connectingHandString = message.hand; + connectingHandJointIndex = getConnectingHandJointIndex(); + messageSend({ + key: "connectionAck", + id: senderID, + hand: handToString(currentHand) + }); + } else if (state === STATES.WAITING && connectingId === senderID) { + // the person you are trying to connect sent a request to someone else. See the + // if statement above. So, don't cry, just start the handshake over again + startHandshake(); + } + break; + case "connectionAck": + delete waitingList[senderID]; + if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { + if (message.id === MyAvatar.sessionUUID) { + // start connecting... connectingId = senderID; connectingHandString = message.hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - messageSend({ - key: "connectionAck", - id: senderID, - hand: handToString(currentHand) - }); - } else if (state === STATES.WAITING && connectingId === senderID) { - // the person you are trying to connect sent a request to someone else. See the - // if statement above. So, don't cry, just start the handshake over again + connectingHandJointIndex = getConnectingHandJointIndex(); + stopWaiting(); + startConnecting(senderID, connectingHandString); + } else if (connectingId) { + // this is for someone else (we lost race in connectionRequest), + // so lets start over startHandshake(); } - break; - case "connectionAck": - delete waitingList[senderID]; - if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { - if (message.id === MyAvatar.sessionUUID) { - // start connecting... - connectingId = senderID; - connectingHandString = message.hand; - connectingHandJointIndex = AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? - getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - stopWaiting(); - startConnecting(senderID, connectingHandString); - } else if (connectingId) { - // this is for someone else (we lost race in connectionRequest), - // so lets start over + } + // TODO: check to see if we are waiting for this but the person we are connecting sent it to + // someone else, and try again + break; + case "connecting": + delete waitingList[senderID]; + if (state === STATES.WAITING && senderID === connectingId) { + // temporary logging + if (connectingHandString !== message.hand) { + debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); + } + connectingHandString = message.hand; + if (message.id !== MyAvatar.sessionUUID) { + // the person we were trying to connect is connecting to someone else + // so try again + startHandshake(); + break; + } + startConnecting(senderID, message.hand); + } + break; + case "done": + delete waitingList[senderID]; + if (state === STATES.CONNECTING && connectingId === senderID) { + // if they are done, and didn't connect us, terminate our + // connecting + if (message.connectionId !== MyAvatar.sessionUUID) { + stopConnecting(); + // now just call startHandshake. Should be ok to do so without a + // value for isKeyboard, as we should not change the animation + // state anyways (if any) + startHandshake(); + } + } else { + // if waiting or inactive, lets clear the connecting id. If in makingConnection, + // do nothing + if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { + connectingId = undefined; + connectingHandString = undefined; + connectingHandJointIndex = -1; + if (state !== STATES.INACTIVE) { startHandshake(); } } - // TODO: check to see if we are waiting for this but the person we are connecting sent it to - // someone else, and try again - break; - case "connecting": - delete waitingList[senderID]; - if (state === STATES.WAITING && senderID === connectingId) { - // temporary logging - if (connectingHandString !== message.hand) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); - } - connectingHandString = message.hand; - if (message.id !== MyAvatar.sessionUUID) { - // the person we were trying to connect is connecting to someone else - // so try again - startHandshake(); - break; - } - startConnecting(senderID, message.hand); - } - break; - case "done": - delete waitingList[senderID]; - if (state === STATES.CONNECTING && connectingId === senderID) { - // if they are done, and didn't connect us, terminate our - // connecting - if (message.connectionId !== MyAvatar.sessionUUID) { - stopConnecting(); - // now just call startHandshake. Should be ok to do so without a - // value for isKeyboard, as we should not change the animation - // state anyways (if any) - startHandshake(); - } - } else { - // if waiting or inactive, lets clear the connecting id. If in makingConnection, - // do nothing - if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; - if (state !== STATES.INACTIVE) { - startHandshake(); - } - } - } - break; - default: - debug("unknown message", message); - break; + } + break; + default: + debug("unknown message", message); + break; } } Messages.subscribe(MESSAGE_CHANNEL); Messages.messageReceived.connect(messageHandler); - function makeGripHandler(hand, animate) { // determine if we are gripping or un-gripping if (animate) { - return function(value) { + return function (value) { updateTriggers(value, true, hand); }; - - } else { - return function (value) { - updateTriggers(value, false, hand); - }; } + return function (value) { + updateTriggers(value, false, hand); + }; } function keyPressEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && - !event.isAlt) { + if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { updateTriggers(1.0, true, Controller.Standard.RightHand); } } function keyReleaseEvent(event) { - if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && - !event.isAlt) { + if ((event.text === "x") && !event.isAutoRepeat && !event.isShifted && !event.isMeta && !event.isControl && !event.isAlt) { updateTriggers(0.0, true, Controller.Standard.RightHand); } } From e2dd00bdc468a536f7fdb4c540e1b63b0147b34c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:29:43 -0700 Subject: [PATCH 028/128] unique message for exceeding poll limit --- scripts/system/makeUserConnection.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index eda461f541..e3785e9d77 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -610,7 +610,7 @@ debug(response, 'pollCount', pollCount); if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long. debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT'); - result = {status: 'error', connection: 'expired'}; + result = {status: 'error', connection: 'no-partner-found'}; connectionRequestCompleted(); } else { // poll Script.setTimeout(function () { @@ -640,8 +640,6 @@ } } - // this should be where we make the appropriate connection call. For now just make the - // visualization change. function makeConnection(id) { // send done to let the connection know you have made connection. messageSend({ @@ -651,8 +649,7 @@ state = STATES.MAKING_CONNECTION; - // continue the haptic background until the timeout fires. When we make calls, we will have an interval - // probably, in which we do this. + // continue the haptic background until the timeout fires. Controller.triggerHapticPulse(HAPTIC_DATA.background.strength, MAKING_CONNECTION_TIMEOUT, handToHaptic(currentHand)); requestBody = {'node_id': cleanId(MyAvatar.sessionUUID), 'proposed_node_id': cleanId(id)}; // for use when repeating From 13e10873ce0f6c7f475d660a51c0c3ca364783f5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:39:35 -0700 Subject: [PATCH 029/128] simplify animation load and code --- scripts/system/makeUserConnection.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index e3785e9d77..57a9764e6e 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -260,7 +260,20 @@ return avatar.getJointPosition(handJointIndex); } + var animationData = {}; function shakeHandsAnimation() { + return animationData; + } + function endHandshakeAnimation() { + if (animHandlerId) { + debug("removing animation"); + animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId); + } + } + function startHandshakeAnimation() { + endHandshakeAnimation(); // just in case order of press/unpress is broken + debug("adding animation"); + // all we are doing here is moving the right hand to a spot // that is in front of and a bit above the hips. Basing how // far in front as scaling with the avatar's height (say hips @@ -273,7 +286,8 @@ } result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); - return result; + + animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); } function positionFractionallyTowards(posA, posB, frac) { @@ -460,12 +474,7 @@ // waiting message. Either way, they will start connecting eachother at that point. function startHandshake(fromKeyboard) { if (fromKeyboard) { - debug("adding animation"); - // just in case order of press/unpress is broken - if (animHandlerId) { - animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId); - } - animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); + startHandshakeAnimation(); } debug("starting handshake for", currentHand); pollCount = 0; @@ -525,10 +534,7 @@ key: "done" }); - if (animHandlerId) { - debug("removing animation"); - MyAvatar.removeAnimationStateHandler(animHandlerId); - } + endHandshakeAnimation(); // No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us // in a weird state. request({uri: requestUrl, method: 'DELETE'}, debug); From 7f120febbd6c356f71d7581fe08828aa90109768 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 15:50:04 -0700 Subject: [PATCH 030/128] fix that --- scripts/system/makeUserConnection.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 57a9764e6e..c2d86e71fd 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -261,6 +261,19 @@ } var animationData = {}; + function updateAnimationData() { + // all we are doing here is moving the right hand to a spot + // that is in front of and a bit above the hips. Basing how + // far in front as scaling with the avatar's height (say hips + // to head distance) + var headIndex = MyAvatar.getJointIndex("Head"); + var offset = 0.5; // default distance of hand in front of you + if (headIndex) { + offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y; + } + animationData.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); + animationData.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); + } function shakeHandsAnimation() { return animationData; } @@ -273,20 +286,7 @@ function startHandshakeAnimation() { endHandshakeAnimation(); // just in case order of press/unpress is broken debug("adding animation"); - - // all we are doing here is moving the right hand to a spot - // that is in front of and a bit above the hips. Basing how - // far in front as scaling with the avatar's height (say hips - // to head distance) - var headIndex = MyAvatar.getJointIndex("Head"); - var offset = 0.5; // default distance of hand in front of you - var result = {}; - if (headIndex) { - offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y; - } - result.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3}); - result.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90); - + updateAnimationData(); animHandlerId = MyAvatar.addAnimationStateHandler(shakeHandsAnimation, []); } From 03e929ca18676788dc016fdd261b252fc31f6c08 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 18:25:33 -0700 Subject: [PATCH 031/128] clarify names, particularly hand vs handString --- scripts/system/makeUserConnection.js | 86 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index c2d86e71fd..5540f0c122 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -24,6 +24,7 @@ MAKING_CONNECTION: 3 }; var STATE_STRINGS = ["inactive", "waiting", "connecting", "makingConnection"]; + var HAND_STRING_PROPERTY = 'hand'; // Used in our message protocol. IWBNI we changed it to handString, but that would break compatability. var WAITING_INTERVAL = 100; // ms var CONNECTING_INTERVAL = 100; // ms var MAKING_CONNECTION_TIMEOUT = 800; // ms @@ -234,7 +235,7 @@ } // This returns the ideal hand joint index for the avatar. - // [hand]middle1 -> [hand]index1 -> [hand] + // [handString]middle1 -> [handString]index1 -> [handString] function getIdealHandJointIndex(avatar, hand) { debug("get hand " + hand + " for avatar " + avatar.sessionUUID); var suffixIndex, jointName, jointIndex, handString = handToString(hand); @@ -313,11 +314,11 @@ } } - function calcParticlePos(myHand, otherHand, otherOrientation, reset) { + function calcParticlePos(myHandPosition, otherHandPosition, otherOrientation, reset) { if (reset) { particleRotationAngle = 0.0; } - var position = positionFractionallyTowards(myHand, otherHand, 0.5); + var position = positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); particleRotationAngle += PARTICLE_ANGLE_INCREMENT; // about 0.5 hz var radius = Math.min(PARTICLE_RADIUS, PARTICLE_RADIUS * particleRotationAngle / 360); var axis = Vec3.mix(Quat.getFront(MyAvatar.orientation), Quat.inverse(Quat.getFront(otherOrientation)), 0.5); @@ -333,13 +334,13 @@ } var myHandPosition = getHandPosition(MyAvatar, currentHandJointIndex); - var otherHand; + var otherHandPosition; var otherOrientation; if (connectingId) { var other = AvatarList.getAvatar(connectingId); if (other) { otherOrientation = other.orientation; - otherHand = getHandPosition(other, connectingHandJointIndex); + otherHandPosition = getHandPosition(other, connectingHandJointIndex); } } @@ -354,18 +355,18 @@ var particleProps = {}; // put the position between the 2 hands, if we have a connectingId. This // helps define the plane in which the particles move. - positionFractionallyTowards(myHandPosition, otherHand, 0.5); + positionFractionallyTowards(myHandPosition, otherHandPosition, 0.5); // now manage the rest of the entity if (!particleEffect) { particleRotationAngle = 0.0; particleEmitRate = 500; particleProps = PARTICLE_EFFECT_PROPS; particleProps.isEmitting = 0; - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); particleProps.parentID = MyAvatar.sessionUUID; particleEffect = Entities.addEntity(particleProps, true); } else { - particleProps.position = calcParticlePos(myHandPosition, otherHand, otherOrientation); + particleProps.position = calcParticlePos(myHandPosition, otherHandPosition, otherOrientation); particleProps.isEmitting = 1; Entities.editEntity(particleEffect, particleProps); } @@ -389,7 +390,7 @@ particleEmitRate = Math.max(50, particleEmitRate * 0.5); Entities.editEntity(makingConnectionParticleEffect, {emitRate: 0, isEmitting: 0, position: myHandPosition}); Entities.editEntity(particleEffect, { - position: calcParticlePos(myHandPosition, otherHand, otherOrientation), + position: calcParticlePos(myHandPosition, otherHandPosition, otherOrientation), emitRate: particleEmitRate }); break; @@ -399,14 +400,14 @@ } } - function isNearby(id, hand) { + function isNearby(id, handString) { if (currentHand) { - var handPos = getHandPosition(MyAvatar, currentHandJointIndex); + var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); var avatar = AvatarList.getAvatar(id); if (avatar) { - var otherHand = stringToHand(hand); + var otherHand = stringToHand(handString); var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand); - var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPos); + var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPosition); return (distance < MAX_AVATAR_DISTANCE); } } @@ -414,7 +415,7 @@ } function findNearestWaitingAvatar() { - var handPos = getHandPosition(MyAvatar, currentHandJointIndex); + var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); var minDistance = MAX_AVATAR_DISTANCE; var nearestAvatar = {}; Object.keys(waitingList).forEach(function (identifier) { @@ -422,7 +423,7 @@ if (avatar) { var hand = stringToHand(waitingList[identifier]); var handJointIndex = getIdealHandJointIndex(avatar, hand); - var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPos); + var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition); if (distance < minDistance) { minDistance = distance; nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar}; @@ -434,6 +435,10 @@ function messageSend(message) { Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); } + function handStringMessageSend(message, handString) { + message[HAND_STRING_PROPERTY] = handString; + messageSend(message); + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -452,11 +457,10 @@ connectingId = nearestAvatar.avatar; connectingHandString = handToString(nearestAvatar.hand); debug("sending connectionRequest to", connectingId); - messageSend({ + handStringMessageSend({ key: "connectionRequest", - id: connectingId, - hand: handToString(currentHand) - }); + id: connectingId + }, handToString(currentHand)); } } else { // something happened, stop looking for avatars to connect @@ -494,18 +498,16 @@ connectingHandJointIndex = getIdealHandJointIndex(nearestAvatar.avatarObject, nearestAvatar.hand); currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); debug("sending connectionRequest to", connectingId); - messageSend({ + handStringMessageSend({ key: "connectionRequest", id: connectingId, - hand: handToString(currentHand) - }); + }, handToString(currentHand)); } else { // send waiting message debug("sending waiting message"); - messageSend({ + handStringMessageSend({ key: "waiting", - hand: handToString(currentHand) - }); + }, handToString(currentHand)); lookForWaitingAvatar(); } } @@ -684,12 +686,12 @@ // to be sure the hand is still close enough. If not, we terminate // the interval, go back to the waiting state. If we make it // the entire CONNECTING_TIME, we make the connection. - function startConnecting(id, hand) { + function startConnecting(id, handString) { var count = 0; - debug("connecting", id, "hand", hand); + debug("connecting", id, "hand", handString); // do we need to do this? connectingId = id; - connectingHandString = hand; + connectingHandString = handString; connectingHandJointIndex = getConnectingHandJointIndex(); state = STATES.CONNECTING; @@ -705,11 +707,10 @@ } // send message that we are connecting with them - messageSend({ + handStringMessageSend({ key: "connecting", - id: id, - hand: handToString(currentHand) - }); + id: id + }, handToString(currentHand)); Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand)); connectingInterval = Script.setInterval(function () { @@ -719,7 +720,7 @@ if (state !== STATES.CONNECTING) { debug("stopping connecting interval, state changed"); stopConnecting(); - } else if (!isNearby(id, hand)) { + } else if (!isNearby(id, handString)) { // gotta go back to waiting debug(id, "moved, back to waiting"); stopConnecting(); @@ -769,7 +770,7 @@ case "waiting": // add this guy to waiting object. Any other message from this person will // remove it from the list - waitingList[senderID] = message.hand; + waitingList[senderID] = message[HAND_STRING_PROPERTY]; break; case "connectionRequest": delete waitingList[senderID]; @@ -777,13 +778,12 @@ // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too connectingId = senderID; - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; connectingHandJointIndex = getConnectingHandJointIndex(); - messageSend({ + handStringMessageSend({ key: "connectionAck", id: senderID, - hand: handToString(currentHand) - }); + }, handToString(currentHand)); } else if (state === STATES.WAITING && connectingId === senderID) { // the person you are trying to connect sent a request to someone else. See the // if statement above. So, don't cry, just start the handshake over again @@ -796,7 +796,7 @@ if (message.id === MyAvatar.sessionUUID) { // start connecting... connectingId = senderID; - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; connectingHandJointIndex = getConnectingHandJointIndex(); stopWaiting(); startConnecting(senderID, connectingHandString); @@ -813,17 +813,17 @@ delete waitingList[senderID]; if (state === STATES.WAITING && senderID === connectingId) { // temporary logging - if (connectingHandString !== message.hand) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message.hand); + if (connectingHandString !== message[HAND_STRING_PROPERTY]) { + debug("connecting hand", connectingHandString, "not same as connecting hand in message", message[HAND_STRING_PROPERTY]); } - connectingHandString = message.hand; + connectingHandString = message[HAND_STRING_PROPERTY]; if (message.id !== MyAvatar.sessionUUID) { // the person we were trying to connect is connecting to someone else // so try again startHandshake(); break; } - startConnecting(senderID, message.hand); + startConnecting(senderID, connectingHandString); } break; case "done": From 154af421808ffb6e429ebc493a09f88681ff0c44 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 19:39:50 -0700 Subject: [PATCH 032/128] setupCandidate --- scripts/system/makeUserConnection.js | 37 +++++++++++----------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 5540f0c122..f45efafdc8 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -439,6 +439,19 @@ message[HAND_STRING_PROPERTY] = handString; messageSend(message); } + function setupCandidate() { // find the closest in-range avatar, send connection request, an return true. (Otherwise falsey) + var nearestAvatar = findNearestWaitingAvatar(); + if (nearestAvatar.avatar) { + connectingId = nearestAvatar.avatar; + connectingHandString = handToString(nearestAvatar.hand); + debug("sending connectionRequest to", connectingId); + handStringMessageSend({ + key: "connectionRequest", + id: connectingId + }, handToString(currentHand)); + return true; + } + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -451,17 +464,7 @@ debug("started looking for waiting avatars"); waitingInterval = Script.setInterval(function () { if (state === STATES.WAITING && !connectingId) { - // find the closest in-range avatar, and send connection request - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - debug("sending connectionRequest to", connectingId); - handStringMessageSend({ - key: "connectionRequest", - id: connectingId - }, handToString(currentHand)); - } + setupCandidate(); } else { // something happened, stop looking for avatars to connect stopWaiting(); @@ -490,18 +493,8 @@ stopWaiting(); stopConnecting(); stopMakingConnection(); - - var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); - connectingHandJointIndex = getIdealHandJointIndex(nearestAvatar.avatarObject, nearestAvatar.hand); + if (setupCandidate()) { currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - debug("sending connectionRequest to", connectingId); - handStringMessageSend({ - key: "connectionRequest", - id: connectingId, - }, handToString(currentHand)); } else { // send waiting message debug("sending waiting message"); From d3c9cb9574b6454131e790f84dad7a6d8a1e4315 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 08:04:46 -0700 Subject: [PATCH 033/128] standardize data init/clear --- scripts/system/makeUserConnection.js | 35 +++++++++++++--------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index f45efafdc8..92d6791617 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -452,6 +452,11 @@ return true; } } + function clearConnecting() { + connectingId = undefined; + connectingHandString = undefined; + connectingHandJointIndex = -1; + } function lookForWaitingAvatar() { // we started with nobody close enough, but maybe I've moved @@ -486,9 +491,7 @@ debug("starting handshake for", currentHand); pollCount = 0; state = STATES.WAITING; - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); // just in case stopWaiting(); stopConnecting(); @@ -517,9 +520,7 @@ // as we ignore the key release event when inactive. See updateTriggers // below. state = STATES.INACTIVE; - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); stopWaiting(); stopConnecting(); stopMakingConnection(); @@ -674,6 +675,11 @@ function getConnectingHandJointIndex() { return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; } + function setupConnecting(id, handString) { + connectingId = id; + connectingHandString = handString; + connectingHandJointIndex = getConnectingHandJointIndex(); + } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate @@ -683,9 +689,7 @@ var count = 0; debug("connecting", id, "hand", handString); // do we need to do this? - connectingId = id; - connectingHandString = handString; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(id, handString); state = STATES.CONNECTING; // play sound @@ -770,9 +774,7 @@ if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too - connectingId = senderID; - connectingHandString = message[HAND_STRING_PROPERTY]; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(senderID, message[HAND_STRING_PROPERTY]); handStringMessageSend({ key: "connectionAck", id: senderID, @@ -787,10 +789,7 @@ delete waitingList[senderID]; if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { - // start connecting... - connectingId = senderID; - connectingHandString = message[HAND_STRING_PROPERTY]; - connectingHandJointIndex = getConnectingHandJointIndex(); + setupConnecting(senderID, message[HAND_STRING_PROPERTY]); stopWaiting(); startConnecting(senderID, connectingHandString); } else if (connectingId) { @@ -835,9 +834,7 @@ // if waiting or inactive, lets clear the connecting id. If in makingConnection, // do nothing if (state !== STATES.MAKING_CONNECTION && connectingId === senderID) { - connectingId = undefined; - connectingHandString = undefined; - connectingHandJointIndex = -1; + clearConnecting(); if (state !== STATES.INACTIVE) { startHandshake(); } From 3f99509a1964b69ab3a661f39a5cb59266852159 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 09:44:34 -0700 Subject: [PATCH 034/128] check your own location data on failure, and notify if it is wrong --- scripts/system/makeUserConnection.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 92d6791617..2c984833b4 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -600,6 +600,22 @@ debug("failing with result data", result); // IWBNI we also did some fail sound/visual effect. Window.makeConnection(false, result.connection); + if (Account.isLoggedIn()) { // Give extra failure info + request(location.metaverseServerUrl + '/api/v1/users/' + Account.username + '/location', function (error, response) { + var message = ''; + if (error || response.status !== 'success') { + message = 'Unable to get location.'; + } else if (!response.data || !response.data.location) { + message = "Unexpected location value: " + JSON.stringify(response); + } else if (response.data.location.node_id !== cleanId(MyAvatar.sessionUUID)) { + message = 'Session identification does not match database. Maybe you are logged in on another machine? That would prevent handshakes.' + JSON.stringify(response) + MyAvatar.sessionUUID; + } + if (message) { + Window.makeConnection(false, message); + } + debug("account location:", message || 'ok'); + }); + } UserActivityLogger.makeUserConnection(connectingId, false, result.connection); } var POLL_INTERVAL_MS = 200, POLL_LIMIT = 5; @@ -612,7 +628,7 @@ debug(response, 'pollCount', pollCount); if (pollCount++ >= POLL_LIMIT) { // server will expire, but let's not wait that long. debug('POLL LIMIT REACHED; TIMEOUT: expired message generated by CLIENT'); - result = {status: 'error', connection: 'no-partner-found'}; + result = {status: 'error', connection: 'No logged-in partner found.'}; connectionRequestCompleted(); } else { // poll Script.setTimeout(function () { From 921242fbe5c10eb5e779fa0e06a7dba334858194 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 13:32:21 -0700 Subject: [PATCH 035/128] It may be possible to switch hands without endHandshake, and thus not reset currentHandJointIndex on updateTrigger. That was filled in later by startHanshake ONLY if there was already waiting avatars so that we didn't have wait. As a result, our distance measure would be from hips instead of hand. This changes it to always compute currentHandJointIndex on updateTriggers (and not elsewhere). --- scripts/system/makeUserConnection.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 2c984833b4..eabd4d5dac 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -255,7 +255,6 @@ function getHandPosition(avatar, handJointIndex) { if (handJointIndex === -1) { debug("calling getHandPosition with no hand joint index! (returning avatar position but this is a BUG)"); - debug(new Error().stack); return avatar.position; } return avatar.getJointPosition(handJointIndex); @@ -439,7 +438,7 @@ message[HAND_STRING_PROPERTY] = handString; messageSend(message); } - function setupCandidate() { // find the closest in-range avatar, send connection request, an return true. (Otherwise falsey) + function setupCandidate() { // find the closest in-range avatar, send connection request, and return true. (Otherwise falsey) var nearestAvatar = findNearestWaitingAvatar(); if (nearestAvatar.avatar) { connectingId = nearestAvatar.avatar; @@ -483,7 +482,7 @@ // them a connectionRequest. If nobody is close enough we send a waiting message, and wait for a // connectionRequest. If the 2 people who want to connect are both somewhat out of range when they // initiate the shake, they will race to see who sends the connectionRequest after noticing the - // waiting message. Either way, they will start connecting eachother at that point. + // waiting message. Either way, they will start connecting each other at that point. function startHandshake(fromKeyboard) { if (fromKeyboard) { startHandshakeAnimation(); @@ -496,9 +495,7 @@ stopWaiting(); stopConnecting(); stopMakingConnection(); - if (setupCandidate()) { - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - } else { + if (!setupCandidate()) { // send waiting message debug("sending waiting message"); handStringMessageSend({ @@ -541,10 +538,8 @@ debug("currentHand", currentHand, "ignoring messages from", hand); return; } - if (!currentHand) { - currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); - } + currentHand = hand; + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { From 6af21525d5040ebcb3360c165b94d191d137b99a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 14:19:33 -0700 Subject: [PATCH 036/128] remove extra joint search --- scripts/system/makeUserConnection.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index eabd4d5dac..0faaf00ded 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -399,14 +399,12 @@ } } - function isNearby(id, handString) { + function isNearby() { if (currentHand) { var handPosition = getHandPosition(MyAvatar, currentHandJointIndex); - var avatar = AvatarList.getAvatar(id); + var avatar = AvatarList.getAvatar(connectingId); if (avatar) { - var otherHand = stringToHand(handString); - var otherHandJointIndex = getIdealHandJointIndex(avatar, otherHand); - var distance = Vec3.distance(getHandPosition(avatar, otherHandJointIndex), handPosition); + var distance = Vec3.distance(getHandPosition(avatar, connectingHandJointIndex), handPosition); return (distance < MAX_AVATAR_DISTANCE); } } @@ -728,7 +726,7 @@ if (state !== STATES.CONNECTING) { debug("stopping connecting interval, state changed"); stopConnecting(); - } else if (!isNearby(id, handString)) { + } else if (!isNearby()) { // gotta go back to waiting debug(id, "moved, back to waiting"); stopConnecting(); @@ -800,9 +798,8 @@ delete waitingList[senderID]; if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { - setupConnecting(senderID, message[HAND_STRING_PROPERTY]); stopWaiting(); - startConnecting(senderID, connectingHandString); + startConnecting(senderID, message[HAND_STRING_PROPERTY]); } else if (connectingId) { // this is for someone else (we lost race in connectionRequest), // so lets start over From 4839a6e8729785e8921c128b43907011835011af Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 15:50:30 -0700 Subject: [PATCH 037/128] convert foreign handString to jointIndex once, not often --- scripts/system/makeUserConnection.js | 83 +++++++++++----------------- 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0faaf00ded..4b5f74a2aa 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -104,7 +104,6 @@ var makingConnectionTimeout; var animHandlerId; var connectingId; - var connectingHandString; var connectingHandJointIndex = -1; var waitingList = {}; var particleEffect; @@ -119,7 +118,7 @@ function debug() { var stateString = "<" + STATE_STRINGS[state] + ">"; - var connecting = "[" + connectingId + "/" + connectingHandString + "]"; + var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]"; print.apply(null, [].concat.apply([LABEL, stateString, JSON.stringify(waitingList), connecting], [].map.call(arguments, JSON.stringify))); } @@ -194,17 +193,6 @@ return ""; } - function stringToHand(hand) { - if (hand === "RightHand") { - return Controller.Standard.RightHand; - } - if (hand === "LeftHand") { - return Controller.Standard.LeftHand; - } - debug("stringToHand called with bad hand string:", hand); - return 0; - } - function handToHaptic(hand) { if (hand === Controller.Standard.RightHand) { return 1; @@ -236,10 +224,10 @@ // This returns the ideal hand joint index for the avatar. // [handString]middle1 -> [handString]index1 -> [handString] - function getIdealHandJointIndex(avatar, hand) { - debug("get hand " + hand + " for avatar " + avatar.sessionUUID); - var suffixIndex, jointName, jointIndex, handString = handToString(hand); - for (suffixIndex = 0; suffixIndex < PREFERRER_HAND_JOINT_POSTFIX_ORDER.length; suffixIndex++) { + function getIdealHandJointIndex(avatar, handString) { + debug("get hand " + handString + " for avatar " + (avatar && avatar.sessionUUID)); + var suffixIndex, jointName, jointIndex; + for (suffixIndex = 0; suffixIndex < (avatar ? PREFERRER_HAND_JOINT_POSTFIX_ORDER.length : 0); suffixIndex++) { jointName = handString + PREFERRER_HAND_JOINT_POSTFIX_ORDER[suffixIndex]; jointIndex = avatar.getJointIndex(jointName); if (jointIndex !== -1) { @@ -418,12 +406,11 @@ Object.keys(waitingList).forEach(function (identifier) { var avatar = AvatarList.getAvatar(identifier); if (avatar) { - var hand = stringToHand(waitingList[identifier]); - var handJointIndex = getIdealHandJointIndex(avatar, hand); + var handJointIndex = waitingList[identifier]; var distance = Vec3.distance(getHandPosition(avatar, handJointIndex), handPosition); if (distance < minDistance) { minDistance = distance; - nearestAvatar = {avatar: identifier, hand: hand, avatarObject: avatar}; + nearestAvatar = {avatarId: identifier, jointIndex: handJointIndex}; } } }); @@ -432,26 +419,25 @@ function messageSend(message) { Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message)); } - function handStringMessageSend(message, handString) { - message[HAND_STRING_PROPERTY] = handString; + function handStringMessageSend(message) { + message[HAND_STRING_PROPERTY] = handToString(currentHand); messageSend(message); } function setupCandidate() { // find the closest in-range avatar, send connection request, and return true. (Otherwise falsey) var nearestAvatar = findNearestWaitingAvatar(); - if (nearestAvatar.avatar) { - connectingId = nearestAvatar.avatar; - connectingHandString = handToString(nearestAvatar.hand); + if (nearestAvatar.avatarId) { + connectingId = nearestAvatar.avatarId; + connectingHandJointIndex = nearestAvatar.jointIndex; debug("sending connectionRequest to", connectingId); handStringMessageSend({ key: "connectionRequest", id: connectingId - }, handToString(currentHand)); + }); return true; } } function clearConnecting() { connectingId = undefined; - connectingHandString = undefined; connectingHandJointIndex = -1; } @@ -498,7 +484,7 @@ debug("sending waiting message"); handStringMessageSend({ key: "waiting", - }, handToString(currentHand)); + }); lookForWaitingAvatar(); } } @@ -537,7 +523,7 @@ return; } currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, currentHand); // Always, in case of changed skeleton. + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { @@ -681,24 +667,20 @@ body: {'user_connection_request': requestBody} }, handleConnectionResponseAndMaybeRepeat); } - function getConnectingHandJointIndex() { - return AvatarList.getAvatarIdentifiers().indexOf(connectingId) !== -1 ? getIdealHandJointIndex(AvatarList.getAvatar(connectingId), stringToHand(connectingHandString)) : -1; - } - function setupConnecting(id, handString) { + function setupConnecting(id, jointIndex) { connectingId = id; - connectingHandString = handString; - connectingHandJointIndex = getConnectingHandJointIndex(); + connectingHandJointIndex = jointIndex; } // we change states, start the connectionInterval where we check // to be sure the hand is still close enough. If not, we terminate // the interval, go back to the waiting state. If we make it // the entire CONNECTING_TIME, we make the connection. - function startConnecting(id, handString) { + function startConnecting(id, jointIndex) { var count = 0; - debug("connecting", id, "hand", handString); + debug("connecting", id, "hand", jointIndex); // do we need to do this? - setupConnecting(id, handString); + setupConnecting(id, jointIndex); state = STATES.CONNECTING; // play sound @@ -716,7 +698,7 @@ handStringMessageSend({ key: "connecting", id: id - }, handToString(currentHand)); + }); Controller.triggerHapticPulse(HAPTIC_DATA.initial.strength, HAPTIC_DATA.initial.duration, handToHaptic(currentHand)); connectingInterval = Script.setInterval(function () { @@ -760,13 +742,16 @@ | ---------- (done) ---------> | */ function messageHandler(channel, messageString, senderID) { + var message = {}; + function exisitingOrSearchedJointIndex() { // If this is a new connectingId, we'll need to find the jointIndex + return connectingId ? connectingHandJointIndex : getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]); + } if (channel !== MESSAGE_CHANNEL) { return; } if (MyAvatar.sessionUUID === senderID) { // ignore my own return; } - var message = {}; try { message = JSON.parse(messageString); } catch (e) { @@ -774,20 +759,19 @@ } switch (message.key) { case "waiting": - // add this guy to waiting object. Any other message from this person will - // remove it from the list - waitingList[senderID] = message[HAND_STRING_PROPERTY]; + // add this guy to waiting object. Any other message from this person will remove it from the list + waitingList[senderID] = getIdealHandJointIndex(AvatarList.getAvatar(senderID), message[HAND_STRING_PROPERTY]); break; case "connectionRequest": delete waitingList[senderID]; if (state === STATES.WAITING && message.id === MyAvatar.sessionUUID && (!connectingId || connectingId === senderID)) { // you were waiting for a connection request, so send the ack. Or, you and the other // guy raced and both send connectionRequests. Handle that too - setupConnecting(senderID, message[HAND_STRING_PROPERTY]); + setupConnecting(senderID, exisitingOrSearchedJointIndex()); handStringMessageSend({ key: "connectionAck", id: senderID, - }, handToString(currentHand)); + }); } else if (state === STATES.WAITING && connectingId === senderID) { // the person you are trying to connect sent a request to someone else. See the // if statement above. So, don't cry, just start the handshake over again @@ -799,7 +783,7 @@ if (state === STATES.WAITING && (!connectingId || connectingId === senderID)) { if (message.id === MyAvatar.sessionUUID) { stopWaiting(); - startConnecting(senderID, message[HAND_STRING_PROPERTY]); + startConnecting(senderID, exisitingOrSearchedJointIndex()); } else if (connectingId) { // this is for someone else (we lost race in connectionRequest), // so lets start over @@ -812,18 +796,13 @@ case "connecting": delete waitingList[senderID]; if (state === STATES.WAITING && senderID === connectingId) { - // temporary logging - if (connectingHandString !== message[HAND_STRING_PROPERTY]) { - debug("connecting hand", connectingHandString, "not same as connecting hand in message", message[HAND_STRING_PROPERTY]); - } - connectingHandString = message[HAND_STRING_PROPERTY]; if (message.id !== MyAvatar.sessionUUID) { // the person we were trying to connect is connecting to someone else // so try again startHandshake(); break; } - startConnecting(senderID, connectingHandString); + startConnecting(senderID, connectingHandJointIndex); } break; case "done": From 7ba5ea42293f9f967ecf85b1120d060c98addc0a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 2 May 2017 11:29:51 -0700 Subject: [PATCH 038/128] On touch, grip is a trigger, not a button, so it can have lots of callbacks with slightly different values that we ignore. In that case, don't update hand/joint. --- scripts/system/makeUserConnection.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 4b5f74a2aa..17979e9018 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -519,11 +519,9 @@ function updateTriggers(value, fromKeyboard, hand) { if (currentHand && hand !== currentHand) { - debug("currentHand", currentHand, "ignoring messages from", hand); + debug("currentHand", currentHand, "ignoring messages from", hand); // this can be a lot of spam on Touch. Should guard that someday. return; } - currentHand = hand; - currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. // ok now, we are either initiating or quitting... var isGripping = value > GRIP_MIN; if (isGripping) { @@ -531,6 +529,8 @@ if (state !== STATES.INACTIVE) { return; } + currentHand = hand; + currentHandJointIndex = getIdealHandJointIndex(MyAvatar, handToString(currentHand)); // Always, in case of changed skeleton. startHandshake(fromKeyboard); } else { // TODO: should we end handshake even when inactive? Ponder From 00a4fe044af536e9543a51ffa539e40eec29e3d5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:36:38 -0700 Subject: [PATCH 039/128] fix frozen bots --- libraries/recording/src/recording/Deck.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 186516e01c..a4f154f85b 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -166,6 +166,12 @@ void Deck::processFrames() { if (!overLimit) { auto nextFrameTime = nextClip->positionFrameTime(); nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); + if (nextInterval < 0) { + qCWarning(recordingLog) << " Unexected nextInterval < 0 nextFrameTime:" << nextFrameTime + << "_position:" << _position << "-- setting nextInterval to 0"; + nextInterval = 0; + } + #ifdef WANT_RECORDING_DEBUG qCDebug(recordingLog) << "Now " << _position; qCDebug(recordingLog) << "Next frame time " << nextInterval; From de134adb9537150aee968db62b5084d1a01f8cac Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:46:27 -0700 Subject: [PATCH 040/128] CR review --- libraries/recording/src/recording/Deck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index a4f154f85b..c9ac0524ad 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -167,7 +167,7 @@ void Deck::processFrames() { auto nextFrameTime = nextClip->positionFrameTime(); nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); if (nextInterval < 0) { - qCWarning(recordingLog) << " Unexected nextInterval < 0 nextFrameTime:" << nextFrameTime + qCWarning(recordingLog) << "Unexpected nextInterval < 0 nextFrameTime:" << nextFrameTime << "_position:" << _position << "-- setting nextInterval to 0"; nextInterval = 0; } From 0353940d9ebe721cb17e51f1ea1fb7d494bde917 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 17:42:23 -0400 Subject: [PATCH 041/128] decouple initialization from storage in Table.getTablet() --- .../src/TabletScriptingInterface.cpp | 71 +++++++++++-------- .../src/TabletScriptingInterface.h | 8 +-- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index d4eeecc82e..aa9840b226 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -21,6 +21,8 @@ #include #include "SoundEffect.h" +const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; +const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) { return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); @@ -35,11 +37,11 @@ TabletScriptingInterface::TabletScriptingInterface() { } QObject* TabletScriptingInterface::getSystemToolbarProxy() { - const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; Qt::ConnectionType connectionType = Qt::AutoConnection; if (QThread::currentThread() != _toolbarScriptingInterface->thread()) { connectionType = Qt::BlockingQueuedConnection; } + QObject* toolbarProxy = nullptr; bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR)); if (hasResult) { @@ -51,28 +53,38 @@ QObject* TabletScriptingInterface::getSystemToolbarProxy() { } TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { + TabletProxy* tabletProxy = nullptr; + { + // the only thing guarded should be map mutation + // this avoids a deadlock with the Main thread + // from Qt::BlockingQueuedEvent invocations later in the call-tree + std::lock_guard guard(_mapMutex); - std::lock_guard guard(_mutex); - - // look up tabletId in the map. - auto iter = _tabletProxies.find(tabletId); - if (iter != _tabletProxies.end()) { - // tablet already exists, just return it. - return iter->second; - } else { - // allocate a new tablet, add it to the map then return it. - auto tabletProxy = new TabletProxy(tabletId); - tabletProxy->setParent(this); - _tabletProxies[tabletId] = tabletProxy; - tabletProxy->setToolbarMode(_toolbarMode); - return tabletProxy; + auto iter = _tabletProxies.find(tabletId); + if (iter != _tabletProxies.end()) { + // tablet already exists + return iter->second; + } else { + // tablet must be created + tabletProxy = new TabletProxy(this, tabletId); + _tabletProxies[tabletId] = tabletProxy; + } } + + assert(tabletProxy); + // initialize new tablet + tabletProxy->setToolbarMode(_toolbarMode); + return tabletProxy; } void TabletScriptingInterface::setToolbarMode(bool toolbarMode) { - std::lock_guard guard(_mutex); - - _toolbarMode = toolbarMode; + { + // the only thing guarded should be _toolbarMode + // this avoids a deadlock with the Main thread + // from Qt::BlockingQueuedEvent invocations later in the call-tree + std::lock_guard guard(_mapMutex); + _toolbarMode = toolbarMode; + } for (auto& iter : _tabletProxies) { iter.second->setToolbarMode(toolbarMode); @@ -89,8 +101,9 @@ void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qm } QQuickWindow* TabletScriptingInterface::getTabletWindow() { - TabletProxy* tablet = qobject_cast(getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = qobject_cast(getTablet(SYSTEM_TABLET)); QObject* qmlSurface = tablet->getTabletSurface(); + OffscreenQmlSurface* surface = dynamic_cast(qmlSurface); if (!surface) { @@ -156,7 +169,7 @@ void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEv void TabletScriptingInterface::processEvent(const QKeyEvent* event) { - TabletProxy* tablet = qobject_cast(getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = qobject_cast(getTablet(SYSTEM_TABLET)); QObject* qmlTablet = tablet->getQmlTablet(); QObject* qmlMenu = tablet->getQmlMenu(); @@ -185,10 +198,12 @@ class TabletRootWindow : public QmlWindowClass { virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; } }; -TabletProxy::TabletProxy(QString name) : _name(name) { +TabletProxy::TabletProxy(QObject* parent, QString name) : QObject(parent), _name(name) { } void TabletProxy::setToolbarMode(bool toolbarMode) { + std::lock_guard guard(_tabletMutex); + if (toolbarMode == _toolbarMode) { return; } @@ -288,7 +303,7 @@ bool TabletProxy::isPathLoaded(QVariant path) { return path.toString() == _currentPathLoaded.toString(); } void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) { - std::lock_guard guard(_mutex); + std::lock_guard guard(_tabletMutex); _qmlOffscreenSurface = qmlOffscreenSurface; _qmlTabletRoot = qmlTabletRoot; if (_qmlTabletRoot && _qmlOffscreenSurface) { @@ -519,7 +534,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS QObject* TabletProxy::addButton(const QVariant& properties) { auto tabletButtonProxy = QSharedPointer(new TabletButtonProxy(properties.toMap())); - std::lock_guard guard(_mutex); + std::lock_guard guard(_tabletMutex); _tabletButtonProxies.push_back(tabletButtonProxy); if (!_toolbarMode && _qmlTabletRoot) { auto tablet = getQmlTablet(); @@ -555,7 +570,7 @@ bool TabletProxy::onHomeScreen() { } void TabletProxy::removeButton(QObject* tabletButtonProxy) { - std::lock_guard guard(_mutex); + std::lock_guard guard(_tabletMutex); auto tablet = getQmlTablet(); if (!tablet) { @@ -743,12 +758,12 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : } void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { - std::lock_guard guard(_mutex); + std::lock_guard guard(_buttonMutex); _qmlButton = qmlButton; } void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { - std::lock_guard guard(_mutex); + std::lock_guard guard(_buttonMutex); _toolbarButtonProxy = toolbarButtonProxy; if (_toolbarButtonProxy) { QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot())); @@ -756,12 +771,12 @@ void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { } QVariantMap TabletButtonProxy::getProperties() const { - std::lock_guard guard(_mutex); + std::lock_guard guard(_buttonMutex); return _properties; } void TabletButtonProxy::editProperties(QVariantMap properties) { - std::lock_guard guard(_mutex); + std::lock_guard guard(_buttonMutex); QVariantMap::const_iterator iter = properties.constBegin(); while (iter != properties.constEnd()) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index aff14128db..85c1fdaf9a 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -71,7 +71,7 @@ private: void processTabletEvents(QObject* object, const QKeyEvent* event); protected: - std::mutex _mutex; + std::mutex _mapMutex; std::map _tabletProxies; QObject* _toolbarScriptingInterface { nullptr }; bool _toolbarMode { false }; @@ -90,7 +90,7 @@ class TabletProxy : public QObject { Q_PROPERTY(bool landscape READ getLandscape WRITE setLandscape) Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged) public: - TabletProxy(QString name); + TabletProxy(QObject* parent, QString name); void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface); @@ -248,7 +248,7 @@ protected: QVariant _initialPath { "" }; QVariant _currentPathLoaded { "" }; QString _name; - std::mutex _mutex; + std::mutex _tabletMutex; std::vector> _tabletButtonProxies; QQuickItem* _qmlTabletRoot { nullptr }; QObject* _qmlOffscreenSurface { nullptr }; @@ -309,7 +309,7 @@ signals: protected: QUuid _uuid; int _stableOrder; - mutable std::mutex _mutex; + mutable std::mutex _buttonMutex; QQuickItem* _qmlButton { nullptr }; QObject* _toolbarButtonProxy { nullptr }; QVariantMap _properties; From 84305c20498fe5ceda8fea22b214770c3b5f83cc Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 17:30:31 -0700 Subject: [PATCH 042/128] Fixing the by region update of the compressed texture to match the 4 x 4 tiles alignment --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 8 ++++++-- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 5db924dd5c..9fe6bbb772 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -429,9 +429,13 @@ void GL41VariableAllocationTexture::populateTransferQueue() { // consuming more than X bandwidth auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - auto bytesPerLine = mipSize / lines; + const uint32_t CHUNK_NUM_LINES { 4 }; + const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; + auto bytesPerChunk = mipSize / numChunks; + //auto bytesPerLine = mipSize / lines; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + // uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92d820e5f0..192b7f3088 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -197,9 +197,11 @@ void GL45ResourceTexture::populateTransferQueue() { // consuming more than X bandwidth auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - auto bytesPerLine = mipSize / lines; + const uint32_t CHUNK_NUM_LINES { 4 }; + const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; + auto bytesPerChunk = mipSize / numChunks; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); From 7b387c52c37cd0a777e9a408869811c2d6a908bd Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 3 May 2017 14:42:06 +1200 Subject: [PATCH 043/128] Fix reliability of keyboard enabling in tablet Web pages --- interface/resources/qml/controls/TabletWebView.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 04e784e2ba..333a79e509 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -19,7 +19,7 @@ Item { property alias address: displayUrl.text //for compatibility property string scriptURL property alias eventBridge: eventBridgeWrapper.eventBridge - property bool keyboardEnabled: HMD.active + property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false property bool isDesktop: false @@ -307,6 +307,7 @@ Item { Component.onCompleted: { web.isDesktop = (typeof desktop !== "undefined"); + keyboardEnabled = HMD.active; address = url; } From 8e12c1959d352c2ae456e313f6001f52e43416ab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 3 May 2017 09:53:32 -0700 Subject: [PATCH 044/128] Removing logspam on toggling scripts window --- interface/resources/qml/hifi/dialogs/RunningScripts.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index d95dbc2e55..0514bfbffd 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -34,9 +34,6 @@ ScrollingWindow { property var runningScriptsModel: ListModel { } property bool isHMD: false - onVisibleChanged: console.log("Running scripts visible changed to " + visible) - onShownChanged: console.log("Running scripts visible changed to " + visible) - Settings { category: "Overlay.RunningScripts" property alias x: root.x From a0a6dee7647ac20502e218e1258c7790f511ba38 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:36:38 -0700 Subject: [PATCH 045/128] some cleanups, delete far-grabs during transition to near-grab rather than set ttl to zero --- .../system/controllers/handControllerGrab.js | 95 +++++++------------ 1 file changed, 36 insertions(+), 59 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 6a7ed55417..add513d2b3 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode */ + setGrabCommunications, Menu, HMD, isInEditMode, AvatarManager */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -75,7 +75,6 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // -var linearTimeScale = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -155,7 +154,6 @@ var INCHES_TO_METERS = 1.0 / 39.3701; // these control how long an abandoned pointer line or action will hang around var ACTION_TTL = 15; // seconds -var ACTION_TTL_ZERO = 0; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; @@ -193,7 +191,6 @@ var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; var holdEnabled = true; var nearGrabEnabled = true; var farGrabEnabled = true; -var farToNearGrab = false; var myAvatarScalingEnabled = true; var objectScalingEnabled = true; var mostRecentSearchingHand = RIGHT_HAND; @@ -300,14 +297,13 @@ function getFingerWorldLocation(hand) { // Object assign polyfill if (typeof Object.assign != 'function') { Object.assign = function(target, varArgs) { - 'use strict'; - if (target == null) { + if (target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; - if (nextSource != null) { + if (nextSource !== null) { for (var nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; @@ -801,7 +797,7 @@ function calculateNearestStylusTarget(stylusTargets) { } return nearestStylusTarget; -}; +} // EntityPropertiesCache is a helper class that contains a cache of entity properties. // the hope is to prevent excess calls to Entity.getEntityProperties() @@ -1229,8 +1225,9 @@ function MyController(hand) { newState !== STATE_OVERLAY_LASER_TOUCHING)) { return; } - setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_DISTANCE_ROTATING) - || (newState === STATE_NEAR_GRABBING)); + setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || + (newState === STATE_DISTANCE_ROTATING) || + (newState === STATE_NEAR_GRABBING)); if (WANT_DEBUG || WANT_DEBUG_STATE) { var oldStateName = stateToName(this.state); var newStateName = stateToName(newState); @@ -1428,7 +1425,7 @@ function MyController(hand) { if (PICK_WITH_HAND_RAY) { this.overlayLineOff(); } - } + }; this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) { if (this.otherGrabbingLine === null) { @@ -1641,11 +1638,6 @@ function MyController(hand) { var tipPosition = this.stylusTip.position; - var candidates = { - entities: [], - overlays: [] - }; - // build list of stylus targets, near the stylusTip var stylusTargets = []; var candidateEntities = Entities.findEntities(tipPosition, WEB_DISPLAY_STYLUS_DISTANCE); @@ -1972,9 +1964,10 @@ function MyController(hand) { var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); var otherHandControllerState = this.getOtherHandController().state; - var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING - || otherHandControllerState === STATE_DISTANCE_HOLDING || otherHandControllerState === STATE_DISTANCE_ROTATING) - && this.getOtherHandController().grabbedThingID === hotspot.entityID); + var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING || + otherHandControllerState === STATE_DISTANCE_HOLDING || + otherHandControllerState === STATE_DISTANCE_ROTATING) && + this.getOtherHandController().grabbedThingID === hotspot.entityID); var hasParent = true; if (props.parentID === NULL_UUID) { hasParent = false; @@ -1999,7 +1992,7 @@ function MyController(hand) { return entityProps.cloneable; } return false; - } + }; this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); var props = entityPropertiesCache.getProps(entityID); @@ -2346,7 +2339,7 @@ function MyController(hand) { this.otherGrabbingLineOff(); } else if (this.otherGrabbingUUID !== null) { if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { - var avatar = AvatarList.getAvatar(this.otherGrabbingUUID); + var avatar = AvatarManager.getAvatar(this.otherGrabbingUUID); var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar. var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR)); var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid. @@ -2720,7 +2713,8 @@ function MyController(hand) { var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); - var candidateHotSpotEntities = Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); + var candidateHotSpotEntities = + Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); var potentialEquipHotspot = this.chooseBestEquipHotspotForFarToNearEquip(candidateHotSpotEntities); @@ -2730,40 +2724,23 @@ function MyController(hand) { this.grabbedThingID = potentialEquipHotspot.entityID; this.grabbedIsOverlay = false; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO - }); - - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedThingID).name + "'"); return; } } var rayPositionOnEntity = Vec3.subtract(grabbedProperties.position, this.offsetPosition); //Far to Near Grab: If object is draw by user inside FAR_TO_NEAR_GRAB_MAX_DISTANCE, grab it - if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, controllerLocation.position, FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { + if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, + controllerLocation.position, + FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { this.farToNearGrab = true; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO // Overriding ACTION_TTL,Assign ACTION_TTL_ZERO so that the object is dropped down immediately after the trigger is released. - }); - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_NEAR_GRABBING , "near grab entity '" + this.grabbedThingID + "'"); return; } @@ -2844,7 +2821,7 @@ function MyController(hand) { COLORS_GRAB_DISTANCE_HOLD, this.grabbedThingID); this.previousWorldControllerRotation = worldControllerRotation; - } + }; this.setupHoldAction = function() { this.actionID = Entities.addAction("hold", this.grabbedThingID, { @@ -3043,15 +3020,14 @@ function MyController(hand) { var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { - var item = Entities.getEntityProperties(item, ["name"]); - if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { + var itemWE = Entities.getEntityProperties(itemWE, ["name"]); + if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } - }) + }); var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; if (count >= limit && limit !== 0) { - delete limit; return; } @@ -3067,7 +3043,7 @@ function MyController(hand) { delete cUserData.grabbableKey.cloneable; delete cUserData.grabbableKey.cloneDynamic; delete cUserData.grabbableKey.cloneLimit; - delete cProperties.id + delete cProperties.id; cProperties.dynamic = dynamic; cProperties.locked = false; @@ -3133,7 +3109,7 @@ function MyController(hand) { _this.currentAngularVelocity = ZERO_VEC; _this.prevDropDetected = false; - } + }; if (isClone) { // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. @@ -3149,7 +3125,6 @@ function MyController(hand) { var ttl = ACTION_TTL; if (this.farToNearGrab) { - ttl = ACTION_TTL_ZERO; // farToNearGrab - Assign ACTION_TTL_ZERO so that, the object is dropped down immediately after the trigger is released. if(!this.triggerClicked){ this.farToNearGrab = false; } @@ -3322,7 +3297,7 @@ function MyController(hand) { this.maybeScale(props); } - if (this.actionID && this.actionTimeout - now < ttl * MSECS_PER_SEC) { + if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) { // if less than a 5 seconds left, refresh the actions ttl var success = Entities.updateAction(this.grabbedThingID, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", @@ -3761,10 +3736,12 @@ function MyController(hand) { var TABLET_MAX_TOUCH_DISTANCE = 0.01; if (this.stylusTarget) { - if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { + if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + distance2D(this.stylusTarget.position2D, + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } From 79604d3529e637b1504cd2618934dc7dc2b7d987 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:48:12 -0700 Subject: [PATCH 046/128] AvatarList not AvatarManager --- scripts/system/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index add513d2b3..61d123c0c5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode, AvatarManager */ + setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -2339,7 +2339,7 @@ function MyController(hand) { this.otherGrabbingLineOff(); } else if (this.otherGrabbingUUID !== null) { if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) { - var avatar = AvatarManager.getAvatar(this.otherGrabbingUUID); + var avatar = AvatarList.getAvatar(this.otherGrabbingUUID); var IN_FRONT_OF_AVATAR = { x: 0, y: 0.2, z: 0.4 }; // Up from hips and in front of avatar. var startPosition = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.rotation, IN_FRONT_OF_AVATAR)); var finishPisition = Vec3.sum(rayPickInfo.properties.position, // Entity's centroid. From 20051b970a4da51d40a6c744e027688e97720294 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 16:20:59 -0700 Subject: [PATCH 047/128] make clone work again --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 61d123c0c5..48b04192f5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3020,7 +3020,7 @@ function MyController(hand) { var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { - var itemWE = Entities.getEntityProperties(itemWE, ["name"]); + var itemWE = Entities.getEntityProperties(item, ["name"]); if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } From 0263021c0c5ad88026e1df9e865f2abcd0905085 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:26:51 -0700 Subject: [PATCH 048/128] Fix loaded being reset to false for KTX resources --- .../src/model-networking/TextureCache.cpp | 2 +- libraries/networking/src/ResourceCache.cpp | 12 +++++++----- libraries/networking/src/ResourceCache.h | 7 ++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7745766177..7c1ec74eb8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -421,7 +421,7 @@ void NetworkTexture::startRequestForNextMipLevel() { _ktxResourceState = PENDING_MIP_REQUEST; - init(); + init(false); float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; setLoadPriority(this, priority); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 8d4edab2d5..88ea68780b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -533,13 +533,13 @@ void Resource::ensureLoading() { } void Resource::setLoadPriority(const QPointer& owner, float priority) { - if (!(_failedToLoad || _loaded)) { + if (!(_failedToLoad)) { _loadPriorities.insert(owner, priority); } } void Resource::setLoadPriorities(const QHash, float>& priorities) { - if (_failedToLoad || _loaded) { + if (_failedToLoad) { return; } for (QHash, float>::const_iterator it = priorities.constBegin(); @@ -549,7 +549,7 @@ void Resource::setLoadPriorities(const QHash, float>& prioriti } void Resource::clearLoadPriority(const QPointer& owner) { - if (!(_failedToLoad || _loaded)) { + if (!(_failedToLoad)) { _loadPriorities.remove(owner); } } @@ -612,10 +612,12 @@ void Resource::allReferencesCleared() { } } -void Resource::init() { +void Resource::init(bool resetLoaded) { _startedLoading = false; _failedToLoad = false; - _loaded = false; + if (resetLoaded) { + _loaded = false; + } _attempts = 0; _activeUrl = _url; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 51c8d8554a..a908e11bcc 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -425,7 +425,7 @@ protected slots: void attemptRequest(); protected: - virtual void init(); + virtual void init(bool resetLoaded = true); /// Called by ResourceCache to begin loading this Resource. /// This method can be overriden to provide custom request functionality. If this is done, @@ -454,9 +454,14 @@ protected: QUrl _url; QUrl _activeUrl; ByteRange _requestByteRange; + + // _loaded == true means we are in a loaded and usable state. It is possible that there may still be + // active requests/loading while in this state. Example: Progressive KTX downloads, where higher resolution + // mips are being download. bool _startedLoading = false; bool _failedToLoad = false; bool _loaded = false; + QHash, float> _loadPriorities; QWeakPointer _self; QPointer _cache; From 2c19c5f04526326108ad9b4e7ab42a5a604be5a6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:27:20 -0700 Subject: [PATCH 049/128] Fix KTX creation not handling invalid storage --- libraries/ktx/src/ktx/Reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ktx/src/ktx/Reader.cpp b/libraries/ktx/src/ktx/Reader.cpp index b22f262e85..440e2f048c 100644 --- a/libraries/ktx/src/ktx/Reader.cpp +++ b/libraries/ktx/src/ktx/Reader.cpp @@ -174,7 +174,7 @@ namespace ktx { } std::unique_ptr KTX::create(const StoragePointer& src) { - if (!src) { + if (!src || !(*src)) { return nullptr; } From 7999ed6f605b29d5082d408be779a0a8b82122b2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:28:00 -0700 Subject: [PATCH 050/128] Fix Resource::refresh for NetworkTexture --- .../src/model-networking/TextureCache.cpp | 21 +++++++++++++++++++ .../src/model-networking/TextureCache.h | 2 ++ libraries/networking/src/ResourceCache.h | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7c1ec74eb8..969058c240 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -682,6 +682,27 @@ void NetworkTexture::loadContent(const QByteArray& content) { QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels)); } +void NetworkTexture::refresh() { + if ((_ktxHeaderRequest || _ktxMipRequest) && !loaded && !_failedToLoad) { + return; + } + if (_ktxHeaderRequest || _ktxMipRequest) { + if (_ktxHeaderRequest) { + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + } + if (_ktxMipRequest) { + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + } + ResourceCache::requestCompleted(_self); + } + + Resource::refresh(); +} + ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) : _resource(resource), _url(url), diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c7a7799216..aabc7fcb85 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -58,6 +58,8 @@ public: gpu::TexturePointer getFallbackTexture() const; + void refresh() override; + signals: void networkTextureCreated(const QWeakPointer& self); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index a908e11bcc..f94e1e26d2 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -385,7 +385,7 @@ public: float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } /// Refreshes the resource. - void refresh(); + virtual void refresh(); void setSelf(const QWeakPointer& self) { _self = self; } From a886963e20699192cfa0effccb312a0840f1fc97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 11:12:11 -0700 Subject: [PATCH 051/128] Fix refresh crash with ktx textures --- .../src/model-networking/TextureCache.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 969058c240..06ff891cb9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -472,6 +472,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) { void NetworkTexture::ktxHeaderRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA); + if (!_ktxHeaderRequest) { + return; + } + _ktxHeaderRequestFinished = true; maybeHandleFinishedInitialLoad(); } @@ -479,6 +483,10 @@ void NetworkTexture::ktxHeaderRequestFinished() { void NetworkTexture::ktxMipRequestFinished() { Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP); + if (!_ktxMipRequest) { + return; + } + if (_ktxResourceState == LOADING_INITIAL_DATA) { _ktxHighMipRequestFinished = true; maybeHandleFinishedInitialLoad(); @@ -683,7 +691,7 @@ void NetworkTexture::loadContent(const QByteArray& content) { } void NetworkTexture::refresh() { - if ((_ktxHeaderRequest || _ktxMipRequest) && !loaded && !_failedToLoad) { + if ((_ktxHeaderRequest || _ktxMipRequest) && !_loaded && !_failedToLoad) { return; } if (_ktxHeaderRequest || _ktxMipRequest) { From c583ffbac447d865914442817eaa5323b1fc67fd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 May 2017 11:30:09 -0700 Subject: [PATCH 052/128] Clean up names and comments --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 11 +++++------ .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 9fe6bbb772..a9d1ddb914 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -427,15 +427,14 @@ void GL41VariableAllocationTexture::populateTransferQueue() { // break down the transfers into chunks so that no single transfer is // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - const uint32_t CHUNK_NUM_LINES { 4 }; - const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; - auto bytesPerChunk = mipSize / numChunks; - //auto bytesPerLine = mipSize / lines; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; Q_ASSERT(0 == (mipSize % lines)); - // uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); - uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 192b7f3088..d46f38d88f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -195,13 +195,14 @@ void GL45ResourceTexture::populateTransferQueue() { // break down the transfers into chunks so that no single transfer is // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - const uint32_t CHUNK_NUM_LINES { 4 }; - const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; - auto bytesPerChunk = mipSize / numChunks; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); From 7f6a1be2c7b2d294254d130d291ab2a2d7d44773 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 19:57:09 -0400 Subject: [PATCH 053/128] continue idling while minimized --- interface/src/Application.cpp | 17 +++++++++++++++++ interface/src/Application.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fa66262cc..f9669702c7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2530,6 +2530,11 @@ bool Application::event(QEvent* event) { isPaintingThrottled = false; + return true; + } else if ((int)event->type() == (int)Idle) { + float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); + idle(nsecsElapsed); + return true; } @@ -6499,10 +6504,22 @@ void Application::activeChanged(Qt::ApplicationState state) { } void Application::windowMinimizedChanged(bool minimized) { + // initialize the _minimizedWindowTimer + static std::once_flag once; + std::call_once(once, [&] { + connect(&_minimizedWindowTimer, &QTimer::timeout, this, [] { + QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(static_cast(Idle)), Qt::HighEventPriority); + }); + }); + + // avoid rendering to the display plugin but continue posting Idle events, + // so that physics continues to simulate and the deadlock watchdog knows we're alive if (!minimized && !getActiveDisplayPlugin()->isActive()) { + _minimizedWindowTimer.stop(); getActiveDisplayPlugin()->activate(); } else if (minimized && getActiveDisplayPlugin()->isActive()) { getActiveDisplayPlugin()->deactivate(); + _minimizedWindowTimer.start(THROTTLED_SIM_FRAME_PERIOD_MS); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 041f1f8930..86b2335e62 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -138,6 +138,7 @@ public: enum Event { Present = DisplayPlugin::Present, Paint = Present + 1, + Idle = Paint + 1, Lambda = Paint + 1 }; @@ -536,6 +537,7 @@ private: RateCounter<> _avatarSimCounter; RateCounter<> _simCounter; + QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; From 42887d41c464846877265298600731dbc8538ad7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 13:45:48 -0700 Subject: [PATCH 054/128] debugging --- scripts/system/tablet-goto.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index b9ca6f7407..6c18d0a681 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -145,15 +145,22 @@ var stories = {}, pingPong = false; var DEBUG = true; //fixme + function debug() { + if (!DEBUG) { + return; + } + print([].map.call(arguments, JSON.stringify)); + } + function expire(id) { request({ uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, method: 'PUT', body: {expired: true} }, function (error, response) { + debug('expired story', id, 'error:', error, 'response:', response); if (error || (response.status !== 'success')) { print("ERROR expiring story: ", error || response.status); - return; } }); } @@ -170,9 +177,11 @@ 'per_page=' + count ]; var url = location.metaverseServerUrl + '/api/v1/user_stories?' + options.join('&'); + url = 'https://highfidelity.com/api/v1/user_stories?include_actions=announcement'; //fixme remove request({ uri: url }, function (error, data) { + debug(url, error, data); if (error || (data.status !== 'success')) { print("Error: unable to get", url, error || data.status); return; @@ -181,6 +190,7 @@ pingPong = !pingPong; data.user_stories.forEach(function (story) { var stored = stories[story.id], storedOrNew = stored || story; + debug('story exists:', !!stored, storedOrNew); if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { expire(story.id); return; // before marking From 0f9002d724e94d0776b5c5d2dca9f9a71934b1ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 1 May 2017 14:13:59 -0700 Subject: [PATCH 055/128] Prevent possible crash in texture buffering thread --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 10 +++++++++- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 7f075a1698..e85e74ce73 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -150,6 +150,7 @@ GLExternalTexture::~GLExternalTexture() { // Variable sized textures using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; using WorkQueue = GLVariableAllocationSupport::WorkQueue; +using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; std::list GLVariableAllocationSupport::_memoryManagedTextures; MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; @@ -159,6 +160,7 @@ WorkQueue GLVariableAllocationSupport::_transferQueue; WorkQueue GLVariableAllocationSupport::_promoteQueue; WorkQueue GLVariableAllocationSupport::_demoteQueue; TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; #define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f @@ -553,9 +555,15 @@ void GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& curr if (!_pendingTransfers.empty()) { // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture _currentTransferTexture = currentTexture; - if (_pendingTransfers.front()->tryTransfer()) { + // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job + // doesn't leave scope, causing a crash in the buffering thread + _currentTransferJob = _pendingTransfers.front(); + // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering + // is complete + if (_currentTransferJob->tryTransfer()) { _pendingTransfers.pop(); _currentTransferTexture.reset(); + _currentTransferJob.reset(); } } } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index e0b8a63a99..2603b36b21 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -86,7 +86,8 @@ public: void transfer(); }; - using TransferQueue = std::queue>; + using TransferJobPointer = std::shared_ptr; + using TransferQueue = std::queue; static MemoryPressureState _memoryPressureState; public: @@ -100,6 +101,7 @@ protected: static WorkQueue _promoteQueue; static WorkQueue _demoteQueue; static TexturePointer _currentTransferTexture; + static TransferJobPointer _currentTransferJob; static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; static const uvec3 MAX_TRANSFER_DIMENSIONS; static const size_t MAX_TRANSFER_SIZE; From e96c3e84b42842d8ca4b305a2ed68d8e0b409514 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 13:58:49 -0700 Subject: [PATCH 056/128] if a script attempts to edit a non-entity, don't send the edit packet over the wire --- libraries/entities/src/EntityScriptingInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 10479e931c..7b2f8b8554 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -407,9 +407,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } + bool entityFound = false; _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { + entityFound = true; // make sure the properties has a type, so that the encode can know which properties to include properties.setType(entity->getType()); bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); @@ -464,7 +466,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& }); } }); - queueEntityMessage(PacketType::EntityEdit, entityID, properties); + if (entityFound) { + queueEntityMessage(PacketType::EntityEdit, entityID, properties); + } else { + qDebug() << "warning: attempted edit on unknown entity: " << id; + } return id; } From cb51aaab6189b085f2422f9cf13bbaa2e04060ad Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:40:39 -0700 Subject: [PATCH 057/128] ARG --- interface/src/Application.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 86b2335e62..385aa0a286 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -139,7 +139,7 @@ public: Present = DisplayPlugin::Present, Paint = Present + 1, Idle = Paint + 1, - Lambda = Paint + 1 + Lambda = Idle + 1 }; // FIXME? Empty methods, do we still need them? From 14315155b5de932c702184edd5f3e08fe2f9c43c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:41:00 -0700 Subject: [PATCH 058/128] Remove the +1s --- interface/src/Application.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 385aa0a286..367d878dd4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -137,9 +137,9 @@ public: enum Event { Present = DisplayPlugin::Present, - Paint = Present + 1, - Idle = Paint + 1, - Lambda = Idle + 1 + Paint, + Idle, + Lambda }; // FIXME? Empty methods, do we still need them? From aa74dda9f36bb77a83b9e3f460291d246a91f3ef Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 16:29:37 -0700 Subject: [PATCH 059/128] cleanup --- scripts/system/tablet-goto.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 13c4830d31..2cdfecede4 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -16,6 +16,13 @@ (function () { // BEGIN LOCAL_SCOPE var request = Script.require('request').request; + var DEBUG = false; + function debug() { + if (!DEBUG) { + return; + } + print([].map.call(arguments, JSON.stringify)); + } var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; @@ -101,14 +108,6 @@ tablet.screenChanged.connect(onScreenChanged); var stories = {}, pingPong = false; - var DEBUG = true; //fixme - function debug() { - if (!DEBUG) { - return; - } - print([].map.call(arguments, JSON.stringify)); - } - function expire(id) { request({ uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, @@ -123,7 +122,7 @@ } function pollForAnnouncements() { // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? - var actions = DEBUG ? 'snapshot' : 'announcement'; + var actions = 'announcement'; var count = DEBUG ? 10 : 100; var options = [ 'now=' + new Date().toISOString(), @@ -134,7 +133,6 @@ 'per_page=' + count ]; var url = location.metaverseServerUrl + '/api/v1/user_stories?' + options.join('&'); - url = 'https://highfidelity.com/api/v1/user_stories?include_actions=announcement'; //fixme remove request({ uri: url }, function (error, data) { @@ -148,7 +146,7 @@ data.user_stories.forEach(function (story) { var stored = stories[story.id], storedOrNew = stored || story; debug('story exists:', !!stored, storedOrNew); - if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { + if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { expire(story.id); return; // before marking } From 3e57e804655d5d5043b985c4d16e6ba48ae0f519 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 16:39:34 -0700 Subject: [PATCH 060/128] use new request module. --- scripts/system/snapshot.js | 49 +------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index b93595f0b4..762dad1f7e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -38,54 +38,7 @@ var METAVERSE_BASE = location.metaverseServerUrl; // It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story, // POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS -function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; - // QT bug: apparently doesn't handle onload. Workaround using readyState. - httpRequest.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (httpRequest.readyState >= READY_STATE_DONE) { - var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, - response = !error && httpRequest.responseText, - contentType = !error && httpRequest.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - callback(error, response); - } - }; - if (typeof options === 'string') { - options = { uri: options }; - } - if (options.url) { - options.uri = options.url; - } - if (!options.method) { - options.method = 'GET'; - } - if (options.body && (options.method === 'GET')) { // add query parameters - var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; - for (key in options.body) { - params.push(key + '=' + options.body[key]); - } - options.uri += appender + params.join('&'); - delete options.body; - } - if (options.json) { - options.headers = options.headers || {}; - options.headers["Content-type"] = "application/json"; - options.body = JSON.stringify(options.body); - } - for (key in options.headers || {}) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); -} +var request = Script.require('request').request; function openLoginWindow() { if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) From 0fff7678bf4836feee84e33fa791e13daf0788ac Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 19:07:46 -0700 Subject: [PATCH 061/128] code review --- libraries/entities/src/EntityScriptingInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 7b2f8b8554..2189970cc7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -407,7 +407,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } - bool entityFound = false; + bool entityFound { false }; _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { @@ -469,7 +469,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { - qDebug() << "warning: attempted edit on unknown entity: " << id; + qCWarning(entities) << "attempted edit on unknown entity: " << id; } return id; } From 5e36bebc969fc31e15ea871849527407f9946125 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 May 2017 19:23:21 -0700 Subject: [PATCH 062/128] Store irradiance in the KTX files. --- libraries/gpu/src/gpu/Texture_ktx.cpp | 82 +++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d2f93c0036..d5710bf84b 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -99,6 +99,61 @@ struct GPUKTXPayload { }; const std::string GPUKTXPayload::KEY { "hifi.gpu" }; + +struct IrradianceKTXPayload { + using Version = uint8; + + static const std::string KEY; + static const Version CURRENT_VERSION{ 0 }; + static const size_t PADDING{ 3 }; + static const size_t SIZE{ sizeof(Version) + sizeof(SphericalHarmonics) + PADDING }; + static_assert(IrradianceKTXPayload::SIZE == 148, "Packing size may differ between platforms"); + static_assert(IrradianceKTXPayload::SIZE % 4 == 0, "IrradianceKTXPayload is not 4 bytes aligned"); + + SphericalHarmonics _irradianceSH; + + Byte* serialize(Byte* data) const { + *(Version*)data = CURRENT_VERSION; + data += sizeof(Version); + + memcpy(data, &_irradianceSH, sizeof(SphericalHarmonics)); + data += sizeof(SphericalHarmonics); + + return data + PADDING; + } + + bool unserialize(const Byte* data, size_t size) { + if (size != SIZE) { + return false; + } + + Version version = *(const Version*)data; + if (version != CURRENT_VERSION) { + return false; + } + data += sizeof(Version); + + memcpy(&_irradianceSH, data, sizeof(SphericalHarmonics)); + data += sizeof(SphericalHarmonics); + + return true; + } + + static bool isIrradianceKTX(const ktx::KeyValue& val) { + return (val._key.compare(KEY) == 0); + } + + static bool findInKeyValues(const ktx::KeyValues& keyValues, IrradianceKTXPayload& payload) { + auto found = std::find_if(keyValues.begin(), keyValues.end(), isIrradianceKTX); + if (found != keyValues.end()) { + auto value = found->_value; + return payload.unserialize(value.data(), value.size()); + } + return false; + } +}; +const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" }; + KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { // We are doing a lot of work here just to get descriptor data @@ -304,16 +359,27 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } } - GPUKTXPayload keyval; - keyval._samplerDesc = texture.getSampler().getDesc(); - keyval._usage = texture.getUsage(); - keyval._usageType = texture.getUsageType(); + GPUKTXPayload gpuKeyval; + gpuKeyval._samplerDesc = texture.getSampler().getDesc(); + gpuKeyval._usage = texture.getUsage(); + gpuKeyval._usageType = texture.getUsageType(); + Byte keyvalPayload[GPUKTXPayload::SIZE]; - keyval.serialize(keyvalPayload); + gpuKeyval.serialize(keyvalPayload); ktx::KeyValues keyValues; keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload); + if (texture.getIrradiance()) { + IrradianceKTXPayload irradianceKeyval; + irradianceKeyval._irradianceSH = *texture.getIrradiance(); + + Byte irradianceKeyvalPayload[IrradianceKTXPayload::SIZE]; + irradianceKeyval.serialize(irradianceKeyvalPayload); + + keyValues.emplace_back(IrradianceKTXPayload::KEY, (uint32)IrradianceKTXPayload::SIZE, (ktx::Byte*) &irradianceKeyvalPayload); + } + auto hash = texture.sourceHash(); if (!hash.empty()) { // the sourceHash is an std::string in hex @@ -409,6 +475,12 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe // Assing the mips availables texture->setStoredMipFormat(mipFormat); texture->setKtxBacking(ktxfile); + + IrradianceKTXPayload irradianceKtxKeyValue; + if (IrradianceKTXPayload::findInKeyValues(descriptor.keyValues, irradianceKtxKeyValue)) { + texture->overrideIrradiance(std::make_shared(irradianceKtxKeyValue._irradianceSH)); + } + return texture; } From ed09dcbdc948d102918efc09a233e50051a6adc3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:32:06 -0700 Subject: [PATCH 063/128] don't call editEntity on overlays --- scripts/system/libraries/WebTablet.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index e71aefc51e..5355677bf7 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -274,7 +274,8 @@ WebTablet.prototype.getLocation = function() { }; WebTablet.prototype.setHomeButtonTexture = function() { - Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); + // TODO - is this still needed? + // Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); }; WebTablet.prototype.setURL = function (url) { @@ -337,7 +338,8 @@ WebTablet.prototype.geometryChanged = function (geometry) { // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(this.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(this.tabletEntityID, tabletProperties); } }; @@ -438,7 +440,8 @@ WebTablet.prototype.onHmdChanged = function () { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(this.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(this.tabletEntityID, tabletProperties); // Full scene FXAA should be disabled on the overlay when the tablet in desktop mode. // This should make the text more readable. @@ -529,7 +532,8 @@ WebTablet.prototype.cameraModeChanged = function (newMode) { var tabletProperties = {}; // compute position, rotation & parentJointIndex of the tablet self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties); - Entities.editEntity(self.tabletEntityID, tabletProperties); + // TODO -- is this still needed? + // Entities.editEntity(self.tabletEntityID, tabletProperties); } }; From 69944cc76dcfa1485e6c8a613b188729023766f9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:32:49 -0700 Subject: [PATCH 064/128] add a way to get a SpatiallyNestable's name without knowing what its type is --- interface/src/ui/overlays/Base3DOverlay.cpp | 7 +++++++ interface/src/ui/overlays/Base3DOverlay.h | 5 +++++ libraries/avatars/src/AvatarData.h | 2 ++ libraries/entities/src/EntityItem.h | 2 +- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++++++++- libraries/shared/src/SpatiallyNestable.h | 2 ++ 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index d7057c6faa..6f1167cfc9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -81,6 +81,10 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { QVariantMap properties = originalProperties; + if (properties["name"].isValid()) { + setName(properties["name"].toString()); + } + // carry over some legacy keys if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { if (properties["p1"].isValid()) { @@ -207,6 +211,9 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { } QVariant Base3DOverlay::getProperty(const QString& property) { + if (property == "name") { + return _name; + } if (property == "position" || property == "start" || property == "p1" || property == "point") { return vec3toVariant(getPosition()); } diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index a1c23e5cd8..29d4c093a9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -26,6 +26,9 @@ public: virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); } void setOverlayID(OverlayID overlayID) override { setID(overlayID); } + virtual QString getName() const override { return QString("Overlay:") + _name; } + void setName(QString name) { _name = name; } + // getters virtual bool is3D() const override { return true; } @@ -74,6 +77,8 @@ protected: bool _drawInFront; bool _isAA; bool _isGrabbable { false }; + + QString _name; }; #endif // hifi_Base3DOverlay_h diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4c79470de..a93f37839f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -357,6 +357,8 @@ class AvatarData : public QObject, public SpatiallyNestable { public: + virtual QString getName() const override { return QString("Avatar:") + _displayName; } + static const QString FRAME_NAME; static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ff5f12b2f7..1896893b52 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -281,7 +281,7 @@ public: float getAngularDamping() const; void setAngularDamping(float value); - QString getName() const; + virtual QString getName() const override; void setName(const QString& value); QString getDebugName(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2189970cc7..c499ce4acc 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -469,7 +469,19 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { - qCWarning(entities) << "attempted edit on unknown entity: " << id; + QString name = "unknown"; + QSharedPointer parentFinder = DependencyManager::get(); + if (parentFinder) { + bool success; + auto nestableWP = parentFinder->find(id, success, static_cast(_entityTree.get())); + if (success) { + auto nestable = nestableWP.lock(); + if (nestable) { + name = nestable->getName(); + } + } + } + qCWarning(entities) << "attempted edit on unknown entity: " << id << name; } return id; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 820c8685d7..0f89016f54 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -42,6 +42,8 @@ public: virtual const QUuid getID() const; virtual void setID(const QUuid& id); + virtual QString getName() const { return "SpatiallyNestable"; } + virtual const QUuid getParentID() const; virtual void setParentID(const QUuid& parentID); From 9171995ed32f0fbbdf1ee05a32283e533cf40610 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:41:00 -0700 Subject: [PATCH 065/128] add a comment --- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c499ce4acc..153d9044e9 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -469,6 +469,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (entityFound) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); } else { + // attempt to get a name for the mystery ID QString name = "unknown"; QSharedPointer parentFinder = DependencyManager::get(); if (parentFinder) { From 9d92ed2b9bf1e3be72955b6b1aae08b690aceb79 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 May 2017 16:55:10 +1200 Subject: [PATCH 066/128] Fix keyboard not appearing in social media snapshot sharing dialogs --- interface/resources/qml/controls/TabletWebView.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index ee0f73c259..51b6c16a4c 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -281,6 +281,7 @@ Item { web.initialPage = webview.url; startingUp = false; } + webview.forceActiveFocus(); } } From 30604ab901d23196820cba121fb75b5af37c95d8 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 May 2017 16:56:18 +1200 Subject: [PATCH 067/128] Fix some typos noticed in passing --- interface/resources/qml/controls/TabletWebView.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 51b6c16a4c..d288872289 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -238,7 +238,7 @@ Item { worldId: WebEngineScript.MainWorld } - property string urlTag: "noDownload=false"; + property string urlTag: "noDownload=false"; userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] property string newUrl: "" @@ -264,7 +264,7 @@ Item { keyboard.resetShiftMode(false); // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); + var url = loadRequest.url.toString(); if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { root.stop(); @@ -273,7 +273,7 @@ Item { } if (WebEngineView.LoadFailedStatus == loadRequest.status) { - console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString()); + console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString()); } if (WebEngineView.LoadSucceededStatus == loadRequest.status) { From 99ed2556f26baeb1fea2975e63469ab9d11d90bd Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 16:11:07 +0200 Subject: [PATCH 068/128] Do not send non primary button events to Tablet window --- interface/src/ui/overlays/Web3DOverlay.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index fedead5aa5..38bf5b293b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -421,6 +421,11 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } + //do not send non primary button events to tablet + if (event.getButton() != PointerEvent::PrimaryButton) { + return; + } + QTouchEvent::TouchPoint point; point.setId(event.getID()); point.setState(touchPointState); From 74a82f92df94b21909532cd8b563177dc6a96409 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 4 May 2017 10:05:17 -0700 Subject: [PATCH 069/128] return null uuid for failure --- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 153d9044e9..a4ec7b7636 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -483,6 +483,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } } qCWarning(entities) << "attempted edit on unknown entity: " << id << name; + return QUuid(); // null UUID to indicate failure } return id; } From c7f5115e26a6cdc7e6e33649c475ff7f39550105 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 16:38:25 -0700 Subject: [PATCH 070/128] Move help text to above buttons --- scripts/system/html/css/SnapshotReview.css | 4 ++-- scripts/system/html/js/SnapshotReview.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 29c5f465d7..bd9bb81fdc 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -158,7 +158,7 @@ input[type=button].naked:active { align-items: flex-end; height: 40px; width: calc(100% - 60px); - margin-bottom: 25px; + margin-bottom: -24px; margin-left: 0; } .shareButtons img { @@ -178,7 +178,7 @@ input[type=button].naked:active { height: 25px; line-height: 25px; position: absolute; - bottom: 0; + bottom: 40px; left: 73px; right: 0; font-family: Raleway-Regular; diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 6365c2be18..a8a73b62bc 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -134,7 +134,8 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareBarInnerHTML = '
' + + var shareBarInnerHTML = '' + + '
' + '' + '' + '' + @@ -151,8 +152,6 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled shareBar.innerHTML = shareBarInnerHTML; - shareBar.innerHTML += ''; - // Add onclick handler to parent DIV's img to toggle share buttons document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; From b01bff061e7a6f0122eb3b6562e6f162628ff787 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 10:06:33 -0700 Subject: [PATCH 071/128] Hover lock when uploading --- scripts/system/html/js/SnapshotReview.js | 99 +++++++++++++----------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index a8a73b62bc..4aede9e3bc 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -261,6 +261,7 @@ function showUploadingMessage(selectedID, destination) { var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); shareBarHelp.innerHTML = 'Preparing to Share'; + shareBarHelp.classList.add("uploading"); shareBarHelp.setAttribute("data-destination", destination); } function hideUploadingMessageAndShare(selectedID, storyID) { @@ -270,6 +271,8 @@ function hideUploadingMessageAndShare(selectedID, storyID) { var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), shareBarHelpDestination = shareBarHelp.getAttribute("data-destination"); + + shareBarHelp.classList.remove("uploading"); if (shareBarHelpDestination) { switch (shareBarHelpDestination) { case 'blast': @@ -387,54 +390,56 @@ function shareButtonHovered(destination, selectedID) { shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes, itr; - for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { - shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; - } - shareBarHelp.style.opacity = "1.0"; + if (!shareBarHelp.classList.contains("uploading")) { - switch (destination) { - case 'blast': - var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"); - if (!blastToConnectionsButton.classList.contains("disabled")) { - shareBarHelp.style.backgroundColor = "#EA4C5F"; - shareBarHelp.style.opacity = "1.0"; - blastToConnectionsButton.style.backgroundColor = "#EA4C5F"; - blastToConnectionsButton.style.opacity = "1.0"; - shareBarHelp.innerHTML = blastShareText; - } else { - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; - blastToConnectionsButton.style.backgroundColor = "#000000"; - blastToConnectionsButton.style.opacity = "0.5"; - shareBarHelp.innerHTML = blastAlreadySharedText; - } - break; - case 'hifi': - var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"); - if (!shareWithEveryoneButton.classList.contains("disabled")) { - shareBarHelp.style.backgroundColor = "#1FC6A6"; - shareBarHelp.style.opacity = "1.0"; - shareWithEveryoneButton.style.backgroundColor = "#1FC6A6"; - shareWithEveryoneButton.style.opacity = "1.0"; - shareBarHelp.innerHTML = hifiShareText; - } else { - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; - shareWithEveryoneButton.style.backgroundColor = "#000000"; - shareWithEveryoneButton.style.opacity = "0.5"; - shareBarHelp.innerHTML = hifiAlreadySharedText; - } - break; - case 'facebook': - shareBarHelp.style.backgroundColor = "#3C58A0"; - shareBarHelp.innerHTML = facebookShareText; - document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; - break; - case 'twitter': - shareBarHelp.style.backgroundColor = "#00B4EE"; - shareBarHelp.innerHTML = twitterShareText; - document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; - break; + for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { + shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; + } + shareBarHelp.style.opacity = "1.0"; + switch (destination) { + case 'blast': + var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"); + if (!blastToConnectionsButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#EA4C5F"; + shareBarHelp.style.opacity = "1.0"; + blastToConnectionsButton.style.backgroundColor = "#EA4C5F"; + blastToConnectionsButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = blastShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = blastAlreadySharedText; + } + break; + case 'hifi': + var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"); + if (!shareWithEveryoneButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#1FC6A6"; + shareBarHelp.style.opacity = "1.0"; + shareWithEveryoneButton.style.backgroundColor = "#1FC6A6"; + shareWithEveryoneButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = hifiShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = hifiAlreadySharedText; + } + break; + case 'facebook': + shareBarHelp.style.backgroundColor = "#3C58A0"; + shareBarHelp.innerHTML = facebookShareText; + document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; + break; + case 'twitter': + shareBarHelp.style.backgroundColor = "#00B4EE"; + shareBarHelp.innerHTML = twitterShareText; + document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; + break; + } } } function shareButtonClicked(destination, selectedID) { From 3f977a6743155ef8a3e135a893f87ce72be58638 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 May 2017 10:00:32 -0700 Subject: [PATCH 072/128] Add a simple check on the uniform buffer slot to make sure it s valid --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 1d1f92b297..5bc9cfa56e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -149,6 +149,10 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; + if (slot > GLBackend::MAX_NUM_UNIFORM_BUFFERS) { + // "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" + slot + " which doesn't exist. MaxNumUniformBuffers = " + getMaxNumUniformBuffers()); + return; + } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; From 389d9405f98aa71165eaeaf997b65dba254d2eb6 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 4 May 2017 10:18:14 -0700 Subject: [PATCH 073/128] Showing the comments --- libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 5bc9cfa56e..2d71e8ed78 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -149,8 +149,8 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; - if (slot > GLBackend::MAX_NUM_UNIFORM_BUFFERS) { - // "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" + slot + " which doesn't exist. MaxNumUniformBuffers = " + getMaxNumUniformBuffers()); + if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); return; } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); @@ -207,7 +207,7 @@ void GLBackend::resetResourceStage() { void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { - // "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" + slot + " which doesn't exist. MaxNumResourceBuffers = " + getMaxNumResourceBuffers()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); return; } @@ -237,7 +237,7 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } From ea2b188b64fb89ff51bd9bdf3c958757296afb4c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 2 May 2017 17:07:50 -0700 Subject: [PATCH 074/128] Recompute Y from X/Z for normal maps --- libraries/render-utils/src/MaterialTextures.slh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 7b73896cc5..a530f4faf2 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -64,7 +64,9 @@ float fetchRoughnessMap(vec2 uv) { uniform sampler2D normalMap; vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out - return normalize(texture(normalMap, uv).rbg -vec3(0.5, 0.5, 0.5)); + vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); + vec2 t2 = t*t; + return normalize(vec3(t.x, sqrt(1 - t2.x - t2.y), t.y)); } <@endif@> From 10289f542319dfe689a51b581305d247eb2c77f9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 2 May 2017 19:39:07 -0700 Subject: [PATCH 075/128] Format non compressed normal maps to new format. --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 3 ++ libraries/gpu/src/gpu/Format.cpp | 2 ++ libraries/gpu/src/gpu/Format.h | 1 + libraries/gpu/src/gpu/Texture_ktx.cpp | 2 ++ libraries/image/src/image/Image.cpp | 36 ++++++++++--------- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 7f724cce65..12bfb8e70b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -140,6 +140,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: result = GL_RG8; break; default: @@ -289,6 +290,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: @@ -516,6 +518,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 19d8855bd9..a9c7d249a8 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -25,6 +25,8 @@ const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1 const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; +const Element Element::VEC2_XY{ VEC2, NUINT8, XY }; + const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index f69e8d9386..9fadcecb22 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -234,6 +234,7 @@ public: static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_XY; + static const Element VEC2_XY; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; static const Element VEC2F_XY; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d2f93c0036..4952e7a83b 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -423,6 +423,8 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); + } else if (texelFormat == Format::VEC2_XY && mipFormat == Format::VEC2_XY) { + header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGBA_MASK && mipFormat == Format::COLOR_COMPRESSED_SRGBA_MASK) { diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 707a2e4496..63c7d0d8ab 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -290,6 +290,19 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { float inputGamma = 2.2f; float outputGamma = 2.2f; + nvtt::InputOptions inputOptions; + inputOptions.setTextureLayout(textureType, width, height); + inputOptions.setMipmapData(data, width, height); + + inputOptions.setFormat(inputFormat); + inputOptions.setGamma(inputGamma, outputGamma); + inputOptions.setAlphaMode(alphaMode); + inputOptions.setWrapMode(wrapMode); + inputOptions.setRoundMode(roundMode); + + inputOptions.setMipmapGeneration(true); + inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); + nvtt::CompressionOptions compressionOptions; compressionOptions.setQuality(nvtt::Quality_Production); @@ -346,26 +359,17 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelFormat(8, 0, 0, 0); + } else if (mipFormat == gpu::Element::VEC2_XY) { + inputOptions.setNormalMap(true); + compressionOptions.setFormat(nvtt::Format_RGBA); + compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); + compressionOptions.setPixelFormat(8, 8, 0, 0); } else { qCWarning(imagelogging) << "Unknown mip format"; Q_UNREACHABLE(); return; } - - nvtt::InputOptions inputOptions; - inputOptions.setTextureLayout(textureType, width, height); - inputOptions.setMipmapData(data, width, height); - - inputOptions.setFormat(inputFormat); - inputOptions.setGamma(inputGamma, outputGamma); - inputOptions.setAlphaMode(alphaMode); - inputOptions.setWrapMode(wrapMode); - inputOptions.setRoundMode(roundMode); - - inputOptions.setMipmapGeneration(true); - inputOptions.setMipmapFilter(nvtt::MipmapFilter_Box); - nvtt::OutputOptions outputOptions; outputOptions.setOutputHeader(false); MyOutputHandler outputHandler(texture, face); @@ -548,8 +552,8 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; #else - gpu::Element formatMip = gpu::Element::COLOR_RGBA_32; - gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; + gpu::Element formatMip = gpu::Element::VEC2_XY; + gpu::Element formatGPU = gpu::Element::VEC2_XY; #endif theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); From 542923839dc75a4e55297d7a4df9eddf03432e4b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 May 2017 15:54:07 -0700 Subject: [PATCH 076/128] Change name to VEC2NU8_XY --- libraries/gpu/src/gpu/Format.cpp | 2 +- libraries/gpu/src/gpu/Format.h | 2 +- libraries/gpu/src/gpu/Texture_ktx.cpp | 2 +- libraries/image/src/image/Image.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index a9c7d249a8..43bcd35b88 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -25,7 +25,7 @@ const Element Element::COLOR_COMPRESSED_SRGBA_MASK{ VEC4, NUINT8, COMPRESSED_BC1 const Element Element::COLOR_COMPRESSED_SRGBA{ VEC4, NUINT8, COMPRESSED_BC3_SRGBA }; const Element Element::COLOR_COMPRESSED_XY{ VEC4, NUINT8, COMPRESSED_BC5_XY }; -const Element Element::VEC2_XY{ VEC2, NUINT8, XY }; +const Element Element::VEC2NU8_XY{ VEC2, NUINT8, XY }; const Element Element::COLOR_R11G11B10{ SCALAR, FLOAT, R11G11B10 }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 9fadcecb22..8b7bbdcbed 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -234,7 +234,7 @@ public: static const Element COLOR_COMPRESSED_SRGBA_MASK; static const Element COLOR_COMPRESSED_SRGBA; static const Element COLOR_COMPRESSED_XY; - static const Element VEC2_XY; + static const Element VEC2NU8_XY; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; static const Element VEC2F_XY; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 4952e7a83b..7c61540043 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -423,7 +423,7 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RGBA, ktx::GLInternalFormat_Uncompressed::SRGB8_ALPHA8, ktx::GLBaseInternalFormat::RGBA); } else if (texelFormat == Format::COLOR_R_8 && mipFormat == Format::COLOR_R_8) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RED, ktx::GLInternalFormat_Uncompressed::R8, ktx::GLBaseInternalFormat::RED); - } else if (texelFormat == Format::VEC2_XY && mipFormat == Format::VEC2_XY) { + } else if (texelFormat == Format::VEC2NU8_XY && mipFormat == Format::VEC2NU8_XY) { header.setUncompressed(ktx::GLType::UNSIGNED_BYTE, 1, ktx::GLFormat::RG, ktx::GLInternalFormat_Uncompressed::RG8, ktx::GLBaseInternalFormat::RG); } else if (texelFormat == Format::COLOR_COMPRESSED_SRGB && mipFormat == Format::COLOR_COMPRESSED_SRGB) { header.setCompressed(ktx::GLInternalFormat_Compressed::COMPRESSED_SRGB_S3TC_DXT1_EXT, ktx::GLBaseInternalFormat::RGB); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 63c7d0d8ab..825b07003b 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -359,7 +359,7 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { compressionOptions.setFormat(nvtt::Format_RGB); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); compressionOptions.setPixelFormat(8, 0, 0, 0); - } else if (mipFormat == gpu::Element::VEC2_XY) { + } else if (mipFormat == gpu::Element::VEC2NU8_XY) { inputOptions.setNormalMap(true); compressionOptions.setFormat(nvtt::Format_RGBA); compressionOptions.setPixelType(nvtt::PixelType_UnsignedNorm); @@ -552,8 +552,8 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; #else - gpu::Element formatMip = gpu::Element::VEC2_XY; - gpu::Element formatGPU = gpu::Element::VEC2_XY; + gpu::Element formatMip = gpu::Element::VEC2NU8_XY; + gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; #endif theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); From b08d23924a58a9134bd82a05d820389324652036 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 11:00:13 -0700 Subject: [PATCH 077/128] Keep hover status of both images in sync --- scripts/system/html/js/SnapshotReview.js | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 4aede9e3bc..27062faea2 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -143,11 +143,11 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled '
' + ''; shareBar.innerHTML = shareBarInnerHTML; @@ -164,11 +164,11 @@ function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, c document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); if (divID === "p0") { selectImageToShare(divID, true); - if (canBlast) { - shareButtonHovered('blast', divID); - } else { - shareButtonHovered('hifi', divID); - } + } + if (canBlast) { + shareButtonHovered('blast', divID, false); + } else { + shareButtonHovered('hifi', divID, false); } } function shareForUrl(selectedID) { @@ -382,7 +382,7 @@ function shareWithEveryone(selectedID, isGif) { showUploadingMessage(selectedID, 'hifi'); } } -function shareButtonHovered(destination, selectedID) { +function shareButtonHovered(destination, selectedID, shouldAlsoModifyOther) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } @@ -391,7 +391,6 @@ function shareButtonHovered(destination, selectedID) { itr; if (!shareBarHelp.classList.contains("uploading")) { - for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; } @@ -441,6 +440,14 @@ function shareButtonHovered(destination, selectedID) { break; } } + + if (shouldAlsoModifyOther && imageCount > 1) { + if (selectedID === "p0" && !document.getElementById("p1").classList.contains("processingGif")) { + shareButtonHovered(destination, "p1", false); + } else if (selectedID === "p1") { + shareButtonHovered(destination, "p0", false); + } + } } function shareButtonClicked(destination, selectedID) { if (selectedID.id) { @@ -525,6 +532,7 @@ window.onload = function () { message.image_data.forEach(function (element, idx) { addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); }); + document.getElementById("p1").classList.add("processingGif"); } else { var gifPath = message.image_data[0].localPath, p1img = document.getElementById('p1img'); @@ -534,6 +542,7 @@ window.onload = function () { if (messageOptions.canShare) { shareForUrl("p1"); appendShareBar("p1", true, false, false, true); + document.getElementById("p1").classList.remove("processingGif"); } } } else { From c8a9b02c7a1fe873ab21c6190231793147d8440a Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 20:26:02 +0200 Subject: [PATCH 078/128] Block only secondary button --- interface/src/ui/overlays/Web3DOverlay.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 38bf5b293b..ecc63801fc 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -421,8 +421,10 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } - //do not send non primary button events to tablet - if (event.getButton() != PointerEvent::PrimaryButton) { + //do not send secondary button events to tablet + if (event.getButton() == PointerEvent::SecondaryButton || + //do not block composed events + event.getButtons() == PointerEvent::SecondaryButton) { return; } From fb995bebf17a3a92c6eacf70bf07b7cad2e6609c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 4 May 2017 12:04:21 -0700 Subject: [PATCH 079/128] Prevent crash on switching to 3D display twice --- libraries/ui/src/VrMenu.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 73eb32ce17..878514dd41 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -37,15 +37,17 @@ public: qmlObject->setObjectName(uuid.toString()); // Make sure we can find it again in the future updateQmlItemFromAction(); - auto connection = QObject::connect(action, &QAction::changed, [=] { + _changedConnection = QObject::connect(action, &QAction::changed, [=] { updateQmlItemFromAction(); }); - QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { - QObject::disconnect(connection); + _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { + QObject::disconnect(_changedConnection); }); } ~MenuUserData() { + QObject::disconnect(_changedConnection); + QObject::disconnect(_shutdownConnection); _action->setUserData(USER_DATA_ID, nullptr); _qml->setUserData(USER_DATA_ID, nullptr); } @@ -104,6 +106,8 @@ public: private: MenuUserData(const MenuUserData&); + QMetaObject::Connection _shutdownConnection; + QMetaObject::Connection _changedConnection; QAction* _action { nullptr }; QObject* _qml { nullptr }; }; From f728ae795595550182cc45554f85f020a2a5caa6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 May 2017 12:38:27 -0700 Subject: [PATCH 080/128] Remove unecessary normalize --- libraries/render-utils/src/MaterialTextures.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index a530f4faf2..e694935361 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -66,7 +66,7 @@ vec3 fetchNormalMap(vec2 uv) { // unpack normal, swizzle to get into hifi tangent space with Y axis pointing out vec2 t = 2.0 * (texture(normalMap, uv).rg - vec2(0.5, 0.5)); vec2 t2 = t*t; - return normalize(vec3(t.x, sqrt(1 - t2.x - t2.y), t.y)); + return vec3(t.x, sqrt(1 - t2.x - t2.y), t.y); } <@endif@> From e87f8dfe45fd76e8d16e30e3ea5ec0cf5430caec Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 May 2017 13:31:24 -0700 Subject: [PATCH 081/128] fix issue with not seeing your own attachments --- interface/src/avatar/Avatar.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3bd4c663d2..49ab862acf 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -525,13 +525,11 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - if (_skeletonModel->addToScene(scene, transaction)) { - _renderItemID = scene->allocateID(); - transaction.resetItem(_renderItemID, avatarPayloadPointer); - - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->addToScene(scene, transaction); - } + _renderItemID = scene->allocateID(); + transaction.resetItem(_renderItemID, avatarPayloadPointer); + _skeletonModel->addToScene(scene, transaction); + for (auto& attachmentModel : _attachmentModels) { + attachmentModel->addToScene(scene, transaction); } } From 87a685c4f9302ba15bc9b02237eacb2d2982fe68 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 May 2017 13:41:20 -0700 Subject: [PATCH 082/128] Fix OSX warning in TextureCache --- .../model-networking/src/model-networking/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 06ff891cb9..9653cde7d8 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -705,7 +705,7 @@ void NetworkTexture::refresh() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); } Resource::refresh(); From fe36e6a793c2a7dcffd0777d97466d5ab9ea058b Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:45:12 -0400 Subject: [PATCH 083/128] fix JSConsole script message handlers and about: filename --- interface/src/ui/JSConsole.cpp | 13 +++++++------ interface/src/ui/JSConsole.h | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 7700874d9a..2d21d1d7ec 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -33,6 +33,8 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; const QString GUTTER_ERROR = "X"; +const QString JSConsole::_consoleFileName { "about:console" }; + JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : QWidget(parent), _ui(new Ui::Console), @@ -84,8 +86,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine - _ownScriptEngine = scriptEngine == NULL; - _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(QString(), false) : scriptEngine; + _ownScriptEngine = (scriptEngine == NULL); + _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); @@ -107,11 +109,10 @@ void JSConsole::executeCommand(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue result; - static const QString filename = "JSConcole"; QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command), - Q_ARG(const QString&, filename)); + Q_ARG(const QString&, _consoleFileName)); return result; } @@ -134,12 +135,12 @@ void JSConsole::commandFinished() { resetCurrentCommandHistory(); } -void JSConsole::handleError(const QString& scriptName, const QString& message) { +void JSConsole::handleError(const QString& message, const QString& scriptName) { Q_UNUSED(scriptName); appendMessage(GUTTER_ERROR, "" + message.toHtmlEscaped() + ""); } -void JSConsole::handlePrint(const QString& scriptName, const QString& message) { +void JSConsole::handlePrint(const QString& message, const QString& scriptName) { Q_UNUSED(scriptName); appendMessage("", message); } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index d5f5aff301..938b670e78 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -47,8 +47,8 @@ protected: protected slots: void scrollToBottom(); void resizeTextInput(); - void handlePrint(const QString& scriptName, const QString& message); - void handleError(const QString& scriptName, const QString& message); + void handlePrint(const QString& message, const QString& scriptName); + void handleError(const QString& message, const QString& scriptName); void commandFinished(); private: @@ -66,6 +66,7 @@ private: bool _ownScriptEngine; QString _rootCommand; ScriptEngine* _scriptEngine; + static const QString _consoleFileName; }; From 8cea7f6a62a8d0a7c006b344f1e703aae873146a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:54:16 -0400 Subject: [PATCH 084/128] add test for print/Script.print behavior --- scripts/developer/tests/printTest.js | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/developer/tests/printTest.js diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js new file mode 100644 index 0000000000..b94ff0c54b --- /dev/null +++ b/scripts/developer/tests/printTest.js @@ -0,0 +1,34 @@ +/* eslint-env jasmine */ + +// this test generates sample print, Script.print, etc. output + +main(); + +function main() { + // to match with historical behavior, Script.print(message) output only triggers + // the printedMessage signal (and therefore doesn't show up in the application log) + Script.print('[Script.print] hello world'); + + // the rest of these should show up in both the application log and signaled print handlers + print('[print]', 'hello', 'world'); + + // note: these trigger the equivalent of an emit + Script.printedMessage('[Script.printedMessage] hello world', '{filename}'); + Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); + + { + // FIXME: while not directly related to Script.print, these currently don't + // show up at all in the "HMD-friendly script log" or "Console..." + + Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); + Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); + + Vec3.print('[Vec3.print]', Vec3.HALF); + + var q = Quat.fromPitchYawRollDegrees(45, 45, 45); + Quat.print('[Quat.print]', q); + + var m = Mat4.createFromRotAndTrans(q, Vec3.HALF); + Mat4.print('[Mat4.print (row major)]', m); + } +} From ba5a371da1aeb454f9178091fdd8a25f813d3418 Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 4 May 2017 22:54:41 +0200 Subject: [PATCH 085/128] Decrease CPU usage in tabletUI periodical updates --- interface/src/Application.cpp | 3 +- interface/src/ui/AvatarInputs.h | 2 +- scripts/system/tablet-ui/tabletUI.js | 70 ++++++++++++---------------- 3 files changed, 34 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64b18ec522..4a7d552ad2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5435,7 +5435,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } - scriptEngine->registerGlobalObject("Overlays", &_overlays); scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this)); // hook our avatar and avatar hash map object into this script engine @@ -5534,6 +5533,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); + scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); + qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 34b2cbca8b..bc9955a24f 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -34,7 +34,7 @@ class AvatarInputs : public QQuickItem { public: static AvatarInputs* getInstance(); - float loudnessToAudioLevel(float loudness); + Q_INVOKABLE float loudnessToAudioLevel(float loudness); AvatarInputs(QQuickItem* parent = nullptr); void update(); bool showAudioTools() const { return _showAudioTools; } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index dbfd3e632e..3d8e1811ba 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,7 +13,7 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu */ + MyAvatar, Menu, AvatarInputs */ (function() { // BEGIN LOCAL_SCOPE var tabletRezzed = false; @@ -25,6 +25,9 @@ var debugTablet = false; var tabletScalePercentage = 100.0; UIWebTablet = null; + var MSECS_PER_SEC = 1000.0; + var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; + var gTablet = null; Script.include("../libraries/WebTablet.js"); @@ -49,7 +52,7 @@ } function getTabletScalePercentageFromSettings() { - var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; + var toolbarMode = gTablet.toolbarMode; var tabletScalePercentage = DEFAULT_TABLET_SCALE; if (!toolbarMode) { if (HMD.active) { @@ -77,6 +80,9 @@ if (debugTablet) { print("TABLET rezzing"); } + if (gTablet === null) { + gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + } tabletScalePercentage = getTabletScalePercentageFromSettings(); UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", @@ -92,7 +98,7 @@ } function showTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = true; + gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -114,7 +120,7 @@ } function hideTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; + gTablet.tabletShown = false; if (!UIWebTablet) { return; } @@ -130,7 +136,7 @@ } function closeTabletUI() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false; + gTablet.tabletShown = false; if (UIWebTablet) { if (UIWebTablet.onClose) { UIWebTablet.onClose(); @@ -149,17 +155,21 @@ print("TABLET closeTabletUI, UIWebTablet is null"); } tabletRezzed = false; + gTablet = null } function updateShowTablet() { - var MSECS_PER_SEC = 1000.0; var now = Date.now(); + if (gTablet === null) { + gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + } + // close the WebTablet if it we go into toolbar mode. - var tabletShown = Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown; - var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode; - var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape; + var tabletShown = gTablet.tabletShown; + var toolbarMode = gTablet.toolbarMode; + var landscape = gTablet.landscape; if (tabletShown && toolbarMode) { closeTabletUI(); @@ -167,18 +177,20 @@ return; } + //TODO: move to tablet qml? if (tabletShown) { - var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); var currentMicLevel = getMicLevel(); - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.updateMicEnabled(currentMicEnabled); - tablet.updateAudioBar(currentMicLevel); + gTablet.updateMicEnabled(currentMicEnabled); + gTablet.updateAudioBar(currentMicLevel); } - updateTabletWidthFromSettings(); - if (UIWebTablet) { - UIWebTablet.setLandscape(landscape); + if (validCheckTime - now > MSECS_PER_SEC/4) { + //each 250ms should be just fine + updateTabletWidthFromSettings(); + if (UIWebTablet) { + UIWebTablet.setLandscape(landscape); + } } if (validCheckTime - now > MSECS_PER_SEC) { @@ -228,7 +240,7 @@ } if (channel === "home") { if (UIWebTablet) { - Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape = false; + gTablet.landscape = false; } } } @@ -239,30 +251,10 @@ Script.setInterval(updateShowTablet, 100); - // Initialise variables used to calculate audio level - var accumulatedLevel = 0.0; - // Note: Might have to tweak the following two based on the rate we're getting the data - var AVERAGING_RATIO = 0.05; - // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js function getMicLevel() { - var LOUDNESS_FLOOR = 11.0; - var LOUDNESS_SCALE = 2.8 / 5.0; - var LOG2 = Math.log(2.0); - var micLevel = 0.0; - accumulatedLevel = AVERAGING_RATIO * accumulatedLevel + (1 - AVERAGING_RATIO) * (MyAvatar.audioLoudness); - // Convert to log base 2 - var logLevel = Math.log(accumulatedLevel + 1) / LOG2; - - if (logLevel <= LOUDNESS_FLOOR) { - micLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; - } else { - micLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; - } - if (micLevel > 1.0) { - micLevel = 1.0; - } - return micLevel; + //reuse already existing C++ code + return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness) } Script.scriptEnding.connect(function () { From e765d8858ce874dbce989286ea54c635138ac8f4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:57:41 -0400 Subject: [PATCH 086/128] update script engine to not treat about:* as a local filename; resolve debugPrint FIXME --- libraries/script-engine/src/ScriptEngine.cpp | 13 +++++++++---- libraries/script-engine/src/ScriptEngine.h | 1 + libraries/script-engine/src/ScriptEngines.cpp | 5 +++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c904062507..8bbb9a3e2c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -105,11 +105,11 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) { } message += context->argument(i).toString(); } - qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline + qCDebug(scriptengineScript).noquote() << message; // noquote() so that \n is treated as newline - // FIXME - this approach neeeds revisiting. print() comes here, which ends up calling Script.print? - engine->globalObject().property("Script").property("print") - .call(engine->nullValue(), QScriptValueList({ message })); + if (ScriptEngine *scriptEngine = qobject_cast(engine)) { + scriptEngine->print(message); + } return QScriptValue(); } @@ -472,6 +472,11 @@ void ScriptEngine::scriptInfoMessage(const QString& message) { emit infoMessage(message, getFilename()); } +void ScriptEngine::scriptPrintedMessage(const QString& message) { + qCDebug(scriptengine) << message; + emit printedMessage(message, getFilename()); +} + // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5ea8d052e9..6188f24d71 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -221,6 +221,7 @@ public: void scriptErrorMessage(const QString& message); void scriptWarningMessage(const QString& message); void scriptInfoMessage(const QString& message); + void scriptPrintedMessage(const QString& message); int getNumRunningEntityScripts() const; bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 88b0e0b7b5..2076657288 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -453,7 +453,8 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL (scriptFilename.scheme() != "http" && scriptFilename.scheme() != "https" && scriptFilename.scheme() != "atp" && - scriptFilename.scheme() != "file")) { + scriptFilename.scheme() != "file" && + scriptFilename.scheme() != "about")) { // deal with a "url" like c:/something scriptUrl = normalizeScriptURL(QUrl::fromLocalFile(scriptFilename.toString())); } else { @@ -472,7 +473,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL }, Qt::QueuedConnection); - if (scriptFilename.isEmpty()) { + if (scriptFilename.isEmpty() || !scriptUrl.isValid()) { launchScriptEngine(scriptEngine); } else { // connect to the appropriate signals of this script engine From 2d0bbf70ae06f9d31a33050da7eb0e4ea5a176bb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Apr 2017 14:42:15 -0700 Subject: [PATCH 087/128] Migrate core avatar rendering functionality to library --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 32 +++- interface/src/Application.h | 1 + interface/src/FancyCamera.cpp | 3 + interface/src/FancyCamera.h | 4 +- interface/src/Util.cpp | 5 - interface/src/Util.h | 3 - interface/src/avatar/AvatarManager.cpp | 4 +- interface/src/avatar/AvatarManager.h | 6 +- interface/src/avatar/MyAvatar.cpp | 40 +++-- interface/src/avatar/MyAvatar.h | 21 ++- interface/src/avatar/MyHead.cpp | 76 +++++++++ interface/src/avatar/MyHead.h | 30 ++++ interface/src/avatar/MySkeletonModel.cpp | 158 ++++++++++++++++++ interface/src/avatar/MySkeletonModel.h | 26 +++ interface/src/devices/DdeFaceTracker.cpp | 15 +- interface/src/devices/DdeFaceTracker.h | 2 + interface/src/devices/Leapmotion.cpp | 6 +- interface/src/devices/Leapmotion.h | 3 - libraries/avatars-renderer/CMakeLists.txt | 2 +- .../src/avatars-renderer}/Avatar.cpp | 63 +++---- .../src/avatars-renderer}/Avatar.h | 23 +-- .../avatars-renderer}/AvatarMotionState.cpp | 5 +- .../src/avatars-renderer}/AvatarMotionState.h | 3 +- .../src/avatars-renderer}/Head.cpp | 112 ++----------- .../src/avatars-renderer}/Head.h | 63 +++---- .../Logging.cpp} | 2 +- .../Logging.h} | 0 .../src/avatars-renderer/OtherAvatar.cpp | 16 ++ .../src/avatars-renderer/OtherAvatar.h | 20 +++ .../src/avatars-renderer}/ScriptAvatar.cpp | 0 .../src/avatars-renderer}/ScriptAvatar.h | 0 .../src/avatars-renderer}/SkeletonModel.cpp | 148 ++-------------- .../src/avatars-renderer}/SkeletonModel.h | 2 +- libraries/avatars/src/AvatarData.h | 4 +- libraries/avatars/src/HeadData.cpp | 5 - .../src}/CauterizedMeshPartPayload.cpp | 4 +- .../src}/CauterizedMeshPartPayload.h | 7 +- .../render-utils/src}/CauterizedModel.cpp | 7 +- .../render-utils/src}/CauterizedModel.h | 5 +- .../render-utils/src}/SoftAttachmentModel.cpp | 4 - .../render-utils/src}/SoftAttachmentModel.h | 3 - libraries/shared/src/GLMHelpers.cpp | 6 + libraries/shared/src/GLMHelpers.h | 3 + .../shared/src/shared}/Camera.cpp | 35 ---- .../shared/src/shared}/Camera.h | 10 +- tests/render-perf/src/Camera.hpp | 4 +- tests/render-perf/src/main.cpp | 4 +- 48 files changed, 530 insertions(+), 467 deletions(-) create mode 100644 interface/src/avatar/MyHead.cpp create mode 100644 interface/src/avatar/MyHead.h create mode 100644 interface/src/avatar/MySkeletonModel.cpp create mode 100644 interface/src/avatar/MySkeletonModel.h rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Avatar.cpp (96%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Avatar.h (96%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/AvatarMotionState.cpp (99%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/AvatarMotionState.h (98%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Head.cpp (77%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Head.h (79%) rename libraries/avatars-renderer/src/{AvatarsRendererLogging.cpp => avatars-renderer/Logging.cpp} (89%) rename libraries/avatars-renderer/src/{AvatarsRendererLogging.h => avatars-renderer/Logging.h} (100%) create mode 100644 libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp create mode 100644 libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/ScriptAvatar.cpp (100%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/ScriptAvatar.h (100%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/SkeletonModel.cpp (66%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/SkeletonModel.h (99%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedMeshPartPayload.cpp (94%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedMeshPartPayload.h (87%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedModel.cpp (98%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedModel.h (95%) rename {interface/src/avatar => libraries/render-utils/src}/SoftAttachmentModel.cpp (97%) rename {interface/src/avatar => libraries/render-utils/src}/SoftAttachmentModel.h (95%) rename {interface/src => libraries/shared/src/shared}/Camera.cpp (79%) rename {interface/src => libraries/shared/src/shared}/Camera.h (94%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d7e4b1ae7c..71341f3f11 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -191,7 +191,7 @@ endif() # link required hifi libraries link_hifi_libraries( shared octree ktx gpu gl gpu-gl procedural model render - recording fbx networking model-networking entities avatars + recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui auto-updater controllers plugins image trackers diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e3df955ba..1585d41115 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -129,12 +129,12 @@ #include #include #include - +#include #include "AudioClient.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" -#include "avatar/ScriptAvatar.h" +#include "avatar/MyHead.h" #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" #include "devices/Leapmotion.h" @@ -1586,6 +1586,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); + + connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); } void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { @@ -2191,7 +2193,7 @@ void Application::paintGL() { _myCamera.setOrientation(glm::quat_cast(camMat)); } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setOrientation(myAvatar->getHead()->getCameraOrientation()); + _myCamera.setOrientation(myAvatar->getMyHead()->getCameraOrientation()); } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { @@ -4082,6 +4084,30 @@ void Application::cycleCamera() { cameraMenuChanged(); // handle the menu change } +void Application::cameraModeChanged() { + switch (_myCamera.getMode()) { + case CAMERA_MODE_FIRST_PERSON: + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + break; + case CAMERA_MODE_THIRD_PERSON: + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); + break; + case CAMERA_MODE_MIRROR: + Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true); + break; + case CAMERA_MODE_INDEPENDENT: + Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); + break; + case CAMERA_MODE_ENTITY: + Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); + break; + default: + break; + } + cameraMenuChanged(); +} + + void Application::cameraMenuChanged() { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 041f1f8930..cbdfe8aeb1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -372,6 +372,7 @@ public slots: static void showHelp(); void cycleCamera(); + void cameraModeChanged(); void cameraMenuChanged(); void toggleOverlays(); void setOverlaysVisible(bool visible); diff --git a/interface/src/FancyCamera.cpp b/interface/src/FancyCamera.cpp index 7bb64a4a8e..298cab9948 100644 --- a/interface/src/FancyCamera.cpp +++ b/interface/src/FancyCamera.cpp @@ -12,6 +12,9 @@ #include "Application.h" +PickRay FancyCamera::computePickRay(float x, float y) const { + return qApp->computePickRay(x, y); +} QUuid FancyCamera::getCameraEntity() const { if (_cameraEntity != nullptr) { diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 66f7a07dbd..3552dc6ca8 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -11,7 +11,7 @@ #ifndef hifi_FancyCamera_h #define hifi_FancyCamera_h -#include "Camera.h" +#include #include @@ -30,6 +30,8 @@ public: FancyCamera() : Camera() {} EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } + PickRay computePickRay(float x, float y) const override; + public slots: QUuid getCameraEntity() const; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 7bf48d98d2..78a503bc71 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -142,11 +142,6 @@ void renderWorldBox(gpu::Batch& batch) { geometryCache->renderSolidSphereInstance(batch, GREY); } -// Return a random vector of average length 1 -const glm::vec3 randVector() { - return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; -} - // Do some basic timing tests and report the results void runTimingTests() { // How long does it take to make a call to get the time? diff --git a/interface/src/Util.h b/interface/src/Util.h index 60e38ae0ec..b1b4c78bcb 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -17,9 +17,6 @@ #include -float randFloat(); -const glm::vec3 randVector(); - void renderWorldBox(gpu::Batch& batch); void runTimingTests(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 04ab1531ba..1306ce03ea 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -32,9 +32,9 @@ #include #include #include +#include #include "Application.h" -#include "Avatar.h" #include "AvatarManager.h" #include "InterfaceLogging.h" #include "Menu.h" @@ -299,7 +299,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return std::make_shared(qApp->thread(), std::make_shared()); + return std::make_shared(qApp->thread(), std::make_shared()); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c67088a4be..9df1639853 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -21,13 +21,11 @@ #include #include #include +#include +#include -#include "Avatar.h" #include "MyAvatar.h" -#include "AvatarMotionState.h" -#include "ScriptAvatar.h" -class MyAvatar; class AudioInjector; class AvatarManager : public AvatarHashMap { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5e285f21ba..3f3ce7d9e9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "MyAvatar.h" + #include #include @@ -43,11 +45,12 @@ #include #include +#include "MyHead.h" +#include "MySkeletonModel.h" #include "Application.h" #include "AvatarManager.h" #include "AvatarActionHold.h" #include "Menu.h" -#include "MyAvatar.h" #include "Util.h" #include "InterfaceLogging.h" #include "DebugDraw.h" @@ -96,23 +99,12 @@ static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.91 MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig), - _wasPushing(false), - _isPushing(false), - _isBeingPushed(false), - _isBraking(false), - _isAway(false), - _boomLength(ZOOM_DEFAULT), _yawSpeed(YAW_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT), - _thrust(0.0f), - _actionMotorVelocity(0.0f), - _scriptedMotorVelocity(0.0f), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _characterController(this), - _lookAtTargetAvatar(), - _shouldRender(true), _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), @@ -129,6 +121,14 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()) { + + // give the pointer to our head to inherited _headData variable from AvatarData + _headData = new MyHead(this); + + _skeletonModel = std::make_shared(this, nullptr, rig); + connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); + + using namespace recording; _skeletonModel->flagAsCauterized(); @@ -536,7 +536,7 @@ void MyAvatar::simulate(float deltaTime) { } head->setPosition(headPosition); head->setScale(getUniformScale()); - head->simulate(deltaTime, true); + head->simulate(deltaTime); } // Record avatars movements. @@ -1450,12 +1450,12 @@ void MyAvatar::updateMotors() { glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_characterController.getState() == CharacterController::State::Hover) { - motorRotation = getHead()->getCameraOrientation(); + motorRotation = getMyHead()->getCameraOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift // so we decompose camera's rotation and store the twist part in motorRotation glm::quat liftRotation; - swingTwistDecomposition(getHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); + swingTwistDecomposition(getMyHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; @@ -1469,7 +1469,7 @@ void MyAvatar::updateMotors() { } if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) { - motorRotation = getHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); + motorRotation = getMyHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) { motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); } else { @@ -1814,7 +1814,7 @@ void MyAvatar::updateOrientation(float deltaTime) { if (getCharacterController()->getState() == CharacterController::State::Hover) { // This is the direction the user desires to fly in. - glm::vec3 desiredFacing = getHead()->getCameraOrientation() * Vectors::UNIT_Z; + glm::vec3 desiredFacing = getMyHead()->getCameraOrientation() * Vectors::UNIT_Z; desiredFacing.y = 0.0f; // This is our reference frame, it is captured when the user begins to move. @@ -1958,7 +1958,7 @@ void MyAvatar::updatePosition(float deltaTime) { if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) { _hoverReferenceCameraFacingIsCaptured = true; // transform the camera facing vector into sensor space. - _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z); + _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getMyHead()->getCameraOrientation() * Vectors::UNIT_Z); } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) { _hoverReferenceCameraFacingIsCaptured = false; } @@ -2804,3 +2804,7 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& }); } } + +const MyHead* MyAvatar::getMyHead() const { + return static_cast(getHead()); +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6a1e457a97..7c510f0556 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -22,14 +22,15 @@ #include #include +#include -#include "Avatar.h" #include "AtRestDetector.h" #include "MyCharacterController.h" #include class AvatarActionHold; class ModelItemID; +class MyHead; enum eyeContactTarget { LEFT_EYE, @@ -149,6 +150,7 @@ public: explicit MyAvatar(QThread* thread, RigPointer rig); ~MyAvatar(); + void instantiableAvatar() override {}; void registerMetaTypes(QScriptEngine* engine); virtual void simulateAttachments(float deltaTime) override; @@ -353,6 +355,7 @@ public: eyeContactTarget getEyeContactTarget(); + const MyHead* getMyHead() const; Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } @@ -589,17 +592,17 @@ private: std::array _driveKeys; std::bitset _disabledDriveKeys; - bool _wasPushing; - bool _isPushing; - bool _isBeingPushed; - bool _isBraking; - bool _isAway; + bool _wasPushing { false }; + bool _isPushing { false }; + bool _isBeingPushed { false }; + bool _isBraking { false }; + bool _isAway { false }; - float _boomLength; + float _boomLength { ZOOM_DEFAULT }; float _yawSpeed; // degrees/sec float _pitchSpeed; // degrees/sec - glm::vec3 _thrust; // impulse accumulator for outside sources + glm::vec3 _thrust { 0.0f }; // impulse accumulator for outside sources glm::vec3 _actionMotorVelocity; // target local-frame velocity of avatar (default controller actions) glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script) @@ -615,7 +618,7 @@ private: AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; - bool _shouldRender; + bool _shouldRender { true }; float _oculusYawOffset; eyeContactTarget _eyeContactTarget; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp new file mode 100644 index 0000000000..c41fff3bb5 --- /dev/null +++ b/interface/src/avatar/MyHead.cpp @@ -0,0 +1,76 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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 "MyHead.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "devices/DdeFaceTracker.h" +#include "Application.h" +#include "MyAvatar.h" + +using namespace std; + +MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) { +} + +glm::quat MyHead::getCameraOrientation() const { + // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so + // you may wonder why this code is here. This method will be called while in Oculus mode to determine how + // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not + // always the same. + if (qApp->isHMDMode()) { + MyAvatar* myAvatar = static_cast(_owningAvatar); + return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation(); + } else { + Avatar* owningAvatar = static_cast(_owningAvatar); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); + } +} + +void MyHead::simulate(float deltaTime) { + auto player = DependencyManager::get(); + // Only use face trackers when not playing back a recording. + if (!player->isPlaying()) { + FaceTracker* faceTracker = qApp->getActiveFaceTracker(); + _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); + if (_isFaceTrackerConnected) { + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + + if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { + + if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { + calculateMouthShapes(deltaTime); + + const int JAW_OPEN_BLENDSHAPE = 21; + const int MMMM_BLENDSHAPE = 34; + const int FUNNEL_BLENDSHAPE = 40; + const int SMILE_LEFT_BLENDSHAPE = 28; + const int SMILE_RIGHT_BLENDSHAPE = 29; + _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; + _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; + _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; + } + applyEyelidOffset(getFinalOrientationInWorldFrame()); + } + } + auto eyeTracker = DependencyManager::get(); + _isEyeTrackerConnected = eyeTracker->isTracking(); + } + Parent::simulate(deltaTime); +} \ No newline at end of file diff --git a/interface/src/avatar/MyHead.h b/interface/src/avatar/MyHead.h new file mode 100644 index 0000000000..097415153c --- /dev/null +++ b/interface/src/avatar/MyHead.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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_MyHead_h +#define hifi_MyHead_h + +#include + +class MyAvatar; +class MyHead : public Head { + using Parent = Head; +public: + explicit MyHead(MyAvatar* owningAvatar); + + /// \return orientationBody * orientationBasePitch + glm::quat getCameraOrientation() const; + void simulate(float deltaTime) override; + +private: + // disallow copies of the Head, copy of owning Avatar is disallowed too + MyHead(const Head&); + MyHead& operator= (const MyHead&); +}; + +#endif // hifi_MyHead_h diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp new file mode 100644 index 0000000000..639c9f003f --- /dev/null +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -0,0 +1,158 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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 "MySkeletonModel.h" + +#include + +#include "Application.h" +#include "InterfaceLogging.h" + +MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel(owningAvatar, parent, rig) { +} + +Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { + switch (state) { + default: + case CharacterController::State::Ground: + return Rig::CharacterControllerState::Ground; + case CharacterController::State::Takeoff: + return Rig::CharacterControllerState::Takeoff; + case CharacterController::State::InAir: + return Rig::CharacterControllerState::InAir; + case CharacterController::State::Hover: + return Rig::CharacterControllerState::Hover; + }; +} + +// Called within Model::simulate call, below. +void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + const FBXGeometry& geometry = getFBXGeometry(); + + Head* head = _owningAvatar->getHead(); + + // make sure lookAt is not too close to face (avoid crosseyes) + glm::vec3 lookAt = _owningAvatar->isMyAvatar() ? head->getLookAtPosition() : head->getCorrectedLookAtPosition(); + MyAvatar* myAvatar = static_cast(_owningAvatar); + + Rig::HeadParameters headParams; + + // input action is the highest priority source for head orientation. + auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); + if (avatarHeadPose.isValid()) { + glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); + headParams.rigHeadPosition = extractTranslation(rigHeadMat); + headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat); + headParams.headEnabled = true; + } else { + if (qApp->isHMDMode()) { + // get HMD position from sensor space into world space, and back into rig space + glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); + glm::mat4 worldToRig = glm::inverse(rigToWorld); + glm::mat4 rigHMDMat = worldToRig * worldHMDMat; + _rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); + headParams.headEnabled = true; + } else { + // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. + // preMult 180 is necessary to convert from avatar to rig coordinates. + // postMult 180 is necessary to convert head from -z forward to z forward. + headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; + headParams.headEnabled = false; + } + } + + auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); + if (avatarHipsPose.isValid()) { + glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); + headParams.hipsMatrix = rigHipsMat; + headParams.hipsEnabled = true; + } else { + headParams.hipsEnabled = false; + } + + auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); + if (avatarSpine2Pose.isValid()) { + glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); + headParams.spine2Matrix = rigSpine2Mat; + headParams.spine2Enabled = true; + } else { + headParams.spine2Enabled = false; + } + + headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; + + _rig->updateFromHeadParameters(headParams, deltaTime); + + Rig::HandAndFeetParameters handAndFeetParams; + + auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); + if (leftPose.isValid()) { + handAndFeetParams.isLeftEnabled = true; + handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); + handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); + } else { + handAndFeetParams.isLeftEnabled = false; + } + + auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); + if (rightPose.isValid()) { + handAndFeetParams.isRightEnabled = true; + handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); + handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); + } else { + handAndFeetParams.isRightEnabled = false; + } + + auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); + if (leftFootPose.isValid()) { + handAndFeetParams.isLeftFootEnabled = true; + handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation(); + handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation(); + } else { + handAndFeetParams.isLeftFootEnabled = false; + } + + auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); + if (rightFootPose.isValid()) { + handAndFeetParams.isRightFootEnabled = true; + handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation(); + handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation(); + } else { + handAndFeetParams.isRightFootEnabled = false; + } + + handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); + handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); + handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); + + _rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); + + Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); + + auto velocity = myAvatar->getLocalVelocity(); + auto position = myAvatar->getLocalPosition(); + auto orientation = myAvatar->getLocalOrientation(); + _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); + + // evaluate AnimGraph animation and update jointStates. + Model::updateRig(deltaTime, parentTransform); + + Rig::EyeParameters eyeParams; + eyeParams.eyeLookAt = lookAt; + eyeParams.eyeSaccade = head->getSaccade(); + eyeParams.modelRotation = getRotation(); + eyeParams.modelTranslation = getTranslation(); + eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + + _rig->updateFromEyeParameters(eyeParams); + + Parent::updateRig(deltaTime, parentTransform); +} + diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h new file mode 100644 index 0000000000..84fccc825a --- /dev/null +++ b/interface/src/avatar/MySkeletonModel.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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_MySkeletonModel_h +#define hifi_MySkeletonModel_h + +#include + +/// A skeleton loaded from a model. +class MySkeletonModel : public SkeletonModel { + Q_OBJECT + +private: + using Parent = SkeletonModel; + +public: + MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); + void updateRig(float deltaTime, glm::mat4 parentTransform) override; +}; + +#endif // hifi_MySkeletonModel_h diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index fa7b2c173e..ed52083d77 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -9,20 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "DdeFaceTracker.h" + #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include +#include #include "Application.h" -#include "DdeFaceTracker.h" -#include "FaceshiftConstants.h" #include "InterfaceLogging.h" #include "Menu.h" diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index f125dfc3cf..dfb9c6d638 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -12,6 +12,8 @@ #ifndef hifi_DdeFaceTracker_h #define hifi_DdeFaceTracker_h +#include + #if defined(Q_OS_WIN) || defined(Q_OS_OSX) #define HAVE_DDE #endif diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp index cb99cf324d..c643042300 100644 --- a/interface/src/devices/Leapmotion.cpp +++ b/interface/src/devices/Leapmotion.cpp @@ -1,7 +1,4 @@ // -// Leapmotion.cpp -// interface/src/devices -// // Created by Sam Cake on 6/2/2014 // Copyright 2014 High Fidelity, Inc. // @@ -10,10 +7,11 @@ // #include "Leapmotion.h" -#include "Menu.h" #include +#include "Menu.h" + const int PALMROOT_NUM_JOINTS = 3; const int FINGER_NUM_JOINTS = 4; const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS; diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h index 6ecec8ccf9..a119821846 100644 --- a/interface/src/devices/Leapmotion.h +++ b/interface/src/devices/Leapmotion.h @@ -1,7 +1,4 @@ // -// Leapmotion.h -// interface/src/devices -// // Created by Sam Cake on 6/2/2014 // Copyright 2014 High Fidelity, Inc. // diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index bee2cb31d6..b13bc0a4a6 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME avatars-renderer) AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu model animation physics model-networking script-engine render render-utils) +link_hifi_libraries(shared gpu model animation physics model-networking script-engine render image render-utils) target_bullet() diff --git a/interface/src/avatar/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp similarity index 96% rename from interface/src/avatar/Avatar.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 3bd4c663d2..6408807670 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -27,16 +27,13 @@ #include #include #include +#include +#include -#include "AvatarMotionState.h" -#include "Camera.h" -#include "InterfaceLogging.h" -#include "SceneScriptingInterface.h" -#include "SoftAttachmentModel.h" +#include "Logging.h" using namespace std; -const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const int NUM_BODY_CONE_SIDES = 9; const float CHAT_MESSAGE_SCALE = 0.0015f; const float CHAT_MESSAGE_HEIGHT = 0.1f; @@ -71,6 +68,11 @@ namespace render { } } +bool showAvatars { true }; +void Avatar::setShowAvatars(bool render) { + showAvatars = render; +} + static bool showReceiveStats = false; void Avatar::setShowReceiveStats(bool receiveStats) { showReceiveStats = receiveStats; @@ -97,25 +99,6 @@ void Avatar::setShowNamesAboveHeads(bool show) { } Avatar::Avatar(QThread* thread, RigPointer rig) : - AvatarData(), - _skeletonOffset(0.0f), - _bodyYawDelta(0.0f), - _positionDeltaAccumulator(0.0f), - _lastVelocity(0.0f), - _acceleration(0.0f), - _lastAngularVelocity(0.0f), - _lastOrientation(), - _worldUpDirection(DEFAULT_UP_DIRECTION), - _moving(false), - _smoothPositionTime(SMOOTH_TIME_POSITION), - _smoothPositionTimer(std::numeric_limits::max()), - _smoothOrientationTime(SMOOTH_TIME_ORIENTATION), - _smoothOrientationTimer(std::numeric_limits::max()), - _smoothPositionInitial(), - _smoothPositionTarget(), - _smoothOrientationInitial(), - _smoothOrientationTarget(), - _initialized(false), _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread @@ -123,12 +106,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) : setScale(glm::vec3(1.0f)); // avatar scale is uniform - // give the pointer to our head to inherited _headData variable from AvatarData - _headData = static_cast(new Head(this)); - - _skeletonModel = std::make_shared(this, nullptr, rig); - connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - auto geometryCache = DependencyManager::get(); _nameRectGeometryID = geometryCache->allocateID(); _leftPointerGeometryID = geometryCache->allocateID(); @@ -283,7 +260,7 @@ void Avatar::updateAvatarEntities() { // and either add or update the entity. QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(data); if (!jsonProperties.isObject()) { - qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(data.toHex()); + qCDebug(avatars_renderer) << "got bad avatarEntity json" << QString(data.toHex()); continue; } @@ -306,7 +283,7 @@ void Avatar::updateAvatarEntities() { // NOTE: if this avatar entity is not attached to us, strip its entity script completely... auto attachedScript = properties.getScript(); if (!isMyAvatar() && !attachedScript.isEmpty()) { - qCDebug(interfaceapp) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript; + qCDebug(avatars_renderer) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript; QString noScript; properties.setScript(noScript); } @@ -410,7 +387,7 @@ void Avatar::simulate(float deltaTime, bool inView) { Head* head = getHead(); head->setPosition(headPosition); head->setScale(getUniformScale()); - head->simulate(deltaTime, false); + head->simulate(deltaTime); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); @@ -748,12 +725,12 @@ float Avatar::getBoundingRadius() const { #ifdef DEBUG void debugValue(const QString& str, const glm::vec3& value) { if (glm::any(glm::isnan(value)) || glm::any(glm::isinf(value))) { - qCWarning(interfaceapp) << "debugValue() " << str << value; + qCWarning(avatars_renderer) << "debugValue() " << str << value; } }; void debugValue(const QString& str, const float& value) { if (glm::isnan(value) || glm::isinf(value)) { - qCWarning(interfaceapp) << "debugValue() " << str << value; + qCWarning(avatars_renderer) << "debugValue() " << str << value; } }; #define DEBUG_VALUE(str, value) debugValue(str, value) @@ -783,7 +760,7 @@ glm::vec3 Avatar::getDisplayNamePosition() const { } if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) { - qCWarning(interfaceapp) << "Invalid display name position" << namePosition + qCWarning(avatars_renderer) << "Invalid display name position" << namePosition << ", setting is to (0.0f, 0.5f, 0.0f)"; namePosition = glm::vec3(0.0f, 0.5f, 0.0f); } @@ -1115,14 +1092,14 @@ void Avatar::setModelURLFinished(bool success) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { - qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts."; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } @@ -1438,7 +1415,7 @@ void Avatar::setParentID(const QUuid& parentID) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qCDebug(interfaceapp) << "Avatar::setParentID failed to reset avatar's location."; + qCDebug(avatars_renderer) << "Avatar::setParentID failed to reset avatar's location."; } if (initialParentID != parentID) { _parentChanged = usecTimestampNow(); @@ -1456,7 +1433,7 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qCDebug(interfaceapp) << "Avatar::setParentJointIndex failed to reset avatar's location."; + qCDebug(avatars_renderer) << "Avatar::setParentJointIndex failed to reset avatar's location."; } } } @@ -1488,7 +1465,7 @@ QList Avatar::getSkeleton() { void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) { if (scene) { auto nodelist = DependencyManager::get(); - if (DependencyManager::get()->shouldRenderAvatars() + if (showAvatars && !nodelist->isIgnoringNode(getSessionUUID()) && !nodelist->isRadiusIgnoringNode(getSessionUUID())) { render::Transaction transaction; @@ -1496,7 +1473,7 @@ void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer scene->enqueueTransaction(transaction); } } else { - qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown"; + qCWarning(avatars_renderer) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown"; } } diff --git a/interface/src/avatar/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h similarity index 96% rename from interface/src/avatar/Avatar.h rename to libraries/avatars-renderer/src/avatars-renderer/Avatar.h index f86bf35bd9..20704a08b2 100644 --- a/interface/src/avatar/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Head.h" @@ -68,6 +69,7 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: + static void setShowAvatars(bool render); static void setShowReceiveStats(bool receiveStats); static void setShowMyLookAtVectors(bool showMine); static void setShowOtherLookAtVectors(bool showOthers); @@ -77,6 +79,8 @@ public: explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); + virtual void instantiableAvatar() = 0; + typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; @@ -251,7 +255,6 @@ public: bool isInScene() const { return render::Item::isValidID(_renderItemID); } bool isMoving() const { return _moving; } - //void setMotionState(AvatarMotionState* motionState); void setPhysicsCallback(AvatarPhysicsCallback cb); void addPhysicsFlags(uint32_t flags); bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } @@ -268,7 +271,6 @@ public slots: void setModelURLFinished(bool success); protected: - const float SMOOTH_TIME_POSITION = 0.125f; const float SMOOTH_TIME_ORIENTATION = 0.075f; @@ -282,7 +284,7 @@ protected: std::vector> _attachmentsToRemove; std::vector> _attachmentsToDelete; - float _bodyYawDelta; // degrees/sec + float _bodyYawDelta { 0.0f }; // degrees/sec // These position histories and derivatives are in the world-frame. // The derivatives are the MEASURED results of all external and internal forces @@ -298,9 +300,8 @@ protected: glm::vec3 _angularAcceleration; glm::quat _lastOrientation; - glm::vec3 _worldUpDirection; - float _stringLength; - bool _moving; ///< set when position is changing + glm::vec3 _worldUpDirection { Vectors::UP }; + bool _moving { false }; ///< set when position is changing // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; @@ -336,10 +337,10 @@ protected: RateCounter<> _jointDataSimulationRate; // Smoothing data for blending from one position/orientation to another on remote agents. - float _smoothPositionTime; - float _smoothPositionTimer; - float _smoothOrientationTime; - float _smoothOrientationTimer; + float _smoothPositionTime { SMOOTH_TIME_POSITION }; + float _smoothPositionTimer { std::numeric_limits::max() }; + float _smoothOrientationTime { SMOOTH_TIME_ORIENTATION }; + float _smoothOrientationTimer { std::numeric_limits::max() }; glm::vec3 _smoothPositionInitial; glm::vec3 _smoothPositionTarget; glm::quat _smoothOrientationInitial; @@ -360,7 +361,7 @@ private: int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; int _nameRectGeometryID { 0 }; - bool _initialized; + bool _initialized { false }; bool _isLookAtTarget { false }; bool _isAnimatingScale { false }; diff --git a/interface/src/avatar/AvatarMotionState.cpp b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp similarity index 99% rename from interface/src/avatar/AvatarMotionState.cpp rename to libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp index ffa99e3990..0305634400 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp @@ -9,13 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AvatarMotionState.h" + #include #include #include -#include "Avatar.h" -#include "AvatarMotionState.h" -#include "BulletUtil.h" AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); diff --git a/interface/src/avatar/AvatarMotionState.h b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h similarity index 98% rename from interface/src/avatar/AvatarMotionState.h rename to libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h index a8dd7327ca..f8801ddf2f 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h @@ -15,8 +15,9 @@ #include #include +#include -class Avatar; +#include "Avatar.h" class AvatarMotionState : public ObjectMotionState { public: diff --git a/interface/src/avatar/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp similarity index 77% rename from interface/src/avatar/Head.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 16e5776d87..a90c9cd5f7 100644 --- a/interface/src/avatar/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -8,55 +8,28 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Head.h" + #include #include #include #include +#include +#include +#include #include - -#include "Application.h" -#include "Avatar.h" -#include "DependencyManager.h" -#include "GeometryUtil.h" -#include "Head.h" -#include "Menu.h" -#include "Util.h" -#include "devices/DdeFaceTracker.h" #include +#include "Avatar.h" + using namespace std; +static bool fixGaze { false }; +static bool disableEyelidAdjustment { false }; + Head::Head(Avatar* owningAvatar) : - HeadData((AvatarData*)owningAvatar), - _returnHeadToCenter(false), - _position(0.0f, 0.0f, 0.0f), - _rotation(0.0f, 0.0f, 0.0f), - _leftEyePosition(0.0f, 0.0f, 0.0f), - _rightEyePosition(0.0f, 0.0f, 0.0f), - _eyePosition(0.0f, 0.0f, 0.0f), - _scale(1.0f), - _lastLoudness(0.0f), - _longTermAverageLoudness(-1.0f), - _audioAttack(0.0f), - _audioJawOpen(0.0f), - _trailingAudioJawOpen(0.0f), - _mouth2(0.0f), - _mouth3(0.0f), - _mouth4(0.0f), - _mouthTime(0.0f), - _saccade(0.0f, 0.0f, 0.0f), - _saccadeTarget(0.0f, 0.0f, 0.0f), - _leftEyeBlinkVelocity(0.0f), - _rightEyeBlinkVelocity(0.0f), - _timeWithoutTalking(0.0f), - _deltaPitch(0.0f), - _deltaYaw(0.0f), - _deltaRoll(0.0f), - _isCameraMoving(false), - _isLookingAtMe(false), - _lookingAtMeStarted(0), - _wasLastLookingAtMe(0), + HeadData(owningAvatar), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { @@ -69,7 +42,7 @@ void Head::reset() { _baseYaw = _basePitch = _baseRoll = 0.0f; } -void Head::simulate(float deltaTime, bool isMine) { +void Head::simulate(float deltaTime) { const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for // grab the audio loudness from the owning avatar, if we have one @@ -90,43 +63,7 @@ void Head::simulate(float deltaTime, bool isMine) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } - if (isMine) { - auto player = DependencyManager::get(); - // Only use face trackers when not playing back a recording. - if (!player->isPlaying()) { - FaceTracker* faceTracker = qApp->getActiveFaceTracker(); - _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); - if (_isFaceTrackerConnected) { - _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - - if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { - - if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { - calculateMouthShapes(deltaTime); - - const int JAW_OPEN_BLENDSHAPE = 21; - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; - _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; - _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; - } - - applyEyelidOffset(getFinalOrientationInWorldFrame()); - } - } - - auto eyeTracker = DependencyManager::get(); - _isEyeTrackerConnected = eyeTracker->isTracking(); - } - } - if (!_isFaceTrackerConnected) { - if (!_isEyeTrackerConnected) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; @@ -222,7 +159,7 @@ void Head::simulate(float deltaTime, bool isMine) { } else { _saccade = glm::vec3(); } - if (Menu::getInstance()->isOptionChecked(MenuOption::FixGaze)) { // if debug menu turns off, use no saccade + if (fixGaze) { // if debug menu turns off, use no saccade _saccade = glm::vec3(); } @@ -277,7 +214,7 @@ void Head::calculateMouthShapes(float deltaTime) { void Head::applyEyelidOffset(glm::quat headOrientation) { // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. - if (Menu::getInstance()->isOptionChecked(MenuOption::DisableEyelidAdjustment)) { + if (disableEyelidAdjustment) { return; } @@ -350,7 +287,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() { } } -void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { +void Head::setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition) { if (!isLookingAtMe()) { _lookingAtMeStarted = usecTimestampNow(); } @@ -366,25 +303,6 @@ bool Head::isLookingAtMe() { return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; } -glm::quat Head::getCameraOrientation() const { - // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so - // you may wonder why this code is here. This method will be called while in Oculus mode to determine how - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're - // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not - // always the same. - if (qApp->isHMDMode()) { - MyAvatar* myAvatar = dynamic_cast(_owningAvatar); - if (myAvatar) { - return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation(); - } else { - return getOrientation(); - } - } else { - Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); - } -} - glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { glm::quat orientation = getOrientation(); glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; diff --git a/interface/src/avatar/Head.h b/libraries/avatars-renderer/src/avatars-renderer/Head.h similarity index 79% rename from interface/src/avatar/Head.h rename to libraries/avatars-renderer/src/avatars-renderer/Head.h index fd20e709f5..aea6a41528 100644 --- a/interface/src/avatar/Head.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.h @@ -11,16 +11,10 @@ #ifndef hifi_Head_h #define hifi_Head_h -#include -#include - +#include #include - #include -#include "world.h" - - const float EYE_EAR_GAP = 0.08f; class Avatar; @@ -31,9 +25,9 @@ public: void init(); void reset(); - void simulate(float deltaTime, bool isMine); + virtual void simulate(float deltaTime); void setScale(float scale); - void setPosition(glm::vec3 position) { _position = position; } + void setPosition(const glm::vec3& position) { _position = position; } void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } @@ -43,17 +37,14 @@ public: /// \return orientationBody * (orientationBase+Delta) glm::quat getFinalOrientationInWorldFrame() const; - /// \return orientationBody * orientationBasePitch - glm::quat getCameraOrientation () const; - - void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); + void setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition); glm::vec3 getCorrectedLookAtPosition(); void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } bool isLookingAtMe(); quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; } float getScale() const { return _scale; } - glm::vec3 getPosition() const { return _position; } + const glm::vec3& getPosition() const { return _position; } const glm::vec3& getEyePosition() const { return _eyePosition; } const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } @@ -91,46 +82,46 @@ public: float getTimeWithoutTalking() const { return _timeWithoutTalking; } -private: +protected: glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; } // disallow copies of the Head, copy of owning Avatar is disallowed too Head(const Head&); Head& operator= (const Head&); - bool _returnHeadToCenter; + bool _returnHeadToCenter { false }; glm::vec3 _position; glm::vec3 _rotation; glm::vec3 _leftEyePosition; glm::vec3 _rightEyePosition; glm::vec3 _eyePosition; - float _scale; - float _lastLoudness; - float _longTermAverageLoudness; - float _audioAttack; - float _audioJawOpen; - float _trailingAudioJawOpen; - float _mouth2; - float _mouth3; - float _mouth4; - float _mouthTime; + float _scale { 1.0f }; + float _lastLoudness { 0.0f }; + float _longTermAverageLoudness { -1.0f }; + float _audioAttack { 0.0f }; + float _audioJawOpen { 0.0f }; + float _trailingAudioJawOpen { 0.0f }; + float _mouth2 { 0.0f }; + float _mouth3 { 0.0f }; + float _mouth4 { 0.0f }; + float _mouthTime { 0.0f }; glm::vec3 _saccade; glm::vec3 _saccadeTarget; - float _leftEyeBlinkVelocity; - float _rightEyeBlinkVelocity; - float _timeWithoutTalking; + float _leftEyeBlinkVelocity { 0.0f }; + float _rightEyeBlinkVelocity { 0.0f }; + float _timeWithoutTalking { 0.0f }; // delta angles for local head rotation (driven by hardware input) - float _deltaPitch; - float _deltaYaw; - float _deltaRoll; + float _deltaPitch { 0.0f }; + float _deltaYaw { 0.0f }; + float _deltaRoll { 0.0f }; - bool _isCameraMoving; - bool _isLookingAtMe; - quint64 _lookingAtMeStarted; - quint64 _wasLastLookingAtMe; + bool _isCameraMoving { false }; + bool _isLookingAtMe { false }; + quint64 _lookingAtMeStarted { 0 }; + quint64 _wasLastLookingAtMe { 0 }; glm::vec3 _correctedLookAtPosition; diff --git a/libraries/avatars-renderer/src/AvatarsRendererLogging.cpp b/libraries/avatars-renderer/src/avatars-renderer/Logging.cpp similarity index 89% rename from libraries/avatars-renderer/src/AvatarsRendererLogging.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Logging.cpp index 2804df1b7a..f50902354d 100644 --- a/libraries/avatars-renderer/src/AvatarsRendererLogging.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Logging.cpp @@ -6,6 +6,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AvatarsRendererLogging.h" +#include "Logging.h" Q_LOGGING_CATEGORY(avatars_renderer, "hifi.avatars.rendering") diff --git a/libraries/avatars-renderer/src/AvatarsRendererLogging.h b/libraries/avatars-renderer/src/avatars-renderer/Logging.h similarity index 100% rename from libraries/avatars-renderer/src/AvatarsRendererLogging.h rename to libraries/avatars-renderer/src/avatars-renderer/Logging.h diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp new file mode 100644 index 0000000000..ad69ba24cb --- /dev/null +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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 "OtherAvatar.h" + +OtherAvatar::OtherAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig) { + // give the pointer to our head to inherited _headData variable from AvatarData + _headData = new Head(this); + _skeletonModel = std::make_shared(this, nullptr, rig); + connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h new file mode 100644 index 0000000000..2f6c9a38aa --- /dev/null +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h @@ -0,0 +1,20 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 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_OtherAvatar_h +#define hifi_OtherAvatar_h + +#include "Avatar.h" + +class OtherAvatar : public Avatar { +public: + explicit OtherAvatar(QThread* thread, RigPointer rig = nullptr); + void instantiableAvatar() {}; +}; + +#endif // hifi_OtherAvatar_h diff --git a/interface/src/avatar/ScriptAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.cpp similarity index 100% rename from interface/src/avatar/ScriptAvatar.cpp rename to libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.cpp diff --git a/interface/src/avatar/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h similarity index 100% rename from interface/src/avatar/ScriptAvatar.h rename to libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h diff --git a/interface/src/avatar/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp similarity index 66% rename from interface/src/avatar/SkeletonModel.cpp rename to libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index f81a83523b..8fcc859b62 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -9,19 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "SkeletonModel.h" + #include #include #include #include +#include +#include -#include "Application.h" #include "Avatar.h" -#include "Menu.h" -#include "SkeletonModel.h" -#include "Util.h" -#include "InterfaceLogging.h" -#include "AnimDebugDraw.h" +#include "Logging.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : CauterizedModel(rig, parent), @@ -47,7 +46,7 @@ void SkeletonModel::initJointStates() { // Determine the default eye position for avatar scale = 1.0 int headJointIndex = geometry.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) { - qCWarning(interfaceapp) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); + qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -72,21 +71,6 @@ void SkeletonModel::initJointStates() { emit skeletonLoaded(); } -Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { - switch (state) { - default: - case CharacterController::State::Ground: - return Rig::CharacterControllerState::Ground; - case CharacterController::State::Takeoff: - return Rig::CharacterControllerState::Takeoff; - case CharacterController::State::InAir: - return Rig::CharacterControllerState::InAir; - case CharacterController::State::Hover: - return Rig::CharacterControllerState::Hover; - }; -} - - // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -102,122 +86,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { lookAt = _owningAvatar->getHead()->getEyePosition() + (MIN_LOOK_AT_FOCUS_DISTANCE / focusDistance) * focusOffset; } - if (_owningAvatar->isMyAvatar()) { - MyAvatar* myAvatar = static_cast(_owningAvatar); - - Rig::HeadParameters headParams; - - // input action is the highest priority source for head orientation. - auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); - if (avatarHeadPose.isValid()) { - glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - headParams.rigHeadPosition = extractTranslation(rigHeadMat); - headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat); - headParams.headEnabled = true; - } else { - if (qApp->isHMDMode()) { - // get HMD position from sensor space into world space, and back into rig space - glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); - glm::mat4 worldToRig = glm::inverse(rigToWorld); - glm::mat4 rigHMDMat = worldToRig * worldHMDMat; - _rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); - headParams.headEnabled = true; - } else { - // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. - // preMult 180 is necessary to convert from avatar to rig coordinates. - // postMult 180 is necessary to convert head from -z forward to z forward. - headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; - headParams.headEnabled = false; - } - } - - auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); - if (avatarHipsPose.isValid()) { - glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); - headParams.hipsMatrix = rigHipsMat; - headParams.hipsEnabled = true; - } else { - headParams.hipsEnabled = false; - } - - auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); - if (avatarSpine2Pose.isValid()) { - glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); - headParams.spine2Matrix = rigSpine2Mat; - headParams.spine2Enabled = true; - } else { - headParams.spine2Enabled = false; - } - - headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; - - _rig->updateFromHeadParameters(headParams, deltaTime); - - Rig::HandAndFeetParameters handAndFeetParams; - - auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); - if (leftPose.isValid()) { - handAndFeetParams.isLeftEnabled = true; - handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); - handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); - } else { - handAndFeetParams.isLeftEnabled = false; - } - - auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); - if (rightPose.isValid()) { - handAndFeetParams.isRightEnabled = true; - handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); - handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); - } else { - handAndFeetParams.isRightEnabled = false; - } - - auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); - if (leftFootPose.isValid()) { - handAndFeetParams.isLeftFootEnabled = true; - handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation(); - handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation(); - } else { - handAndFeetParams.isLeftFootEnabled = false; - } - - auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); - if (rightFootPose.isValid()) { - handAndFeetParams.isRightFootEnabled = true; - handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation(); - handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation(); - } else { - handAndFeetParams.isRightFootEnabled = false; - } - - handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); - handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); - handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); - - _rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); - - Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - - auto velocity = myAvatar->getLocalVelocity(); - auto position = myAvatar->getLocalPosition(); - auto orientation = myAvatar->getLocalOrientation(); - _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); - - // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); - - Rig::EyeParameters eyeParams; - eyeParams.eyeLookAt = lookAt; - eyeParams.eyeSaccade = head->getSaccade(); - eyeParams.modelRotation = getRotation(); - eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - - _rig->updateFromEyeParameters(eyeParams); - } else { + if (!_owningAvatar->isMyAvatar()) { // no need to call Model::updateRig() because otherAvatars get their joint state // copied directly from AvtarData::_jointData (there are no Rig animations to blend) _needsUpdateClusterMatrices = true; @@ -249,6 +118,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); } + + // evaluate AnimGraph animation and update jointStates. + Model::updateRig(deltaTime, parentTransform); } void SkeletonModel::updateAttitude() { diff --git a/interface/src/avatar/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h similarity index 99% rename from interface/src/avatar/SkeletonModel.h rename to libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 7a6081a010..23af04e964 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -114,7 +114,7 @@ protected: void computeBoundingShape(); -private: +protected: bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4c79470de..6196c4eeb3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -94,7 +94,7 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = // +-----+-----+-+-+-+--+ // Key state - K0,K1 is found in the 1st and 2nd bits // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits -// Faceshift - F is found in the 5th bit +// Face tracker - F is found in the 5th bit // Eye tracker - E is found in the 6th bit // Referential Data - R is found in the 7th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits @@ -123,7 +123,7 @@ namespace AvatarDataPacket { // it might be nice to use a dictionary to compress that // Packet State Flags - we store the details about the existence of other records in this bitset: - // AvatarGlobalPosition, Avatar Faceshift, eye tracking, and existence of + // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 271ce133a2..b55be7c156 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -23,11 +23,6 @@ #include "AvatarData.h" -/// The names of the blendshapes expected by Faceshift, terminated with an empty string. -extern const char* FACESHIFT_BLENDSHAPES[]; -/// The size of FACESHIFT_BLENDSHAPES -extern const int NUM_FACESHIFT_BLENDSHAPES; - HeadData::HeadData(AvatarData* owningAvatar) : _baseYaw(0.0f), _basePitch(0.0f), diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp similarity index 94% rename from interface/src/avatar/CauterizedMeshPartPayload.cpp rename to libraries/render-utils/src/CauterizedMeshPartPayload.cpp index c11f92083b..2e3d0385cd 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -13,7 +13,7 @@ #include -#include "SkeletonModel.h" +#include "CauterizedModel.h" using namespace render; @@ -29,7 +29,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - SkeletonModel* skeleton = static_cast(_model); + CauterizedModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); if (useCauterizedMesh) { diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h similarity index 87% rename from interface/src/avatar/CauterizedMeshPartPayload.h rename to libraries/render-utils/src/CauterizedMeshPartPayload.h index dc88e950c1..010cd6fcb6 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -1,9 +1,6 @@ // -// CauterizedModelMeshPartPayload.h -// interface/src/avatar -// // Created by AndrewMeadows 2017.01.17 -// Copyright 2017 High Fidelity, Inc. +// Copyright 2013-2017 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 @@ -12,7 +9,7 @@ #ifndef hifi_CauterizedMeshPartPayload_h #define hifi_CauterizedMeshPartPayload_h -#include +#include "MeshPartPayload.h" class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: diff --git a/interface/src/avatar/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp similarity index 98% rename from interface/src/avatar/CauterizedModel.cpp rename to libraries/render-utils/src/CauterizedModel.cpp index d7b7e0fedf..14625952ea 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -1,7 +1,4 @@ // -// CauterizedModel.cpp -// interface/src/avatar -// // Created by Andrew Meadows 2017.01.17 // Copyright 2017 High Fidelity, Inc. // @@ -11,10 +8,10 @@ #include "CauterizedModel.h" -#include -#include #include +#include "AbstractViewStateInterface.h" +#include "MeshPartPayload.h" #include "CauterizedMeshPartPayload.h" #include "RenderUtilsLogging.h" diff --git a/interface/src/avatar/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h similarity index 95% rename from interface/src/avatar/CauterizedModel.h rename to libraries/render-utils/src/CauterizedModel.h index ba12aee32b..39bcedf639 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -1,7 +1,4 @@ // -// CauterizeableModel.h -// interface/src/avatar -// // Created by Andrew Meadows 2016.01.17 // Copyright 2017 High Fidelity, Inc. // @@ -13,7 +10,7 @@ #define hifi_CauterizedModel_h -#include +#include "Model.h" class CauterizedModel : public Model { Q_OBJECT diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp similarity index 97% rename from interface/src/avatar/SoftAttachmentModel.cpp rename to libraries/render-utils/src/SoftAttachmentModel.cpp index 0521f7a893..8fef0f8f77 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -1,7 +1,4 @@ // -// SoftAttachmentModel.cpp -// interface/src/avatar -// // Created by Anthony J. Thibault on 12/17/15. // Copyright 2013 High Fidelity, Inc. // @@ -10,7 +7,6 @@ // #include "SoftAttachmentModel.h" -#include "InterfaceLogging.h" SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : CauterizedModel(rig, parent), diff --git a/interface/src/avatar/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h similarity index 95% rename from interface/src/avatar/SoftAttachmentModel.h rename to libraries/render-utils/src/SoftAttachmentModel.h index fea679839a..b66c1a289a 100644 --- a/interface/src/avatar/SoftAttachmentModel.h +++ b/libraries/render-utils/src/SoftAttachmentModel.h @@ -1,7 +1,4 @@ // -// SoftAttachmentModel.h -// interface/src/avatar -// // Created by Anthony J. Thibault on 12/17/15. // Copyright 2015 High Fidelity, Inc. // diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index db42fef8bc..70237e8ff6 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -582,3 +582,9 @@ glm::mat4 orthoInverse(const glm::mat4& m) { r[3][3] = 1.0f; return r; } + +// Return a random vector of average length 1 +glm::vec3 randVector() { + return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; +} + diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index fd75fa416c..ef92552d1f 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -252,6 +252,9 @@ inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value glm::mat4 orthoInverse(const glm::mat4& m); +// Return a random vector of average length 1 +glm::vec3 randVector(); + // // Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128 // diff --git a/interface/src/Camera.cpp b/libraries/shared/src/shared/Camera.cpp similarity index 79% rename from interface/src/Camera.cpp rename to libraries/shared/src/shared/Camera.cpp index 37f53e3cc2..48fea9e835 100644 --- a/interface/src/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -8,16 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include - -#include "Application.h" #include "Camera.h" -#include "Menu.h" -#include "Util.h" - CameraMode stringToMode(const QString& mode) { if (mode == "third person") { @@ -102,35 +93,9 @@ void Camera::setProjection(const glm::mat4& projection) { _projection = projection; } -PickRay Camera::computePickRay(float x, float y) { - return qApp->computePickRay(x, y); -} - void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); - switch (targetMode) { - case CAMERA_MODE_FIRST_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); - break; - case CAMERA_MODE_THIRD_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); - break; - case CAMERA_MODE_MIRROR: - Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true); - break; - case CAMERA_MODE_INDEPENDENT: - Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); - break; - case CAMERA_MODE_ENTITY: - Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); - break; - default: - break; - } - - qApp->cameraMenuChanged(); - if (_mode != targetMode) { setMode(targetMode); } diff --git a/interface/src/Camera.h b/libraries/shared/src/shared/Camera.h similarity index 94% rename from interface/src/Camera.h rename to libraries/shared/src/shared/Camera.h index c5a3b192ba..5f2162ff6e 100644 --- a/interface/src/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -11,11 +11,9 @@ #ifndef hifi_Camera_h #define hifi_Camera_h -#include -#include -#include -#include -#include +#include "../GLMHelpers.h" +#include "../RegisteredMetaTypes.h" +#include "../ViewFrustum.h" enum CameraMode { @@ -87,7 +85,7 @@ public slots: * @param {float} y Y-coordinate on screen. * @return {PickRay} The computed {PickRay}. */ - PickRay computePickRay(float x, float y); + virtual PickRay computePickRay(float x, float y) const = 0; /**jsdoc * Set the camera to look at position position. Only works while in independent. diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index ada1277c47..0fea933041 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -2,7 +2,7 @@ #include -class Camera { +class SimpleCamera { protected: float fov { 60.0f }; float znear { DEFAULT_NEAR_CLIP }, zfar { DEFAULT_FAR_CLIP }; @@ -42,7 +42,7 @@ public: std::bitset keys; - Camera() { + SimpleCamera() { matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index a1120ee3e1..cff866edbf 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -109,7 +109,7 @@ public: } }; -class QWindowCamera : public Camera { +class QWindowCamera : public SimpleCamera { Key forKey(int key) { switch (key) { case Qt::Key_W: return FORWARD; @@ -1067,7 +1067,7 @@ private: } void cycleMode() { - static auto defaultProjection = Camera().matrices.perspective; + static auto defaultProjection = SimpleCamera().matrices.perspective; _renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT); if (_renderMode == HMD) { _camera.matrices.perspective[0] = vec4 { 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; From 6cb95b239340f5d58eaf263598e7e3a952b1bf64 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 16:59:38 -0400 Subject: [PATCH 088/128] add and connect handlers for scriptWarningMessage and scriptInfoMessage signals --- interface/src/ui/JSConsole.cpp | 16 ++++++++++++++++ interface/src/ui/JSConsole.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 2d21d1d7ec..1abc5e8c2e 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -28,6 +28,8 @@ const int MAX_HISTORY_SIZE = 64; const QString COMMAND_STYLE = "color: #266a9b;"; const QString RESULT_SUCCESS_STYLE = "color: #677373;"; +const QString RESULT_INFO_STYLE = "color: #223bd1;"; +const QString RESULT_WARNING_STYLE = "color: #d1b322;"; const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; @@ -79,6 +81,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); if (_ownScriptEngine) { _scriptEngine->deleteLater(); @@ -90,6 +94,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); } @@ -145,6 +151,16 @@ void JSConsole::handlePrint(const QString& message, const QString& scriptName) { appendMessage("", message); } +void JSConsole::handleInfo(const QString& message, const QString& scriptName) { + Q_UNUSED(scriptName); + appendMessage("", "" + message.toHtmlEscaped() + ""); +} + +void JSConsole::handleWarning(const QString& message, const QString& scriptName) { + Q_UNUSED(scriptName); + appendMessage("", "" + message.toHtmlEscaped() + ""); +} + void JSConsole::mouseReleaseEvent(QMouseEvent* event) { _ui->promptTextEdit->setFocus(); } diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 938b670e78..864f847071 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -48,6 +48,8 @@ protected slots: void scrollToBottom(); void resizeTextInput(); void handlePrint(const QString& message, const QString& scriptName); + void handleInfo(const QString& message, const QString& scriptName); + void handleWarning(const QString& message, const QString& scriptName); void handleError(const QString& message, const QString& scriptName); void commandFinished(); From 9954380ce33d36bf79ba2af8f8df02ae681b3073 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 May 2017 14:44:48 -0700 Subject: [PATCH 089/128] don't filter out the place you're at anymore --- interface/resources/qml/hifi/Feed.qml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index fd3472b7be..fc108f47e3 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -156,10 +156,8 @@ Column { function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches var words = filter.toUpperCase().split(/\s+/).filter(identity); function suggestable(story) { - if (story.action === 'snapshot') { - return true; - } - return story.place_name !== AddressManager.placename; // Not our entry, but do show other entry points to current domain. + // We could filter out places we don't want to suggest, such as those where (story.place_name === AddressManager.placename) or (story.username === Account.username). + return true; } function matches(story) { if (!words.length) { From 25ab8e7e9443632253e5a69b392998025a88e09f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 May 2017 14:45:58 -0700 Subject: [PATCH 090/128] debugging on call, but mostly, specify json headers, etc. --- scripts/system/tablet-goto.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 2cdfecede4..fb842d1314 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -21,7 +21,7 @@ if (!DEBUG) { return; } - print([].map.call(arguments, JSON.stringify)); + print('tablet-goto.js:', [].map.call(arguments, JSON.stringify)); } var gotoQmlSource = "TabletAddressDialog.qml"; @@ -46,6 +46,7 @@ switch (message.method) { case 'request': request(message.params, function (error, data) { + debug('rpc', request, 'error:', error, 'data:', data); response.error = error; response.result = data; tablet.sendToQml(response); @@ -109,12 +110,14 @@ var stories = {}, pingPong = false; function expire(id) { - request({ + var options = { uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, method: 'PUT', - body: {expired: true} - }, function (error, response) { - debug('expired story', id, 'error:', error, 'response:', response); + json: true, + body: {expire: "true"} + }; + request(options, function (error, response) { + debug('expired story', options, 'error:', error, 'response:', response); if (error || (response.status !== 'success')) { print("ERROR expiring story: ", error || response.status); } @@ -147,7 +150,9 @@ var stored = stories[story.id], storedOrNew = stored || story; debug('story exists:', !!stored, storedOrNew); if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { - expire(story.id); + if (storedOrNew.audience == 'for_connections') { // Only expire if we haven't already done so. + expire(story.id); + } return; // before marking } storedOrNew.pingPong = pingPong; @@ -161,6 +166,7 @@ }); for (key in stories) { // Any story we were tracking that was not marked, has expired. if (stories[key].pingPong !== pingPong) { + debug('removing story', key); delete stories[key]; } } From deeb9c367ad7cdf7dd991060716461a64c13659d Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 17:46:28 -0400 Subject: [PATCH 091/128] update test to reflect support for scriptWarningMessage and scriptInfoMessage --- scripts/developer/tests/printTest.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index b94ff0c54b..d1069a6a8f 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -14,15 +14,12 @@ function main() { // note: these trigger the equivalent of an emit Script.printedMessage('[Script.printedMessage] hello world', '{filename}'); + Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); + Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); { - // FIXME: while not directly related to Script.print, these currently don't - // show up at all in the "HMD-friendly script log" or "Console..." - - Script.infoMessage('[Script.infoMessage] hello world', '{filename}'); - Script.warningMessage('[Script.warningMessage] hello world', '{filename}'); - + // FIXME: these only show up in the application debug log Vec3.print('[Vec3.print]', Vec3.HALF); var q = Quat.fromPitchYawRollDegrees(45, 45, 45); From ecda3132233f36dd53046ab9ff92f730fd0adc0e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 4 May 2017 15:07:59 -0700 Subject: [PATCH 092/128] Add runtime access to compression settings --- interface/src/ui/PreferencesDialog.cpp | 24 +++++ libraries/image/src/image/Image.cpp | 131 ++++++++++++++++++------- libraries/image/src/image/Image.h | 10 ++ 3 files changed, 131 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index e2e22fe366..767c122bb6 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -330,6 +330,30 @@ void setupPreferences() { preferences->addPreference(preference); } } + { + auto getter = []()->bool { return image::isColorTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setColorTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Color Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isNormalTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setNormalTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Normal Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isGrayscaleTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setGrayscaleTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Grayscale Textures", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = []()->bool { return image::isCubeTexturesCompressionEnabled(); }; + auto setter = [](bool value) { return image::setCubeTexturesCompressionEnabled(value); }; + auto preference = new CheckPreference(RENDER, "Compress Cube Textures", getter, setter); + preferences->addPreference(preference); + } } { static const QString RENDER("Networking"); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 825b07003b..68add428c1 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -22,16 +22,19 @@ #include #include #include +#include #include "ImageLogging.h" using namespace gpu; #define CPU_MIPMAPS 1 -#define COMPRESS_COLOR_TEXTURES 0 -#define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now -#define COMPRESS_GRAYSCALE_TEXTURES 0 -#define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now + +static std::mutex settingsMutex; +static Setting::Handle compressColorTextures("hifi.graphics.compressColorTextures", false); +static Setting::Handle compressNormalTextures("hifi.graphics.compressNormalTextures", false); +static Setting::Handle compressGrayscaleTextures("hifi.graphics.compressGrayscaleTextures", false); +static Setting::Handle compressCubeTextures("hifi.graphics.compressCubeTextures", false); static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE(4096); @@ -144,6 +147,64 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(co return processCubeTextureColorFromImage(srcImage, srcImageName, false); } + +bool isColorTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressColorTextures.get(); +#else + return false; +#endif +} + +bool isNormalTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressNormalTextures.get(); +#else + return false; +#endif +} + +bool isGrayscaleTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressGrayscaleTextures.get(); +#else + return false; +#endif +} + +bool isCubeTexturesCompressionEnabled() { +#if CPU_MIPMAPS + std::lock_guard guard(settingsMutex); + return compressCubeTextures.get(); +#else + return false; +#endif +} + +void setColorTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressColorTextures.set(enabled); +} + +void setNormalTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressNormalTextures.set(enabled); +} + +void setGrayscaleTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressGrayscaleTextures.set(enabled); +} + +void setCubeTexturesCompressionEnabled(bool enabled) { + std::lock_guard guard(settingsMutex); + compressCubeTextures.set(enabled); +} + + gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) { // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. @@ -428,18 +489,19 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { -#if CPU_MIPMAPS && COMPRESS_COLOR_TEXTURES + gpu::Element formatMip; gpu::Element formatGPU; - if (validAlpha) { - formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA; + if (isColorTexturesCompressionEnabled()) { + if (validAlpha) { + formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA; + } else { + formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB; + } + formatMip = formatGPU; } else { - formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB; + formatMip = gpu::Element::COLOR_SBGRA_32; + formatGPU = gpu::Element::COLOR_SRGBA_32; } - gpu::Element formatMip = formatGPU; -#else - gpu::Element formatMip = gpu::Element::COLOR_SBGRA_32; - gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32; -#endif if (isStrict) { theTexture = gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); @@ -547,14 +609,12 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - -#if CPU_MIPMAPS && COMPRESS_NORMALMAP_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_XY; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_XY; -#else gpu::Element formatMip = gpu::Element::VEC2NU8_XY; gpu::Element formatGPU = gpu::Element::VEC2NU8_XY; -#endif + if (isNormalTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_XY; + formatGPU = gpu::Element::COLOR_COMPRESSED_XY; + } theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -580,14 +640,15 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImag gpu::TexturePointer theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { - -#if CPU_MIPMAPS && COMPRESS_GRAYSCALE_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_RED; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_RED; -#else - gpu::Element formatMip = gpu::Element::COLOR_R_8; - gpu::Element formatGPU = gpu::Element::COLOR_R_8; -#endif + gpu::Element formatMip; + gpu::Element formatGPU; + if (isGrayscaleTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_RED; + formatGPU = gpu::Element::COLOR_COMPRESSED_RED; + } else { + formatMip = gpu::Element::COLOR_R_8; + formatGPU = gpu::Element::COLOR_R_8; + } theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); @@ -864,13 +925,15 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& image = image.convertToFormat(QImage::Format_ARGB32); } -#if CPU_MIPMAPS && COMPRESS_CUBEMAP_TEXTURES - gpu::Element formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; - gpu::Element formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; -#else - gpu::Element formatMip = gpu::Element::COLOR_SRGBA_32; - gpu::Element formatGPU = gpu::Element::COLOR_SRGBA_32; -#endif + gpu::Element formatMip; + gpu::Element formatGPU; + if (isCubeTexturesCompressionEnabled()) { + formatMip = gpu::Element::COLOR_COMPRESSED_SRGBA; + formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA; + } else { + formatMip = gpu::Element::COLOR_SRGBA_32; + formatGPU = gpu::Element::COLOR_SRGBA_32; + } // Find the layout of the cubemap in the 2D image // Use the original image size since processSourceImage may have altered the size / aspect ratio diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 3e5aa868d2..d9dd1105cd 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -63,6 +63,16 @@ gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, con } // namespace TextureUsage +bool isColorTexturesCompressionEnabled(); +bool isNormalTexturesCompressionEnabled(); +bool isGrayscaleTexturesCompressionEnabled(); +bool isCubeTexturesCompressionEnabled(); + +void setColorTexturesCompressionEnabled(bool enabled); +void setNormalTexturesCompressionEnabled(bool enabled); +void setGrayscaleTexturesCompressionEnabled(bool enabled); +void setCubeTexturesCompressionEnabled(bool enabled); + gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType); } // namespace image From 43a177cf9a3c3d9e271d1d7c7cac4ee3023384d3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 May 2017 15:22:44 -0700 Subject: [PATCH 093/128] CR feedback --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 98e4299a13..be55653f64 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -502,7 +502,10 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - _renderItemID = scene->allocateID(); + + if (_renderItemID == render::Item::INVALID_ITEM_ID) { + _renderItemID = scene->allocateID(); + } transaction.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel->addToScene(scene, transaction); for (auto& attachmentModel : _attachmentModels) { From 946c5e766253e1da45a8dfc3f5a958a79467e609 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 15:24:51 -0700 Subject: [PATCH 094/128] Almost finished with not-logged-in and not-shareable --- scripts/system/html/css/SnapshotReview.css | 14 ++ scripts/system/html/css/hifi-style.css | 10 +- scripts/system/html/js/SnapshotReview.js | 184 ++++++++++++++++----- scripts/system/snapshot.js | 147 +++++++++------- 4 files changed, 248 insertions(+), 107 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index bd9bb81fdc..69de4bb147 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -187,6 +187,20 @@ input[type=button].naked:active { padding-left: 8px; color: white; } +.helpTextDiv { + width: 350px; + height: 65px; + margin-right: 15px; + line-height: 65px; + position: absolute; + bottom: 0; + right: 0; + font-family: Raleway-Regular; + font-weight: 500; + font-size: 16px; + text-align: right; + color: white; +} /* // END styling of share overlay */ diff --git a/scripts/system/html/css/hifi-style.css b/scripts/system/html/css/hifi-style.css index ac34cee09f..ec6cd1a402 100644 --- a/scripts/system/html/css/hifi-style.css +++ b/scripts/system/html/css/hifi-style.css @@ -136,11 +136,14 @@ input[type=radio]:active + label > span > span{ } .grayButton { - font-family: FiraSans-SemiBold; - color: white; + font-family: Raleway-Bold; + font-size: 13px; + color: black; padding: 0px 10px; + border-radius: 3px; border-width: 0px; background-image: linear-gradient(#FFFFFF, #AFAFAF); + min-height: 30px; } .grayButton:hover { background-image: linear-gradient(#FFFFFF, #FFFFFF); @@ -152,7 +155,8 @@ input[type=radio]:active + label > span > span{ background-image: linear-gradient(#FFFFFF, ##AFAFAF); } .blueButton { - font-family: FiraSans-SemiBold; + font-family: Raleway-Bold; + font-size: 13px; color: white; padding: 0px 10px; border-radius: 3px; diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 27062faea2..dc0d319d16 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -62,6 +62,12 @@ function chooseSnapshotLocation() { action: "chooseSnapshotLocation" })); } +function login() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "login" + })); +} function clearImages() { document.getElementById("snap-button").disabled = false; var snapshotImagesDiv = document.getElementById("snapshot-images"); @@ -74,6 +80,52 @@ function clearImages() { idCounter = 0; } +function selectImageWithHelpText(selectedID, isSelected) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var imageContainer = document.getElementById(selectedID), + image = document.getElementById(selectedID + 'img'), + shareBar = document.getElementById(selectedID + "shareBar"), + helpTextDiv = document.getElementById(selectedID + "helpTextDiv"), + showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), + itr, + containers = document.getElementsByClassName("shareControls"); + + if (isSelected) { + showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, false); }; + showShareButtonsButtonDiv.classList.remove("inactive"); + showShareButtonsButtonDiv.classList.add("active"); + + image.onclick = function () { selectImageWithHelpText(selectedID, false); }; + imageContainer.style.outline = "4px solid #00b4ef"; + imageContainer.style.outlineOffset = "-4px"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; + shareBar.style.pointerEvents = "initial"; + + helpTextDiv.style.visibility = "visible"; + + for (itr = 0; itr < containers.length; itr += 1) { + var parentID = containers[itr].id.slice(0, 2); + if (parentID !== selectedID) { + selectImageWithHelpText(parentID, false); + } + } + } else { + showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, true); }; + showShareButtonsButtonDiv.classList.remove("active"); + showShareButtonsButtonDiv.classList.add("inactive"); + + image.onclick = function () { selectImageWithHelpText(selectedID, true); }; + imageContainer.style.outline = "none"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + shareBar.style.pointerEvents = "none"; + + helpTextDiv.style.visibility = "hidden"; + } +} function selectImageToShare(selectedID, isSelected) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID @@ -97,6 +149,7 @@ function selectImageToShare(selectedID, isSelected) { imageContainer.style.outlineOffset = "-4px"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; + shareBar.style.pointerEvents = "initial"; shareButtonsDiv.style.visibility = "visible"; shareBarHelp.style.visibility = "visible"; @@ -116,12 +169,13 @@ function selectImageToShare(selectedID, isSelected) { imageContainer.style.outline = "none"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + shareBar.style.pointerEvents = "none"; shareButtonsDiv.style.visibility = "hidden"; shareBarHelp.style.visibility = "hidden"; } } -function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { var shareBar = document.createElement("div"), shareBarHelpID = parentID + "shareBarHelp", shareButtonsDivID = parentID + "shareButtonsDiv", @@ -130,45 +184,85 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled blastToConnectionsButtonID = parentID + "blastToConnectionsButton", shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", facebookButtonID = parentID + "facebookButton", - twitterButtonID = parentID + "twitterButton"; + twitterButtonID = parentID + "twitterButton", + shareBarInnerHTML = ''; shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareBarInnerHTML = '' + - '
' + - '' + - '' + - '' + + + if (isLoggedIn) { + if (canShare) { + shareBarInnerHTML = '' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + + // Add onclick handler to parent DIV's img to toggle share buttons + document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; + } else { + shareBarInnerHTML = '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + // Add onclick handler to parent DIV's img to toggle share buttons + document.getElementById(parentID + 'img').onclick = function () { selectImageWithHelpText(parentID, true); }; + } + } else { + shareBarInnerHTML = '
' + + '' + + '' + + '' + + '
' + '' + - '' + - ''; shareBar.innerHTML = shareBarInnerHTML; - // Add onclick handler to parent DIV's img to toggle share buttons - document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; - return shareBar; } -function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function appendShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (divID.id) { divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); + document.getElementById(divID).appendChild(createShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); if (divID === "p0") { - selectImageToShare(divID, true); + if (isLoggedIn) { + if (canShare) { + selectImageWithHelpText(divID, true); + } else { + selectImageToShare(divID, true); + } + } else { + selectImageWithHelpText(divID, true); + } } - if (canBlast) { - shareButtonHovered('blast', divID, false); - } else { - shareButtonHovered('hifi', divID, false); + if (isLoggedIn && canShare) { + if (canBlast) { + shareButtonHovered('blast', divID, false); + } else { + shareButtonHovered('hifi', divID, false); + } } } function shareForUrl(selectedID) { @@ -178,7 +272,7 @@ function shareForUrl(selectedID) { data: paths[parseInt(selectedID.substring(1), 10)] })); } -function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (!image_data.localPath) { return; } @@ -203,12 +297,13 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b if (isGif) { imageContainer.innerHTML += 'GIF'; } - if (!isGifLoading && !isShowingPreviousImages && canShare) { - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true); + if (!isGifLoading) { + appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + } + if (!isGifLoading && !isShowingPreviousImages) { shareForUrl(id); } - if (isShowingPreviousImages && image_data.story_id) { - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { updateShareInfo(id, image_data.story_id); } } @@ -485,7 +580,7 @@ function handleCaptureSetting(setting) { window.onload = function () { // Uncomment the line below to test functionality in a browser. // See definition of "testInBrowser()" to modify tests. - //testInBrowser(3); + //testInBrowser(4); openEventBridge(function () { // Set up a handler for receiving the data, and tell the .js we are ready to receive it. EventBridge.scriptEventReceived.connect(function (message) { @@ -514,7 +609,7 @@ window.onload = function () { imageCount = message.image_data.length; if (imageCount > 0) { message.image_data.forEach(function (element, idx) { - addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); + addImage(element, messageOptions.isLoggedIn, message.canShare, false, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); }); } else { showSnapshotInstructions(); @@ -530,7 +625,7 @@ window.onload = function () { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon message.image_data.push({ localPath: messageOptions.loadingGifPath }); message.image_data.forEach(function (element, idx) { - addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); + addImage(element, messageOptions.isLoggedIn, idx === 0 && messageOptions.canShare, idx === 1, false); }); document.getElementById("p1").classList.add("processingGif"); } else { @@ -541,14 +636,14 @@ window.onload = function () { paths[1] = gifPath; if (messageOptions.canShare) { shareForUrl("p1"); - appendShareBar("p1", true, false, false, true); + appendShareBar("p1", messageOptions.isLoggedIn, true, false, false, true); document.getElementById("p1").classList.remove("processingGif"); } } } else { imageCount = message.image_data.length; message.image_data.forEach(function (element) { - addImage(element, false, messageOptions.canShare, false); + addImage(element, messageOptions.isLoggedIn, messageOptions.canShare, false, false); }); } break; @@ -590,18 +685,23 @@ function testInBrowser(test) { } else if (test === 1) { imageCount = 2; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); } else if (test === 2) { - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); showConfirmationMessage("p0", 'blast'); showConfirmationMessage("p1", 'hifi'); } else if (test === 3) { imageCount = 2; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); showUploadingMessage("p0", 'hifi'); - } + } else if (test === 4) { + imageCount = 2; + //addImage({ localPath: 'http://lorempixel.com/553/255' }); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, false, true, false, false, true); +} } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index b93595f0b4..a4e18d2869 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -29,12 +29,13 @@ var button = tablet.addButton({ sortOrder: 5 }); -var snapshotOptions; +var snapshotOptions = {}; var imageData = []; var storyIDsToMaybeDelete = []; var shareAfterLogin = false; -var snapshotToShareAfterLogin; +var snapshotToShareAfterLogin = []; var METAVERSE_BASE = location.metaverseServerUrl; +var isLoggedIn; // It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story, // POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS @@ -108,7 +109,6 @@ function onMessage(message) { return; } - var isLoggedIn; switch (message.action) { case 'ready': // DOM is ready and page has loaded tablet.emitScriptEvent(JSON.stringify({ @@ -141,6 +141,9 @@ function onMessage(message) { Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false); } break; + case 'login': + openLoginWindow(); + break; case 'chooseSnapshotLocation': var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", ""); @@ -177,18 +180,19 @@ function onMessage(message) { print('Sharing snapshot with audience "for_url":', message.data); Window.shareSnapshot(message.data, message.href || href); } else { - // TODO + shareAfterLogin = true; + snapshotToShareAfterLogin.push({ path: message.data, href: message.href || href }); } break; case 'blastToConnections': isLoggedIn = Account.isLoggedIn(); - if (message.isGif) { - Settings.setValue("previousAnimatedSnapBlastingDisabled", true); - } else { - Settings.setValue("previousStillSnapBlastingDisabled", true); - } - if (isLoggedIn) { + if (message.isGif) { + Settings.setValue("previousAnimatedSnapBlastingDisabled", true); + } else { + Settings.setValue("previousStillSnapBlastingDisabled", true); + } + print('Uploading new story for announcement!'); request({ @@ -234,20 +238,16 @@ function onMessage(message) { }); } }); - - } else { - openLoginWindow(); } break; case 'shareSnapshotWithEveryone': isLoggedIn = Account.isLoggedIn(); - if (message.isGif) { - Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); - } else { - Settings.setValue("previousStillSnapHifiSharingDisabled", true); - } - if (isLoggedIn) { + if (message.isGif) { + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); + } else { + Settings.setValue("previousStillSnapHifiSharingDisabled", true); + } print('Modifying audience of story ID', message.story_id, "to 'for_feed'"); var requestBody = { audience: "for_feed" @@ -275,10 +275,6 @@ function onMessage(message) { print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!")); } }); - } else { - openLoginWindow(); - shareAfterLogin = true; - snapshotToShareAfterLogin = { path: message.data, href: message.href || href }; } break; case 'removeFromStoryIDsToMaybeDelete': @@ -291,6 +287,42 @@ function onMessage(message) { } } +function fillImageDataFromPrevious() { + isLoggedIn = Account.isLoggedIn(); + var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); + var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); + var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled"); + var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled"); + var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); + var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); + var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); + var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); + snapshotOptions = { + containsGif: previousAnimatedSnapPath !== "", + processingGif: false, + shouldUpload: false, + canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + isLoggedIn: isLoggedIn + }; + imageData = []; + if (previousStillSnapPath !== "") { + imageData.push({ + localPath: previousStillSnapPath, + story_id: previousStillSnapStoryID, + blastButtonDisabled: previousStillSnapBlastingDisabled, + hifiButtonDisabled: previousStillSnapHifiSharingDisabled + }); + } + if (previousAnimatedSnapPath !== "") { + imageData.push({ + localPath: previousAnimatedSnapPath, + story_id: previousAnimatedSnapStoryID, + blastButtonDisabled: previousAnimatedSnapBlastingDisabled, + hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled + }); + } +} + var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); var isInSnapshotReview = false; var shouldActivateButton = false; @@ -300,37 +332,7 @@ function onButtonClicked() { tablet.gotoHomeScreen(); } else { shouldActivateButton = true; - var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); - var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); - var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled"); - var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled"); - var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); - var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); - var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); - var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); - snapshotOptions = { - containsGif: previousAnimatedSnapPath !== "", - processingGif: false, - shouldUpload: false, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID") - } - imageData = []; - if (previousStillSnapPath !== "") { - imageData.push({ - localPath: previousStillSnapPath, - story_id: previousStillSnapStoryID, - blastButtonDisabled: previousStillSnapBlastingDisabled, - hifiButtonDisabled: previousStillSnapHifiSharingDisabled - }); - } - if (previousAnimatedSnapPath !== "") { - imageData.push({ - localPath: previousAnimatedSnapPath, - story_id: previousAnimatedSnapStoryID, - blastButtonDisabled: previousAnimatedSnapBlastingDisabled, - hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled - }); - } + fillImageDataFromPrevious(); tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); tablet.webEventReceived.connect(onMessage); HMD.openTablet(); @@ -453,6 +455,7 @@ function isDomainOpen(id, callback) { } function stillSnapshotTaken(pathStillSnapshot, notify) { + isLoggedIn = Account.isLoggedIn(); // show hud Reticle.visible = reticleVisible; Reticle.allowMouseCapture = true; @@ -481,7 +484,8 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { snapshotOptions = { containsGif: false, processingGif: false, - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -496,6 +500,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { function processingGifStarted(pathStillSnapshot) { Window.processingGifStarted.disconnect(processingGifStarted); Window.processingGifCompleted.connect(processingGifCompleted); + isLoggedIn = Account.isLoggedIn(); // show hud Reticle.visible = reticleVisible; Reticle.allowMouseCapture = true; @@ -515,7 +520,8 @@ function processingGifStarted(pathStillSnapshot) { containsGif: true, processingGif: true, loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'), - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -528,6 +534,7 @@ function processingGifStarted(pathStillSnapshot) { } function processingGifCompleted(pathAnimatedSnapshot) { + isLoggedIn = Account.isLoggedIn(); Window.processingGifCompleted.disconnect(processingGifCompleted); if (!buttonConnected) { button.clicked.connect(onButtonClicked); @@ -540,7 +547,8 @@ function processingGifCompleted(pathAnimatedSnapshot) { snapshotOptions = { containsGif: true, processingGif: false, - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -576,10 +584,25 @@ function onTabletScreenChanged(type, url) { } } function onUsernameChanged() { - if (shareAfterLogin && Account.isLoggedIn()) { - print('Sharing snapshot after login:', snapshotToShareAfterLogin.path); - Window.shareSnapshot(snapshotToShareAfterLogin.path, snapshotToShareAfterLogin.href); - shareAfterLogin = false; + fillImageDataFromPrevious(); + isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "showPreviousImages", + options: snapshotOptions, + image_data: imageData, + canShare: canShare + })); + }); + if (isLoggedIn) { + if (shareAfterLogin) { + snapshotToShareAfterLogin.forEach(function (element) { + print('Uploading snapshot after login:', element.path); + Window.shareSnapshot(element.path, element.href); + }); + shareAfterLogin = false; + snapshotToShareAfterLogin = []; + } } } function snapshotLocationSet(location) { @@ -595,7 +618,7 @@ button.clicked.connect(onButtonClicked); buttonConnected = true; Window.snapshotShared.connect(snapshotUploaded); tablet.screenChanged.connect(onTabletScreenChanged); -Account.usernameChanged.connect(onUsernameChanged); +GlobalServices.myUsernameChanged.connect(onUsernameChanged); Snapshot.snapshotLocationSet.connect(snapshotLocationSet); Script.scriptEnding.connect(function () { if (buttonConnected) { From f71552c648af5c10e32e764c0601555a87bc35be Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 17:04:13 -0400 Subject: [PATCH 095/128] * update Vec3.print, Quat.print, Mat4.print, ScriptUUID.print to work with JSConsole and HMD-friendly script log * add test output for Quat-euler and Mat4-row/col prints --- libraries/script-engine/src/Mat4.cpp | 16 ++++++++++------ libraries/script-engine/src/Mat4.h | 5 +++-- libraries/script-engine/src/Quat.cpp | 15 +++++++++++++-- libraries/script-engine/src/Quat.h | 5 +++-- libraries/script-engine/src/ScriptUUID.cpp | 10 ++++++++-- libraries/script-engine/src/ScriptUUID.h | 5 +++-- libraries/script-engine/src/Vec3.cpp | 12 +++++++++--- libraries/script-engine/src/Vec3.h | 3 ++- scripts/developer/tests/printTest.js | 14 +++++++++++--- 9 files changed, 62 insertions(+), 23 deletions(-) diff --git a/libraries/script-engine/src/Mat4.cpp b/libraries/script-engine/src/Mat4.cpp index 6676d0cde1..2d26a66823 100644 --- a/libraries/script-engine/src/Mat4.cpp +++ b/libraries/script-engine/src/Mat4.cpp @@ -11,7 +11,9 @@ #include #include +#include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "Mat4.h" glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const { @@ -66,10 +68,12 @@ glm::vec3 Mat4::getUp(const glm::mat4& m) const { return glm::vec3(m[0][1], m[1][1], m[2][1]); } -void Mat4::print(const QString& label, const glm::mat4& m) const { - qCDebug(scriptengine) << qPrintable(label) << - "row0 =" << m[0][0] << "," << m[1][0] << "," << m[2][0] << "," << m[3][0] << - "row1 =" << m[0][1] << "," << m[1][1] << "," << m[2][1] << "," << m[3][1] << - "row2 =" << m[0][2] << "," << m[1][2] << "," << m[2][2] << "," << m[3][2] << - "row3 =" << m[0][3] << "," << m[1][3] << "," << m[2][3] << "," << m[3][3]; +void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const { + glm::mat4 out = transpose ? glm::transpose(m) : m; + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(glm::to_string(out).c_str()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } diff --git a/libraries/script-engine/src/Mat4.h b/libraries/script-engine/src/Mat4.h index 19bbbe178a..8b942874ee 100644 --- a/libraries/script-engine/src/Mat4.h +++ b/libraries/script-engine/src/Mat4.h @@ -16,9 +16,10 @@ #include #include +#include /// Scriptable Mat4 object. Used exclusively in the JavaScript API -class Mat4 : public QObject { +class Mat4 : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -43,7 +44,7 @@ public slots: glm::vec3 getRight(const glm::mat4& m) const; glm::vec3 getUp(const glm::mat4& m) const; - void print(const QString& label, const glm::mat4& m) const; + void print(const QString& label, const glm::mat4& m, bool transpose = false) const; }; #endif // hifi_Mat4_h diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 05002dcf5d..8d84b45b90 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -15,7 +15,9 @@ #include #include +#include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "Quat.h" quat Quat::normalize(const glm::quat& q) { @@ -114,8 +116,17 @@ float Quat::dot(const glm::quat& q1, const glm::quat& q2) { return glm::dot(q1, q2); } -void Quat::print(const QString& label, const glm::quat& q) { - qCDebug(scriptengine) << qPrintable(label) << q.x << "," << q.y << "," << q.z << "," << q.w; +void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) { + QString message = QString("%1 %2").arg(qPrintable(label)); + if (asDegrees) { + message = message.arg(glm::to_string(safeEulerAngles(q)).c_str()); + } else { + message = message.arg(glm::to_string(q).c_str()); + } + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } bool Quat::equal(const glm::quat& q1, const glm::quat& q2) { diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index ee3ab9aa7c..3b3a6fde7c 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -18,6 +18,7 @@ #include #include +#include /**jsdoc * A Quaternion @@ -30,7 +31,7 @@ */ /// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API -class Quat : public QObject { +class Quat : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -58,7 +59,7 @@ public slots: glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha); glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h); float dot(const glm::quat& q1, const glm::quat& q2); - void print(const QString& label, const glm::quat& q); + void print(const QString& label, const glm::quat& q, bool asDegrees = false); bool equal(const glm::quat& q1, const glm::quat& q2); glm::quat cancelOutRollAndPitch(const glm::quat& q); glm::quat cancelOutRoll(const glm::quat& q); diff --git a/libraries/script-engine/src/ScriptUUID.cpp b/libraries/script-engine/src/ScriptUUID.cpp index 6a52f4f6ca..ee15f1a760 100644 --- a/libraries/script-engine/src/ScriptUUID.cpp +++ b/libraries/script-engine/src/ScriptUUID.cpp @@ -14,6 +14,7 @@ #include #include "ScriptEngineLogging.h" +#include "ScriptEngine.h" #include "ScriptUUID.h" QUuid ScriptUUID::fromString(const QString& s) { @@ -36,6 +37,11 @@ bool ScriptUUID::isNull(const QUuid& id) { return id.isNull(); } -void ScriptUUID::print(const QString& lable, const QUuid& id) { - qCDebug(scriptengine) << qPrintable(lable) << id.toString(); +void ScriptUUID::print(const QString& label, const QUuid& id) { + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(id.toString()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h index db94b5082b..221f9c46f0 100644 --- a/libraries/script-engine/src/ScriptUUID.h +++ b/libraries/script-engine/src/ScriptUUID.h @@ -15,9 +15,10 @@ #define hifi_ScriptUUID_h #include +#include /// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API -class ScriptUUID : public QObject { +class ScriptUUID : public QObject, protected QScriptable { Q_OBJECT public slots: @@ -26,7 +27,7 @@ public slots: QUuid generate(); bool isEqual(const QUuid& idA, const QUuid& idB); bool isNull(const QUuid& id); - void print(const QString& lable, const QUuid& id); + void print(const QString& label, const QUuid& id); }; #endif // hifi_ScriptUUID_h diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 6c8f618500..f66b99dfa1 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -14,20 +14,26 @@ #include #include +#include #include "ScriptEngineLogging.h" #include "NumericalConstants.h" #include "Vec3.h" +#include "ScriptEngine.h" float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3)); return glm::degrees(radians); } - -void Vec3::print(const QString& lable, const glm::vec3& v) { - qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z; +void Vec3::print(const QString& label, const glm::vec3& v) { + QString message = QString("%1 %2").arg(qPrintable(label)); + message = message.arg(glm::to_string(v).c_str()); + qCDebug(scriptengine) << message; + if (ScriptEngine* scriptEngine = qobject_cast(engine())) { + scriptEngine->print(message); + } } bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) { diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index b3a3dc3035..c7179a80c0 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -17,6 +17,7 @@ #include #include +#include #include "GLMHelpers.h" @@ -48,7 +49,7 @@ */ /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API -class Vec3 : public QObject { +class Vec3 : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index d1069a6a8f..5fcd8ecb74 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -19,13 +19,21 @@ function main() { Script.errorMessage('[Script.errorMessage] hello world', '{filename}'); { - // FIXME: these only show up in the application debug log Vec3.print('[Vec3.print]', Vec3.HALF); var q = Quat.fromPitchYawRollDegrees(45, 45, 45); Quat.print('[Quat.print]', q); + Quat.print('[Quat.print (euler)]', q, true); - var m = Mat4.createFromRotAndTrans(q, Vec3.HALF); - Mat4.print('[Mat4.print (row major)]', m); + function vec4(x,y,z,w) { + return { x: x, y: y, z: z, w: w }; + } + var m = Mat4.createFromColumns( + vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16) + ); + Mat4.print('[Mat4.print (col major)]', m); + Mat4.print('[Mat4.print (row major)]', m, true); + + Uuid.print('[Uuid.print]', Uuid.toString(0)); } } From f11d6eff92e0827b91a13532ae5dbdbf29db5526 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 May 2017 15:43:44 -0700 Subject: [PATCH 096/128] fix typos: RenderItemsMap not RenderItems --- libraries/render-utils/src/Model.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index eddda41d5e..acc84646c5 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -573,7 +573,7 @@ bool Model::addToScene(const render::ScenePointer& scene, bool somethingAdded = false; if (_collisionGeometry) { - if (_collisionRenderItems.empty()) { + if (_collisionRenderItemsMap.empty()) { foreach (auto renderItem, _collisionRenderItems) { auto item = scene->allocateID(); auto renderPayload = std::make_shared(renderItem); @@ -583,7 +583,7 @@ bool Model::addToScene(const render::ScenePointer& scene, transaction.resetItem(item, renderPayload); _collisionRenderItemsMap.insert(item, renderPayload); } - somethingAdded = !_collisionRenderItems.empty(); + somethingAdded = !_collisionRenderItemsMap.empty(); } } else { if (_modelMeshRenderItemsMap.empty()) { @@ -632,7 +632,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti transaction.removeItem(item); } _collisionRenderItems.clear(); - _collisionRenderItems.clear(); + _collisionRenderItemsMap.clear(); _addedToScene = false; _renderInfoVertexCount = 0; From f9d29256e0531c850ac9351ff80ad406ae0f9c6c Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 4 May 2017 18:58:30 -0400 Subject: [PATCH 097/128] use an actual Uuid value for the Uuid.print test --- scripts/developer/tests/printTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/tests/printTest.js b/scripts/developer/tests/printTest.js index 5fcd8ecb74..c1fe6ec745 100644 --- a/scripts/developer/tests/printTest.js +++ b/scripts/developer/tests/printTest.js @@ -34,6 +34,6 @@ function main() { Mat4.print('[Mat4.print (col major)]', m); Mat4.print('[Mat4.print (row major)]', m, true); - Uuid.print('[Uuid.print]', Uuid.toString(0)); + Uuid.print('[Uuid.print]', Uuid.fromString(Uuid.toString(0))); } } From f39411656a5f515bf69782d105b37bf823e51465 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 May 2017 16:46:03 -0700 Subject: [PATCH 098/128] use machine GUID from registry for machine ID --- libraries/networking/src/FingerprintUtils.cpp | 129 +++--------------- 1 file changed, 20 insertions(+), 109 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 1990d356b6..89f692e77a 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -19,8 +19,8 @@ #include #ifdef Q_OS_WIN -#include -#include +#include +#include #endif //Q_OS_WIN #ifdef Q_OS_MAC @@ -47,122 +47,32 @@ QString FingerprintUtils::getMachineFingerprintString() { #endif //Q_OS_MAC #ifdef Q_OS_WIN - HRESULT hres; - IWbemLocator *pLoc = NULL; - - // initialize com. Interface already does, but other - // users of this lib don't necessarily do so. - hres = CoInitializeEx(0, COINIT_MULTITHREADED); - if (FAILED(hres)) { - qCDebug(networking) << "Failed to initialize COM library!"; - return uuidString; - } + HKEY cryptoKey; - // initialize WbemLocator - hres = CoCreateInstance( - CLSID_WbemLocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, (LPVOID *) &pLoc); + // try and open the key that contains the machine GUID + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) { + DWORD type; + DWORD guidSize; - if (FAILED(hres)) { - qCDebug(networking) << "Failed to initialize WbemLocator"; - return uuidString; - } - - // Connect to WMI through the IWbemLocator::ConnectServer method - IWbemServices *pSvc = NULL; + const char* MACHINE_GUID_KEY = "MachineGuid"; - // Connect to the root\cimv2 namespace with - // the current user and obtain pointer pSvc - // to make IWbemServices calls. - hres = pLoc->ConnectServer( - _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace - NULL, // User name. NULL = current user - NULL, // User password. NULL = current - 0, // Locale. NULL indicates current - NULL, // Security flags. - 0, // Authority (for example, Kerberos) - 0, // Context object - &pSvc // pointer to IWbemServices proxy - ); + // try and retrieve the size of the GUID value + if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, &type, NULL, &guidSize) == ERROR_SUCCESS) { + // make sure that the value is a string + if (type == REG_SZ) { + // retrieve the machine GUID and return that as our UUID string + std::string machineGUID(guidSize / sizeof(char), '\0'); - if (FAILED(hres)) { - pLoc->Release(); - qCDebug(networking) << "Failed to connect to WMI"; - return uuidString; - } - - // Set security levels on the proxy - hres = CoSetProxyBlanket( - pSvc, // Indicates the proxy to set - RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx - RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx - NULL, // Server principal name - RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx - RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx - NULL, // client identity - EOAC_NONE // proxy capabilities - ); - - if (FAILED(hres)) { - pSvc->Release(); - pLoc->Release(); - qCDebug(networking) << "Failed to set security on proxy blanket"; - return uuidString; - } - - // Use the IWbemServices pointer to grab the Win32_BIOS stuff - IEnumWbemClassObject* pEnumerator = NULL; - hres = pSvc->ExecQuery( - bstr_t("WQL"), - bstr_t("SELECT * FROM Win32_ComputerSystemProduct"), - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, - NULL, - &pEnumerator); - - if (FAILED(hres)) { - pSvc->Release(); - pLoc->Release(); - qCDebug(networking) << "query to get Win32_ComputerSystemProduct info"; - return uuidString; - } - - // Get the SerialNumber from the Win32_BIOS data - IWbemClassObject *pclsObj; - ULONG uReturn = 0; - - SHORT sRetStatus = -100; - - while (pEnumerator) { - HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); - - if(0 == uReturn){ - break; - } - - VARIANT vtProp; - - // Get the value of the Name property - hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0); - if (!FAILED(hres)) { - switch (vtProp.vt) { - case VT_BSTR: - uuidString = QString::fromWCharArray(vtProp.bstrVal); - break; + if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, NULL, + reinterpret_cast(&machineGUID[0]), &guidSize) == ERROR_SUCCESS) { + uuidString = QString::fromStdString(machineGUID); + } } } - VariantClear(&vtProp); - pclsObj->Release(); + RegCloseKey(cryptoKey); } - pEnumerator->Release(); - // Cleanup - pSvc->Release(); - pLoc->Release(); - - qCDebug(networking) << "Windows BIOS UUID: " << uuidString; #endif //Q_OS_WIN return uuidString; @@ -177,6 +87,7 @@ QUuid FingerprintUtils::getMachineFingerprint() { // return QUuid() ("{00000...}"), which handles // any errors in getting the string QUuid uuid(uuidString); + if (uuid == QUuid()) { // if you cannot read a fallback key cuz we aren't saving them, just generate one for // this session and move on From 1857b297e091bf0441526c5420436e13f8bc9582 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 May 2017 15:52:04 -0700 Subject: [PATCH 099/128] don't re-grab the machine fingerprint every DS connection --- libraries/networking/src/FingerprintUtils.cpp | 51 +++++++++++-------- libraries/networking/src/FingerprintUtils.h | 1 + 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 89f692e77a..216e0f28dd 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -30,6 +30,9 @@ #endif //Q_OS_MAC static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; + +QUuid FingerprintUtils::_machineFingerprint { QUuid() }; + QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; #ifdef Q_OS_LINUX @@ -81,30 +84,36 @@ QString FingerprintUtils::getMachineFingerprintString() { QUuid FingerprintUtils::getMachineFingerprint() { - QString uuidString = getMachineFingerprintString(); + if (_machineFingerprint.isNull()) { + QString uuidString = getMachineFingerprintString(); + + // now, turn into uuid. A malformed string will + // return QUuid() ("{00000...}"), which handles + // any errors in getting the string + QUuid uuid(uuidString); - // now, turn into uuid. A malformed string will - // return QUuid() ("{00000...}"), which handles - // any errors in getting the string - QUuid uuid(uuidString); - - if (uuid == QUuid()) { - // if you cannot read a fallback key cuz we aren't saving them, just generate one for - // this session and move on - if (DependencyManager::get().isNull()) { - return QUuid::createUuid(); - } - // read fallback key (if any) - Settings settings; - uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); - qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString(); if (uuid == QUuid()) { - // no fallback yet, set one - uuid = QUuid::createUuid(); - settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); - qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + // if you cannot read a fallback key cuz we aren't saving them, just generate one for + // this session and move on + if (DependencyManager::get().isNull()) { + return QUuid::createUuid(); + } + // read fallback key (if any) + Settings settings; + uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); + qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString(); + + if (uuid == QUuid()) { + // no fallback yet, set one + uuid = QUuid::createUuid(); + settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); + qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + } } + + _machineFingerprint = uuid; } - return uuid; + + return _machineFingerprint; } diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index 572b150ec4..c4cb900a48 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -21,6 +21,7 @@ public: private: static QString getMachineFingerprintString(); + static QUuid _machineFingerprint; }; #endif // hifi_FingerprintUtils_h From 2ad3346697355a0ca759a92178b30c38c5efc1df Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 16:05:20 -0700 Subject: [PATCH 100/128] Are there bugs? --- scripts/system/html/css/SnapshotReview.css | 1 - scripts/system/html/js/SnapshotReview.js | 22 +++++++------- scripts/system/snapshot.js | 34 ++++++++++++++-------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 69de4bb147..218eb8c9a5 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -198,7 +198,6 @@ input[type=button].naked:active { font-family: Raleway-Regular; font-weight: 500; font-size: 16px; - text-align: right; color: white; } /* diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index dc0d319d16..25bc25b776 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -217,8 +217,8 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl '' + '' + '' + - '' + '' + - '' + '' + - '