From 24dd06b65e9068ce4b3b8913266a703983e88d7a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 23 Feb 2017 09:09:54 -0500 Subject: [PATCH 001/292] * 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/292] 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 bab7d1e596ac5975dc73501a59033fa7c450d4fc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Apr 2017 12:17:28 -0700 Subject: [PATCH 003/292] first cut at translate and scale pose filters --- .../src/controllers/UserInputMapper.cpp | 13 +++++ .../src/controllers/impl/Filter.cpp | 27 ++++++++++- .../controllers/src/controllers/impl/Filter.h | 5 ++ .../controllers/impl/RouteBuilderProxy.cpp | 7 +++ .../src/controllers/impl/RouteBuilderProxy.h | 1 + .../controllers/impl/filters/ScaleFilter.h | 7 +++ .../impl/filters/TranslateFilter.cpp | 23 +++++++++ .../impl/filters/TranslateFilter.h | 47 +++++++++++++++++++ script-archive/controllers/puppetFeet3.js | 24 ++++++++++ 9 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/TranslateFilter.h create mode 100644 script-archive/controllers/puppetFeet3.js diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index fe50f023c3..27da222fa6 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -20,6 +20,8 @@ #include #include +#include + #include "StandardController.h" #include "StateController.h" @@ -561,7 +563,18 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { if (source->isPose()) { Pose value = getPose(source, route->peek); static const Pose IDENTITY_POSE { vec3(), quat() }; + if (debugRoutes && route->debug) { + qCDebug(controllers) << "Value was t:" << value.translation << "r:" << value.rotation; + } + // Apply each of the filters. + for (const auto& filter : route->filters) { + value = filter->apply(value); + } + + if (debugRoutes && route->debug) { + qCDebug(controllers) << "Filtered value was t:" << value.translation << "r:" << value.rotation; + if (!value.valid) { qCDebug(controllers) << "Applying invalid pose"; } else if (value == IDENTITY_POSE) { diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 09188318eb..a75c43463e 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -24,6 +24,7 @@ #include "filters/InvertFilter.h" #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" +#include "filters/TranslateFilter.h" using namespace controller; @@ -37,6 +38,7 @@ REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") +REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); @@ -76,7 +78,6 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri return true; } } else if (parameters.isObject()) { - static const QString JSON_MIN = QStringLiteral("interval"); auto objectParameters = parameters.toObject(); if (objectParameters.contains(name)) { output = objectParameters[name].toDouble(); @@ -86,6 +87,30 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri return false; } +// FIXME - we're not really using the name +bool Filter::parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output) { + if (parameters.isDouble()) { + output = glm::vec3(parameters.toDouble()); + return true; + } else if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() == 3) { + output = glm::vec3(arrayParameters[0].toDouble(), + arrayParameters[1].toDouble(), + arrayParameters[2].toDouble()); + return true; + } + } else if (parameters.isObject()) { + auto objectParameters = parameters.toObject(); + if (objectParameters.contains("x") && objectParameters.contains("y") && objectParameters.contains("z")) { + output = glm::vec3(objectParameters["x"].toDouble(), + objectParameters["y"].toDouble(), + objectParameters["z"].toDouble()); + return true; + } + } + return false; +} #if 0 diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index 77585c8ebb..c268a227d7 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -21,6 +21,8 @@ #include +#include "../Pose.h" + class QJsonValue; namespace controller { @@ -34,6 +36,8 @@ namespace controller { using Factory = hifi::SimpleFactory; virtual float apply(float value) const = 0; + virtual Pose apply(Pose value) const { return value; } // most filters don't operate on poses + // Factory features virtual bool parseParameters(const QJsonValue& parameters) { return true; } @@ -42,6 +46,7 @@ namespace controller { static Factory& getFactory() { return _factory; } static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); + static bool parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output); protected: static Factory _factory; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 7dedfda3cb..93ec8d4acd 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -26,6 +26,7 @@ #include "filters/InvertFilter.h" #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" +#include "filters/TranslateFilter.h" #include "conditionals/AndConditional.h" using namespace controller; @@ -103,6 +104,12 @@ QObject* RouteBuilderProxy::deadZone(float min) { return this; } +QObject* RouteBuilderProxy::translate(glm::vec3 translate) { + qDebug() << __FUNCTION__ << "translate:" << translate; + addFilter(std::make_shared(translate)); + return this; +} + QObject* RouteBuilderProxy::constrainToInteger() { addFilter(std::make_shared()); return this; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 1c0ed6931d..0d3ddd55fb 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -48,6 +48,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* deadZone(float min); Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger(); + Q_INVOKABLE QObject* translate(glm::vec3 translate); private: void to(const Endpoint::Pointer& destination); diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h index 670da53fe8..7b03e2ce48 100644 --- a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h @@ -10,6 +10,8 @@ #ifndef hifi_Controllers_Filters_Scale_h #define hifi_Controllers_Filters_Scale_h +#include + #include "../Filter.h" namespace controller { @@ -23,6 +25,11 @@ public: virtual float apply(float value) const override { return value * _scale; } + + virtual Pose apply(Pose value) const override { + return value.transform(glm::scale(glm::mat4(), glm::vec3(_scale))); + } + virtual bool parseParameters(const QJsonValue& parameters) override; private: diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp new file mode 100644 index 0000000000..6f7e1e6a45 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp @@ -0,0 +1,23 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TranslateFilter.h" + +#include +#include + +#include + +using namespace controller; + +bool TranslateFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_TRANSLATE = QStringLiteral("translate"); + bool result = parseVec3Parameter(parameters, JSON_TRANSLATE, _translate); + qDebug() << __FUNCTION__ << "_translate:" << _translate; + return result; +} diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h new file mode 100644 index 0000000000..504e4c3151 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h @@ -0,0 +1,47 @@ +// +// Created by Brad Hefta-Gaub 2017/04/11 +// Copyright 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 +// + +#pragma once +#ifndef hifi_Controllers_Filters_Translate_h +#define hifi_Controllers_Filters_Translate_h + +#include + +#include + +#include "../Filter.h" + +namespace controller { + +class TranslateFilter : public Filter { + REGISTER_FILTER_CLASS(TranslateFilter); +public: + TranslateFilter() { + qDebug() << __FUNCTION__; + } + TranslateFilter(glm::vec3 translate) : _translate(translate) { + qDebug() << __FUNCTION__ << "translate:" << translate; + } + + virtual float apply(float value) const override { + return value; + } + + virtual Pose apply(Pose value) const override { + return value.transform(glm::translate(_translate)); + } + + virtual bool parseParameters(const QJsonValue& parameters) override; + +private: + glm::vec3 _translate { 0.0f }; +}; + +} + +#endif diff --git a/script-archive/controllers/puppetFeet3.js b/script-archive/controllers/puppetFeet3.js new file mode 100644 index 0000000000..8fa7d5c632 --- /dev/null +++ b/script-archive/controllers/puppetFeet3.js @@ -0,0 +1,24 @@ +// +// puppetFeet3.js +// examples/controllers +// +// Created by Brad Hefta-Gaub on 2017/04/11 +// Copyright 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 +// + + +var MAPPING_NAME = "com.highfidelity.examples.puppetFeet3"; +var mapping = Controller.newMapping(MAPPING_NAME); +var puppetOffset = { x: 0, y: -1, z: 0 }; + +mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot); + +Controller.enableMapping(MAPPING_NAME); + + +Script.scriptEnding.connect(function(){ + mapping.disable(); +}); From 82166f494738a6338d53750a92f79dd1ce1fabe0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Apr 2017 14:06:36 -0700 Subject: [PATCH 004/292] add rotate and transform filters for poses --- .../src/controllers/impl/Filter.cpp | 65 +++++++++++++++++++ .../controllers/src/controllers/impl/Filter.h | 2 + .../controllers/impl/RouteBuilderProxy.cpp | 13 +++- .../src/controllers/impl/RouteBuilderProxy.h | 2 + .../controllers/impl/filters/RotateFilter.cpp | 21 ++++++ .../controllers/impl/filters/RotateFilter.h | 40 ++++++++++++ .../impl/filters/TransformFilter.cpp | 21 ++++++ .../impl/filters/TransformFilter.h | 41 ++++++++++++ .../impl/filters/TranslateFilter.cpp | 4 +- .../impl/filters/TranslateFilter.h | 10 +-- script-archive/controllers/puppetFeet3.js | 12 +++- 11 files changed, 218 insertions(+), 13 deletions(-) create mode 100644 libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/RotateFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/TransformFilter.h diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index a75c43463e..12e666825b 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -25,6 +25,7 @@ #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" +#include "filters/TransformFilter.h" using namespace controller; @@ -39,6 +40,7 @@ REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") +REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform") const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); @@ -112,6 +114,69 @@ bool Filter::parseVec3Parameter(const QJsonValue& parameters, const QString& nam return false; } +bool Filter::parseMat4Parameter(const QJsonValue& parameters, const QString& name, glm::mat4& output) { + if (parameters.isObject()) { + auto objectParameters = parameters.toObject(); + + + if (objectParameters.contains("r0c0") && + objectParameters.contains("r1c0") && + objectParameters.contains("r2c0") && + objectParameters.contains("r3c0") && + objectParameters.contains("r0c1") && + objectParameters.contains("r1c1") && + objectParameters.contains("r2c1") && + objectParameters.contains("r3c1") && + objectParameters.contains("r0c2") && + objectParameters.contains("r1c2") && + objectParameters.contains("r2c2") && + objectParameters.contains("r3c2") && + objectParameters.contains("r0c3") && + objectParameters.contains("r1c3") && + objectParameters.contains("r2c3") && + objectParameters.contains("r3c3")) { + + output[0][0] = objectParameters["r0c0"].toDouble(); + output[0][1] = objectParameters["r1c0"].toDouble(); + output[0][2] = objectParameters["r2c0"].toDouble(); + output[0][3] = objectParameters["r3c0"].toDouble(); + output[1][0] = objectParameters["r0c1"].toDouble(); + output[1][1] = objectParameters["r1c1"].toDouble(); + output[1][2] = objectParameters["r2c1"].toDouble(); + output[1][3] = objectParameters["r3c1"].toDouble(); + output[2][0] = objectParameters["r0c2"].toDouble(); + output[2][1] = objectParameters["r1c2"].toDouble(); + output[2][2] = objectParameters["r2c2"].toDouble(); + output[2][3] = objectParameters["r3c2"].toDouble(); + output[3][0] = objectParameters["r0c3"].toDouble(); + output[3][1] = objectParameters["r1c3"].toDouble(); + output[3][2] = objectParameters["r2c3"].toDouble(); + output[3][3] = objectParameters["r3c3"].toDouble(); + + return true; + } + } + return false; +} + +bool Filter::parseQuatParameter(const QJsonValue& parameters, const QString& name, glm::quat& output) { + if (parameters.isObject()) { + auto objectParameters = parameters.toObject(); + if (objectParameters.contains("w") && + objectParameters.contains("x") && + objectParameters.contains("y") && + objectParameters.contains("z")) { + + output = glm::quat(objectParameters["w"].toDouble(), + objectParameters["x"].toDouble(), + objectParameters["y"].toDouble(), + objectParameters["z"].toDouble()); + return true; + } + } + return false; +} + #if 0 diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index c268a227d7..d20a3d08e3 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -47,6 +47,8 @@ namespace controller { static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); static bool parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output); + static bool parseMat4Parameter(const QJsonValue& parameters, const QString& name, glm::mat4& output); + static bool parseQuatParameter(const QJsonValue& parameters, const QString& name, glm::quat& output); protected: static Factory _factory; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 93ec8d4acd..fa662dcc13 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -27,6 +27,8 @@ #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" +#include "filters/TransformFilter.h" +#include "filters/RotateFilter.h" #include "conditionals/AndConditional.h" using namespace controller; @@ -105,11 +107,20 @@ QObject* RouteBuilderProxy::deadZone(float min) { } QObject* RouteBuilderProxy::translate(glm::vec3 translate) { - qDebug() << __FUNCTION__ << "translate:" << translate; addFilter(std::make_shared(translate)); return this; } +QObject* RouteBuilderProxy::transform(glm::mat4 transform) { + addFilter(std::make_shared(transform)); + return this; +} + +QObject* RouteBuilderProxy::rotate(glm::quat rotation) { + addFilter(std::make_shared(rotation)); + return this; +} + QObject* RouteBuilderProxy::constrainToInteger() { addFilter(std::make_shared()); return this; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 0d3ddd55fb..7696eefebe 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -49,6 +49,8 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger(); Q_INVOKABLE QObject* translate(glm::vec3 translate); + Q_INVOKABLE QObject* transform(glm::mat4 transform); + Q_INVOKABLE QObject* rotate(glm::quat rotation); private: void to(const Endpoint::Pointer& destination); diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp b/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp new file mode 100644 index 0000000000..b5a94ba492 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp @@ -0,0 +1,21 @@ +// +// Created by Brad Hefta-Gaub 2017/04/11 +// Copyright 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 "RotateFilter.h" + +#include +#include + +#include + +using namespace controller; + +bool RotateFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_ROTATION = QStringLiteral("rotation"); + return parseQuatParameter(parameters, JSON_ROTATION, _rotation); +} diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h new file mode 100644 index 0000000000..a5c3b10655 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h @@ -0,0 +1,40 @@ +// +// Created by Brad Hefta-Gaub 2017/04/11 +// Copyright 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 +// + +#pragma once +#ifndef hifi_Controllers_Filters_Rotate_h +#define hifi_Controllers_Filters_Rotate_h + +#include + +#include "../Filter.h" + +namespace controller { + +class RotateFilter : public Filter { + REGISTER_FILTER_CLASS(RotateFilter); +public: + RotateFilter() { } + RotateFilter(glm::quat rotation) : _rotation(rotation) {} + + virtual float apply(float value) const override { return value; } + + virtual Pose apply(Pose value) const override { + glm::quat temp = _rotation; + return value.transform(glm::mat4(temp)); + } + + virtual bool parseParameters(const QJsonValue& parameters) override; + +private: + glm::quat _rotation; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp new file mode 100644 index 0000000000..71568b4c29 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TransformFilter.h" + +#include +#include + +#include + +using namespace controller; + +bool TransformFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_TRANSFORM = QStringLiteral("transform"); + return parseMat4Parameter(parameters, JSON_TRANSFORM, _transform); +} diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h new file mode 100644 index 0000000000..9999fd03c7 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h @@ -0,0 +1,41 @@ +// +// Created by Brad Hefta-Gaub 2017/04/11 +// Copyright 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 +// + +#pragma once +#ifndef hifi_Controllers_Filters_Transform_h +#define hifi_Controllers_Filters_Transform_h + +#include + +#include "../Filter.h" + +namespace controller { + +class TransformFilter : public Filter { + REGISTER_FILTER_CLASS(TransformFilter); +public: + TransformFilter() { } + TransformFilter(glm::mat4 transform) : _transform(transform) {} + + virtual float apply(float value) const override { + return value; + } + + virtual Pose apply(Pose value) const override { + return value.transform(_transform); + } + + virtual bool parseParameters(const QJsonValue& parameters) override; + +private: + glm::mat4 _transform; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp index 6f7e1e6a45..e63afcdd9e 100644 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp @@ -17,7 +17,5 @@ using namespace controller; bool TranslateFilter::parseParameters(const QJsonValue& parameters) { static const QString JSON_TRANSLATE = QStringLiteral("translate"); - bool result = parseVec3Parameter(parameters, JSON_TRANSLATE, _translate); - qDebug() << __FUNCTION__ << "_translate:" << _translate; - return result; + return parseVec3Parameter(parameters, JSON_TRANSLATE, _translate); } diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h index 504e4c3151..d5ac417b98 100644 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h @@ -12,8 +12,6 @@ #include -#include - #include "../Filter.h" namespace controller { @@ -21,12 +19,8 @@ namespace controller { class TranslateFilter : public Filter { REGISTER_FILTER_CLASS(TranslateFilter); public: - TranslateFilter() { - qDebug() << __FUNCTION__; - } - TranslateFilter(glm::vec3 translate) : _translate(translate) { - qDebug() << __FUNCTION__ << "translate:" << translate; - } + TranslateFilter() { } + TranslateFilter(glm::vec3 translate) : _translate(translate) {} virtual float apply(float value) const override { return value; diff --git a/script-archive/controllers/puppetFeet3.js b/script-archive/controllers/puppetFeet3.js index 8fa7d5c632..3c9618edd9 100644 --- a/script-archive/controllers/puppetFeet3.js +++ b/script-archive/controllers/puppetFeet3.js @@ -14,7 +14,17 @@ var MAPPING_NAME = "com.highfidelity.examples.puppetFeet3"; var mapping = Controller.newMapping(MAPPING_NAME); var puppetOffset = { x: 0, y: -1, z: 0 }; -mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot); +var rotation = Quat.fromPitchYawRollDegrees(0, 0, -90); +var noTranslation = { x: 0, y: 0, z: 0 }; +var transformMatrix = Mat4.createFromRotAndTrans(rotation, noTranslation); +var rotateAndTranslate = Mat4.createFromRotAndTrans(rotation, puppetOffset); + + +mapping.from(Controller.Standard.LeftHand).peek().rotate(rotation).translate(puppetOffset).to(Controller.Standard.LeftFoot); + +//mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot); +//mapping.from(Controller.Standard.LeftHand).peek().transform(transformMatrix).translate(puppetOffset).to(Controller.Standard.LeftFoot); +//mapping.from(Controller.Standard.LeftHand).peek().transform(rotateAndTranslate).to(Controller.Standard.LeftFoot); Controller.enableMapping(MAPPING_NAME); From 6daf68b338282ad68f615339bc185c48a38d64ce Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Apr 2017 14:18:21 -0700 Subject: [PATCH 005/292] tweak --- libraries/controllers/src/controllers/impl/Filter.h | 2 +- .../controllers/src/controllers/impl/filters/ClampFilter.h | 3 +++ .../src/controllers/impl/filters/ConstrainToIntegerFilter.h | 3 +++ .../impl/filters/ConstrainToPositiveIntegerFilter.h | 3 +++ .../controllers/src/controllers/impl/filters/DeadZoneFilter.h | 3 +++ .../src/controllers/impl/filters/HysteresisFilter.h | 3 +++ .../controllers/src/controllers/impl/filters/PulseFilter.h | 2 ++ 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index d20a3d08e3..2d79a28ddb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -36,7 +36,7 @@ namespace controller { using Factory = hifi::SimpleFactory; virtual float apply(float value) const = 0; - virtual Pose apply(Pose value) const { return value; } // most filters don't operate on poses + virtual Pose apply(Pose value) const = 0; // Factory features virtual bool parseParameters(const QJsonValue& parameters) { return true; } diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h index fd82821b3e..b06a43515f 100644 --- a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h @@ -21,6 +21,9 @@ public: virtual float apply(float value) const override { return glm::clamp(value, _min, _max); } + + virtual Pose apply(Pose value) const override { return value; } + virtual bool parseParameters(const QJsonValue& parameters) override; protected: float _min = 0.0f; diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h index 580dc2a856..c9a25fde72 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h @@ -22,6 +22,9 @@ public: virtual float apply(float value) const override { return glm::sign(value); } + + virtual Pose apply(Pose value) const override { return value; } + protected: }; diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h index 27395cde24..e3f4ee8929 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h @@ -22,6 +22,9 @@ public: virtual float apply(float value) const override { return (value <= 0.0f) ? 0.0f : 1.0f; } + + virtual Pose apply(Pose value) const override { return value; } + protected: }; diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h index 70ac657415..d898647126 100644 --- a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h @@ -20,6 +20,9 @@ public: DeadZoneFilter(float min = 0.0) : _min(min) {}; virtual float apply(float value) const override; + + virtual Pose apply(Pose value) const override { return value; } + virtual bool parseParameters(const QJsonValue& parameters) override; protected: float _min = 0.0f; diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h index 4f7e07928d..4eb563754f 100644 --- a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -19,6 +19,9 @@ class HysteresisFilter : public Filter { public: HysteresisFilter(float min = 0.25, float max = 0.75); virtual float apply(float value) const override; + + virtual Pose apply(Pose value) const override { return value; } + virtual bool parseParameters(const QJsonValue& parameters) override; protected: float _min; diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h index 271f4a04f6..a8c7cbf9e6 100644 --- a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -23,6 +23,8 @@ public: virtual float apply(float value) const override; + virtual Pose apply(Pose value) const override { return value; } + virtual bool parseParameters(const QJsonValue& parameters) override; private: From afe7c386c3831ddc6e75d456fad1879a20f9a818 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Apr 2017 17:03:33 -0700 Subject: [PATCH 006/292] added postTransform filter --- .../controllers/src/controllers/Pose.cpp | 19 +++++++++++ libraries/controllers/src/controllers/Pose.h | 3 ++ .../src/controllers/impl/Filter.cpp | 6 +++- .../controllers/src/controllers/impl/Filter.h | 2 +- .../controllers/impl/RouteBuilderProxy.cpp | 6 ++++ .../src/controllers/impl/RouteBuilderProxy.h | 1 + .../impl/filters/PostTransformFilter.h | 33 +++++++++++++++++++ .../impl/filters/TransformFilter.cpp | 3 +- 8 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index b86391bbba..6bcea82720 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -69,5 +69,24 @@ namespace controller { pose.valid = valid; return pose; } + + Pose Pose::postTransform(const glm::mat4& mat) const { + glm::mat4 original = getMat4(); + glm::mat4 result = original * mat; + + auto translationOut = ::extractTranslation(result); + auto rotationOut = ::glmExtractRotation(result); + auto velocityOut = velocity + glm::cross(angularVelocity, translation - translationOut); // warning: this may be completely wrong + auto angularVelocityOut = angularVelocity; + + Pose pose(translationOut, + rotationOut, + velocityOut, + angularVelocityOut); + + pose.valid = valid; + return pose; + } + } diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 47ba59279a..9e5371ce59 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -41,6 +41,9 @@ namespace controller { vec3 getAngularVelocity() const { return angularVelocity; } Pose transform(const glm::mat4& mat) const; + Pose postTransform(const glm::mat4& mat) const; + + glm::mat4 getMat4() const { return ::createMatFromQuatAndPos(rotation, translation); } static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event); diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 12e666825b..74fa5d99cc 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -26,6 +26,8 @@ #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" #include "filters/TransformFilter.h" +#include "filters/PostTransformFilter.h" +#include "filters/RotateFilter.h" using namespace controller; @@ -41,6 +43,8 @@ REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform") +REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform") +REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate") const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); @@ -114,7 +118,7 @@ bool Filter::parseVec3Parameter(const QJsonValue& parameters, const QString& nam return false; } -bool Filter::parseMat4Parameter(const QJsonValue& parameters, const QString& name, glm::mat4& output) { +bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) { if (parameters.isObject()) { auto objectParameters = parameters.toObject(); diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index 2d79a28ddb..73b0aa06f5 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -47,8 +47,8 @@ namespace controller { static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); static bool parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output); - static bool parseMat4Parameter(const QJsonValue& parameters, const QString& name, glm::mat4& output); static bool parseQuatParameter(const QJsonValue& parameters, const QString& name, glm::quat& output); + static bool parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output); protected: static Factory _factory; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index fa662dcc13..f3c447238a 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -28,6 +28,7 @@ #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" #include "filters/TransformFilter.h" +#include "filters/PostTransformFilter.h" #include "filters/RotateFilter.h" #include "conditionals/AndConditional.h" @@ -116,6 +117,11 @@ QObject* RouteBuilderProxy::transform(glm::mat4 transform) { return this; } +QObject* RouteBuilderProxy::postTransform(glm::mat4 transform) { + addFilter(std::make_shared(transform)); + return this; +} + QObject* RouteBuilderProxy::rotate(glm::quat rotation) { addFilter(std::make_shared(rotation)); return this; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 7696eefebe..de9c23d2cd 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -50,6 +50,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToPositiveInteger(); Q_INVOKABLE QObject* translate(glm::vec3 translate); Q_INVOKABLE QObject* transform(glm::mat4 transform); + Q_INVOKABLE QObject* postTransform(glm::mat4 transform); Q_INVOKABLE QObject* rotate(glm::quat rotation); private: diff --git a/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h new file mode 100644 index 0000000000..656a146ff2 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h @@ -0,0 +1,33 @@ +// +// Created by Brad Hefta-Gaub 2017/04/11 +// Copyright 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 +// + +#pragma once +#ifndef hifi_Controllers_Filters_PostTransform_h +#define hifi_Controllers_Filters_PostTransform_h + +#include + +#include "../Filter.h" + +namespace controller { + +class PostTransformFilter : public Filter { + REGISTER_FILTER_CLASS(PostTransformFilter); +public: + PostTransformFilter() { } + PostTransformFilter(glm::mat4 transform) : _transform(transform) {} + virtual float apply(float value) const override { return value; } + virtual Pose apply(Pose value) const override { return value.postTransform(_transform); } + virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); } +private: + glm::mat4 _transform; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp index 71568b4c29..2b7b0ef21d 100644 --- a/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp @@ -16,6 +16,5 @@ using namespace controller; bool TransformFilter::parseParameters(const QJsonValue& parameters) { - static const QString JSON_TRANSFORM = QStringLiteral("transform"); - return parseMat4Parameter(parameters, JSON_TRANSFORM, _transform); + return parseMat4Parameter(parameters, _transform); } From dce73ea428dfe7bbdebd05aed68ed2c4c332f79f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Apr 2017 13:43:57 -0700 Subject: [PATCH 007/292] some cleanup --- .../controllers/src/controllers/Pose.cpp | 3 +-- libraries/controllers/src/controllers/Pose.h | 2 -- .../src/controllers/impl/Filter.cpp | 5 ++--- .../controllers/src/controllers/impl/Filter.h | 4 ++-- .../controllers/impl/filters/RotateFilter.cpp | 21 ------------------- .../controllers/impl/filters/RotateFilter.h | 5 ++--- .../impl/filters/TransformFilter.cpp | 20 ------------------ .../impl/filters/TransformFilter.h | 12 +++-------- .../impl/filters/TranslateFilter.cpp | 21 ------------------- .../impl/filters/TranslateFilter.h | 12 +++-------- 10 files changed, 13 insertions(+), 92 deletions(-) delete mode 100644 libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp delete mode 100644 libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp delete mode 100644 libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 6bcea82720..0c0de95bb4 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -71,9 +71,8 @@ namespace controller { } Pose Pose::postTransform(const glm::mat4& mat) const { - glm::mat4 original = getMat4(); + glm::mat4 original = ::createMatFromQuatAndPos(rotation, translation); glm::mat4 result = original * mat; - auto translationOut = ::extractTranslation(result); auto rotationOut = ::glmExtractRotation(result); auto velocityOut = velocity + glm::cross(angularVelocity, translation - translationOut); // warning: this may be completely wrong diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 9e5371ce59..a6d1360f9f 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -43,8 +43,6 @@ namespace controller { Pose transform(const glm::mat4& mat) const; Pose postTransform(const glm::mat4& mat) const; - glm::mat4 getMat4() const { return ::createMatFromQuatAndPos(rotation, translation); } - static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event); }; diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 74fa5d99cc..2cb35d85ce 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -93,8 +93,7 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri return false; } -// FIXME - we're not really using the name -bool Filter::parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output) { +bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output) { if (parameters.isDouble()) { output = glm::vec3(parameters.toDouble()); return true; @@ -163,7 +162,7 @@ bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) return false; } -bool Filter::parseQuatParameter(const QJsonValue& parameters, const QString& name, glm::quat& output) { +bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) { if (parameters.isObject()) { auto objectParameters = parameters.toObject(); if (objectParameters.contains("w") && diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index 73b0aa06f5..cde8f991b7 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -46,8 +46,8 @@ namespace controller { static Factory& getFactory() { return _factory; } static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); - static bool parseVec3Parameter(const QJsonValue& parameters, const QString& name, glm::vec3& output); - static bool parseQuatParameter(const QJsonValue& parameters, const QString& name, glm::quat& output); + static bool parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output); + static bool parseQuatParameter(const QJsonValue& parameters, glm::quat& output); static bool parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output); protected: static Factory _factory; diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp b/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp deleted file mode 100644 index b5a94ba492..0000000000 --- a/libraries/controllers/src/controllers/impl/filters/RotateFilter.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Brad Hefta-Gaub 2017/04/11 -// Copyright 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 "RotateFilter.h" - -#include -#include - -#include - -using namespace controller; - -bool RotateFilter::parseParameters(const QJsonValue& parameters) { - static const QString JSON_ROTATION = QStringLiteral("rotation"); - return parseQuatParameter(parameters, JSON_ROTATION, _rotation); -} diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h index a5c3b10655..ee2e081393 100644 --- a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h @@ -25,11 +25,10 @@ public: virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { - glm::quat temp = _rotation; - return value.transform(glm::mat4(temp)); + return value.transform(glm::mat4(glm::quat(_rotation))); } - virtual bool parseParameters(const QJsonValue& parameters) override; + virtual bool parseParameters(const QJsonValue& parameters) override { return parseQuatParameter(parameters, _rotation); } private: glm::quat _rotation; diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp deleted file mode 100644 index 2b7b0ef21d..0000000000 --- a/libraries/controllers/src/controllers/impl/filters/TransformFilter.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// Created by Bradley Austin Davis 2015/10/25 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "TransformFilter.h" - -#include -#include - -#include - -using namespace controller; - -bool TransformFilter::parseParameters(const QJsonValue& parameters) { - return parseMat4Parameter(parameters, _transform); -} diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h index 9999fd03c7..263b70c9b4 100644 --- a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h @@ -22,15 +22,9 @@ public: TransformFilter() { } TransformFilter(glm::mat4 transform) : _transform(transform) {} - virtual float apply(float value) const override { - return value; - } - - virtual Pose apply(Pose value) const override { - return value.transform(_transform); - } - - virtual bool parseParameters(const QJsonValue& parameters) override; + virtual float apply(float value) const override { return value; } + virtual Pose apply(Pose value) const override { return value.transform(_transform); } + virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); } private: glm::mat4 _transform; diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp deleted file mode 100644 index e63afcdd9e..0000000000 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by Bradley Austin Davis 2015/10/25 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "TranslateFilter.h" - -#include -#include - -#include - -using namespace controller; - -bool TranslateFilter::parseParameters(const QJsonValue& parameters) { - static const QString JSON_TRANSLATE = QStringLiteral("translate"); - return parseVec3Parameter(parameters, JSON_TRANSLATE, _translate); -} diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h index d5ac417b98..eda2912a8a 100644 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h @@ -22,15 +22,9 @@ public: TranslateFilter() { } TranslateFilter(glm::vec3 translate) : _translate(translate) {} - virtual float apply(float value) const override { - return value; - } - - virtual Pose apply(Pose value) const override { - return value.transform(glm::translate(_translate)); - } - - virtual bool parseParameters(const QJsonValue& parameters) override; + virtual float apply(float value) const override { return value; } + virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); } + virtual bool parseParameters(const QJsonValue& parameters) override { return parseVec3Parameter(parameters, _translate); } private: glm::vec3 _translate { 0.0f }; From 8ff1ca8af6b933e2cf1be30b97a1f6360388df09 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Apr 2017 16:11:06 -0700 Subject: [PATCH 008/292] swap the velocity components --- libraries/controllers/src/controllers/Pose.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 0c0de95bb4..fc0891799e 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -75,7 +75,7 @@ namespace controller { glm::mat4 result = original * mat; auto translationOut = ::extractTranslation(result); auto rotationOut = ::glmExtractRotation(result); - auto velocityOut = velocity + glm::cross(angularVelocity, translation - translationOut); // warning: this may be completely wrong + auto velocityOut = velocity + glm::cross(angularVelocity, translationOut - translation); // warning: this may be completely wrong auto angularVelocityOut = angularVelocity; Pose pose(translationOut, From 6b96cfad42f2ab88dd07dad1fb3880d217de96ee Mon Sep 17 00:00:00 2001 From: Trevor Berninger Date: Thu, 13 Apr 2017 13:04:28 -0700 Subject: [PATCH 009/292] 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 321d281919eaacec5b9f608bbe73098ab6b3ca15 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Fri, 21 Apr 2017 01:46:44 -0700 Subject: [PATCH 010/292] Separating job/task classes from the render specific --- libraries/render/src/render/Context.h | 20 ++++++- libraries/render/src/render/Task.h | 84 +++++++++++++++++---------- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/libraries/render/src/render/Context.h b/libraries/render/src/render/Context.h index cb0fc65d40..c493bda4b2 100644 --- a/libraries/render/src/render/Context.h +++ b/libraries/render/src/render/Context.h @@ -14,14 +14,28 @@ #include "Scene.h" + +namespace task { + +class JobConfig; + +class JobContext { +public: + virtual ~JobContext() {} + + std::shared_ptr jobConfig{ nullptr }; +}; +using JobContextPointer = std::shared_ptr; +} + namespace render { - class JobConfig; -class RenderContext { +class RenderContext : public task::JobContext { public: + virtual ~RenderContext() {} + RenderArgs* args; - std::shared_ptr jobConfig{ nullptr }; ScenePointer _scene; }; using RenderContextPointer = std::shared_ptr; diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 1035b74340..a4d60b25cc 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -28,13 +28,9 @@ #include "gpu/Batch.h" #include -namespace render { - -class Varying; - +namespace task { // A varying piece of data, to be used as Job/Task I/O -// TODO: Task IO class Varying { public: Varying() {} @@ -300,9 +296,9 @@ public: } }; -class Job; class JobConcept; -class Task; +template class JobT; +template class TaskT; class JobNoIO {}; template class PersistentConfig : public C { @@ -433,13 +429,11 @@ public: void connectChildConfig(QConfigPointer childConfig, const std::string& name); void transferChildrenConfigs(QConfigPointer source); + + JobConcept* _task; public slots: void refresh(); - -private: - friend Task; - JobConcept* _task; }; template void jobConfigure(T& data, const C& configuration) { @@ -451,16 +445,17 @@ template void jobConfigure(T&, const JobConfig&) { template void jobConfigure(T&, const TaskConfig&) { // nop, as the default TaskConfig was used, so the data does not need a configure method } -template void jobRun(T& data, const RenderContextPointer& renderContext, const JobNoIO& input, JobNoIO& output) { + +template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, JobNoIO& output) { data.run(renderContext); } -template void jobRun(T& data, const RenderContextPointer& renderContext, const I& input, JobNoIO& output) { +template void jobRun(T& data, const RC& renderContext, const I& input, JobNoIO& output) { data.run(renderContext, input); } -template void jobRun(T& data, const RenderContextPointer& renderContext, const JobNoIO& input, O& output) { +template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, O& output) { data.run(renderContext, output); } -template void jobRun(T& data, const RenderContextPointer& renderContext, const I& input, O& output) { +template void jobRun(T& data, const RC& renderContext, const I& input, O& output) { data.run(renderContext, input, output); } @@ -479,22 +474,29 @@ public: virtual QConfigPointer& getConfiguration() { return _config; } virtual void applyConfiguration() = 0; - virtual void run(const RenderContextPointer& renderContext) = 0; - -protected: void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } QConfigPointer _config; - - friend class Job; +protected: }; +template class Job { public: - using Concept = JobConcept; + using Context = RC; + using ContextPointer = std::shared_ptr; using Config = JobConfig; using QConfigPointer = std::shared_ptr; using None = JobNoIO; + + //template + class Concept : public JobConcept { + public: + Concept(QConfigPointer config) : JobConcept(config) {} + virtual ~Concept() = default; + + virtual void run(const ContextPointer& renderContext) = 0; + }; using ConceptPointer = std::shared_ptr; template class Model : public Concept { @@ -529,7 +531,7 @@ public: jobConfigure(_data, *std::static_pointer_cast(_config)); } - void run(const RenderContextPointer& renderContext) override { + void run(const ContextPointer& renderContext) override { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { jobRun(_data, renderContext, _input.get(), _output.edit()); @@ -554,7 +556,7 @@ public: return concept->_data; } - void run(const RenderContextPointer& renderContext) { + virtual void run(const ContextPointer& renderContext) { PerformanceTimer perfTimer(_name.c_str()); PROFILE_RANGE(render, _name.c_str()); auto start = usecTimestampNow(); @@ -576,13 +578,17 @@ protected: // The build method is where child Jobs can be added internally to the task // where the input of the task can be setup to feed the child jobs // and where the output of the task is defined -class Task : public Job { +template +class Task : public Job { public: + using Context = RC; + using ContextPointer = std::shared_ptr; using Config = TaskConfig; - using QConfigPointer = Job::QConfigPointer; - using None = Job::None; - using Concept = Job::Concept; - using Jobs = std::vector; + using _Job = Job; + using QConfigPointer = _Job::QConfigPointer; + using None = _Job::None; + using Concept = _Job::Concept; + using Jobs = std::vector<_Job>; Task(std::string name, ConceptPointer concept) : Job(name, concept) {} @@ -627,7 +633,6 @@ public: template static std::shared_ptr create(const Varying& input, A&&... args) { auto model = std::make_shared(input, std::make_shared()); - // std::static_pointer_cast(model->_config)->_task = model.get(); model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); @@ -669,7 +674,7 @@ public: } } - void run(const RenderContextPointer& renderContext) override { + void run(const ContextPointer& renderContext) override { auto config = std::static_pointer_cast(_config); if (config->alwaysEnabled || config->enabled) { for (auto job : _jobs) { @@ -698,6 +703,25 @@ public: protected: }; +} + +namespace render { +using JobConfig =task::JobConfig; +using TaskConfig =task::TaskConfig; + +template using PersistentConfig = task::PersistentConfig; + +using Job = task::Job; +using Task = task::Task; + +using Varying = task::Varying; +template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2; +template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3; +template < typename T0, typename T1, typename T2, typename T3 > using VaryingSet4 = task::VaryingSet4; +template < typename T0, typename T1, typename T2, typename T3, typename T4 > using VaryingSet5 = task::VaryingSet5; +template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5 > using VaryingSet6 = task::VaryingSet6; +template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > using VaryingSet7 = task::VaryingSet7; +template < class T, int NUM > using VaryingArray = task::VaryingArray; // Versions of the COnfig integrating a gpu & batch timer class GPUJobConfig : public JobConfig { From 1e6b2682c96ce8da9fb1c1a2140c8cf21b4711bc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Apr 2017 10:17:57 -0700 Subject: [PATCH 011/292] 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 012/292] 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 8e73fa3eb4a3855a3fe13319c5ba14ca46d0d299 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 21 Apr 2017 18:16:07 -0700 Subject: [PATCH 013/292] Separating the task classes in their own folder, and easing the declaration --- .../render-utils/src/DeferredLightingEffect.h | 1 - .../render-utils/src/RenderShadowTask.cpp | 1 - libraries/render/src/render/Context.h | 46 -- libraries/render/src/render/Engine.h | 61 +- .../src/render/RenderFetchCullSortTask.h | 2 - libraries/render/src/render/Task.h | 769 ------------------ .../src/{render/Task.cpp => task/Config.cpp} | 10 +- libraries/render/src/task/Config.h | 169 ++++ libraries/render/src/task/Task.h | 326 ++++++++ libraries/render/src/task/Varying.h | 287 +++++++ tests/gpu-test/src/TestWindow.h | 2 +- 11 files changed, 847 insertions(+), 827 deletions(-) delete mode 100644 libraries/render/src/render/Context.h delete mode 100644 libraries/render/src/render/Task.h rename libraries/render/src/{render/Task.cpp => task/Config.cpp} (95%) create mode 100644 libraries/render/src/task/Config.h create mode 100644 libraries/render/src/task/Task.h create mode 100644 libraries/render/src/task/Varying.h diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index c5e694cb18..dcf0c84622 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -20,7 +20,6 @@ #include "model/Light.h" #include "model/Geometry.h" -#include "render/Context.h" #include #include "DeferredFrameTransform.h" diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 0a4371cccd..d7ec087174 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -15,7 +15,6 @@ #include -#include #include #include #include diff --git a/libraries/render/src/render/Context.h b/libraries/render/src/render/Context.h deleted file mode 100644 index c493bda4b2..0000000000 --- a/libraries/render/src/render/Context.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Context.h -// render/src/render -// -// Created by Zach Pomerantz on 1/6/2015. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_render_Context_h -#define hifi_render_Context_h - -#include "Scene.h" - - -namespace task { - -class JobConfig; - -class JobContext { -public: - virtual ~JobContext() {} - - std::shared_ptr jobConfig{ nullptr }; -}; -using JobContextPointer = std::shared_ptr; -} - -namespace render { - - -class RenderContext : public task::JobContext { -public: - virtual ~RenderContext() {} - - RenderArgs* args; - ScenePointer _scene; -}; -using RenderContextPointer = std::shared_ptr; - -} - -#endif // hifi_render_Context_h - diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 68d86e6c16..d4dba0e340 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -14,10 +14,67 @@ #include -#include "Context.h" -#include "Task.h" +#include "Scene.h" +#include "../task/Task.h" +#include "gpu/Batch.h" + namespace render { + + + class RenderContext : public task::JobContext { + public: + virtual ~RenderContext() {} + + RenderArgs* args; + ScenePointer _scene; + }; + using RenderContextPointer = std::shared_ptr; + + Task_DeclareTypeAliases(RenderContext) + + // Versions of the COnfig integrating a gpu & batch timer + class GPUJobConfig : public JobConfig { + Q_OBJECT + Q_PROPERTY(double gpuRunTime READ getGPURunTime) + Q_PROPERTY(double batchRunTime READ getBatchRunTime) + + double _msGPURunTime { 0.0 }; + double _msBatchRunTime { 0.0 }; + public: + using Persistent = PersistentConfig; + + GPUJobConfig() = default; + GPUJobConfig(bool enabled) : JobConfig(enabled) {} + + // Running Time measurement on GPU and for Batch execution + void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } + double getGPURunTime() const { return _msGPURunTime; } + double getBatchRunTime() const { return _msBatchRunTime; } + }; + + class GPUTaskConfig : public TaskConfig { + Q_OBJECT + Q_PROPERTY(double gpuRunTime READ getGPURunTime) + Q_PROPERTY(double batchRunTime READ getBatchRunTime) + + double _msGPURunTime { 0.0 }; + double _msBatchRunTime { 0.0 }; + public: + + using Persistent = PersistentConfig; + + + GPUTaskConfig() = default; + GPUTaskConfig(bool enabled) : TaskConfig(enabled) {} + + // Running Time measurement on GPU and for Batch execution + void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } + double getGPURunTime() const { return _msGPURunTime; } + double getBatchRunTime() const { return _msBatchRunTime; } + }; + + // The render engine holds all render tasks, and is itself a render task. // State flows through tasks to jobs via the render and scene contexts - // the engine should not be known from its jobs. diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 12bcb9786d..f32293f001 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -13,8 +13,6 @@ #define hifi_RenderFetchCullSortTask_h #include - -#include "Task.h" #include "CullTask.h" class RenderFetchCullSortTask { diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h deleted file mode 100644 index a4d60b25cc..0000000000 --- a/libraries/render/src/render/Task.h +++ /dev/null @@ -1,769 +0,0 @@ -// -// Task.h -// render/src/render -// -// Created by Zach Pomerantz on 1/6/2016. -// Copyright 2016 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_render_Task_h -#define hifi_render_Task_h -#include - -#include - -#include -#include -#include -#include - -#include "SettingHandle.h" - -#include "Context.h" -#include "Logging.h" - -#include "gpu/Batch.h" -#include - -namespace task { - -// A varying piece of data, to be used as Job/Task I/O -class Varying { -public: - Varying() {} - Varying(const Varying& var) : _concept(var._concept) {} - Varying& operator=(const Varying& var) { - _concept = var._concept; - return (*this); - } - template Varying(const T& data) : _concept(std::make_shared>(data)) {} - - template bool canCast() const { return !!std::dynamic_pointer_cast>(_concept); } - template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } - template T& edit() { return std::static_pointer_cast>(_concept)->_data; } - - - // access potential sub varyings contained in this one. - Varying operator[] (uint8_t index) const { return (*_concept)[index]; } - uint8_t length() const { return (*_concept).length(); } - - template Varying getN (uint8_t index) const { return get()[index]; } - template Varying editN (uint8_t index) { return edit()[index]; } - -protected: - class Concept { - public: - virtual ~Concept() = default; - - virtual Varying operator[] (uint8_t index) const = 0; - virtual uint8_t length() const = 0; - }; - template class Model : public Concept { - public: - using Data = T; - - Model(const Data& data) : _data(data) {} - virtual ~Model() = default; - - virtual Varying operator[] (uint8_t index) const override { - Varying var; - return var; - } - virtual uint8_t length() const override { return 0; } - - Data _data; - }; - - std::shared_ptr _concept; -}; - -using VaryingPairBase = std::pair; -template < typename T0, typename T1 > -class VaryingSet2 : public VaryingPairBase { -public: - using Parent = VaryingPairBase; - typedef void is_proxy_tag; - - VaryingSet2() : Parent(Varying(T0()), Varying(T1())) {} - VaryingSet2(const VaryingSet2& pair) : Parent(pair.first, pair.second) {} - VaryingSet2(const Varying& first, const Varying& second) : Parent(first, second) {} - - const T0& get0() const { return first.get(); } - T0& edit0() { return first.edit(); } - - const T1& get1() const { return second.get(); } - T1& edit1() { return second.edit(); } - - virtual Varying operator[] (uint8_t index) const { - if (index == 1) { - return std::get<1>((*this)); - } else { - return std::get<0>((*this)); - } - } - virtual uint8_t length() const { return 2; } - - Varying hasVarying() const { return Varying((*this)); } -}; - - -template -class VaryingSet3 : public std::tuple{ -public: - using Parent = std::tuple; - - VaryingSet3() : Parent(Varying(T0()), Varying(T1()), Varying(T2())) {} - VaryingSet3(const VaryingSet3& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src)) {} - VaryingSet3(const Varying& first, const Varying& second, const Varying& third) : Parent(first, second, third) {} - - const T0& get0() const { return std::get<0>((*this)).template get(); } - T0& edit0() { return std::get<0>((*this)).template edit(); } - - const T1& get1() const { return std::get<1>((*this)).template get(); } - T1& edit1() { return std::get<1>((*this)).template edit(); } - - const T2& get2() const { return std::get<2>((*this)).template get(); } - T2& edit2() { return std::get<2>((*this)).template edit(); } - - virtual Varying operator[] (uint8_t index) const { - if (index == 2) { - return std::get<2>((*this)); - } else if (index == 1) { - return std::get<1>((*this)); - } else { - return std::get<0>((*this)); - } - } - virtual uint8_t length() const { return 3; } - - Varying hasVarying() const { return Varying((*this)); } -}; - -template -class VaryingSet4 : public std::tuple{ -public: - using Parent = std::tuple; - - VaryingSet4() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3())) {} - VaryingSet4(const VaryingSet4& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src)) {} - VaryingSet4(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth) : Parent(first, second, third, fourth) {} - - const T0& get0() const { return std::get<0>((*this)).template get(); } - T0& edit0() { return std::get<0>((*this)).template edit(); } - - const T1& get1() const { return std::get<1>((*this)).template get(); } - T1& edit1() { return std::get<1>((*this)).template edit(); } - - const T2& get2() const { return std::get<2>((*this)).template get(); } - T2& edit2() { return std::get<2>((*this)).template edit(); } - - const T3& get3() const { return std::get<3>((*this)).template get(); } - T3& edit3() { return std::get<3>((*this)).template edit(); } - - virtual Varying operator[] (uint8_t index) const { - if (index == 3) { - return std::get<3>((*this)); - } else if (index == 2) { - return std::get<2>((*this)); - } else if (index == 1) { - return std::get<1>((*this)); - } else { - return std::get<0>((*this)); - } - } - virtual uint8_t length() const { return 4; } - - Varying hasVarying() const { return Varying((*this)); } -}; - - -template -class VaryingSet5 : public std::tuple{ -public: - using Parent = std::tuple; - - VaryingSet5() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4())) {} - VaryingSet5(const VaryingSet5& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src)) {} - VaryingSet5(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth) : Parent(first, second, third, fourth, fifth) {} - - const T0& get0() const { return std::get<0>((*this)).template get(); } - T0& edit0() { return std::get<0>((*this)).template edit(); } - - const T1& get1() const { return std::get<1>((*this)).template get(); } - T1& edit1() { return std::get<1>((*this)).template edit(); } - - const T2& get2() const { return std::get<2>((*this)).template get(); } - T2& edit2() { return std::get<2>((*this)).template edit(); } - - const T3& get3() const { return std::get<3>((*this)).template get(); } - T3& edit3() { return std::get<3>((*this)).template edit(); } - - const T4& get4() const { return std::get<4>((*this)).template get(); } - T4& edit4() { return std::get<4>((*this)).template edit(); } - - virtual Varying operator[] (uint8_t index) const { - if (index == 4) { - return std::get<4>((*this)); - } else if (index == 3) { - return std::get<3>((*this)); - } else if (index == 2) { - return std::get<2>((*this)); - } else if (index == 1) { - return std::get<1>((*this)); - } else { - return std::get<0>((*this)); - } - } - virtual uint8_t length() const { return 5; } - - Varying hasVarying() const { return Varying((*this)); } -}; - -template -class VaryingSet6 : public std::tuple{ -public: - using Parent = std::tuple; - - VaryingSet6() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5())) {} - VaryingSet6(const VaryingSet6& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src)) {} - VaryingSet6(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth) : Parent(first, second, third, fourth, fifth, sixth) {} - - const T0& get0() const { return std::get<0>((*this)).template get(); } - T0& edit0() { return std::get<0>((*this)).template edit(); } - - const T1& get1() const { return std::get<1>((*this)).template get(); } - T1& edit1() { return std::get<1>((*this)).template edit(); } - - const T2& get2() const { return std::get<2>((*this)).template get(); } - T2& edit2() { return std::get<2>((*this)).template edit(); } - - const T3& get3() const { return std::get<3>((*this)).template get(); } - T3& edit3() { return std::get<3>((*this)).template edit(); } - - const T4& get4() const { return std::get<4>((*this)).template get(); } - T4& edit4() { return std::get<4>((*this)).template edit(); } - - const T5& get5() const { return std::get<5>((*this)).template get(); } - T5& edit5() { return std::get<5>((*this)).template edit(); } - - Varying hasVarying() const { return Varying((*this)); } -}; - -template -class VaryingSet7 : public std::tuple{ -public: - using Parent = std::tuple; - - VaryingSet7() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5()), Varying(T6())) {} - VaryingSet7(const VaryingSet7& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src), std::get<6>(src)) {} - VaryingSet7(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth, const Varying& seventh) : Parent(first, second, third, fourth, fifth, sixth, seventh) {} - - const T0& get0() const { return std::get<0>((*this)).template get(); } - T0& edit0() { return std::get<0>((*this)).template edit(); } - - const T1& get1() const { return std::get<1>((*this)).template get(); } - T1& edit1() { return std::get<1>((*this)).template edit(); } - - const T2& get2() const { return std::get<2>((*this)).template get(); } - T2& edit2() { return std::get<2>((*this)).template edit(); } - - const T3& get3() const { return std::get<3>((*this)).template get(); } - T3& edit3() { return std::get<3>((*this)).template edit(); } - - const T4& get4() const { return std::get<4>((*this)).template get(); } - T4& edit4() { return std::get<4>((*this)).template edit(); } - - const T5& get5() const { return std::get<5>((*this)).template get(); } - T5& edit5() { return std::get<5>((*this)).template edit(); } - - const T6& get6() const { return std::get<6>((*this)).template get(); } - T6& edit6() { return std::get<6>((*this)).template edit(); } - - Varying hasVarying() const { return Varying((*this)); } -}; - - -template < class T, int NUM > -class VaryingArray : public std::array { -public: - VaryingArray() { - for (size_t i = 0; i < NUM; i++) { - (*this)[i] = Varying(T()); - } - } -}; - -class JobConcept; -template class JobT; -template class TaskT; -class JobNoIO {}; - -template class PersistentConfig : public C { -public: - const QString DEFAULT = "Default"; - const QString NONE = "None"; - - PersistentConfig() = delete; - PersistentConfig(const QString& path) : - _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } - PersistentConfig(const QStringList& path) : - _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } - PersistentConfig(const QString& path, bool enabled) : C(enabled), - _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } - PersistentConfig(const QStringList& path, bool enabled) : C(enabled), - _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } - - QStringList getPresetList() { - if (_presets.empty()) { - setPresetList(QJsonObject()); - } - return _presets.keys(); - } - - virtual void setPresetList(const QJsonObject& list) override { - assert(_presets.empty()); - - _default = toJsonValue(*this).toObject().toVariantMap(); - - _presets.unite(list.toVariantMap()); - if (C::alwaysEnabled || C::enabled) { - _presets.insert(DEFAULT, _default); - } - if (!C::alwaysEnabled) { - _presets.insert(NONE, QVariantMap{{ "enabled", false }}); - } - - auto preset = _preset.get(); - if (preset != _preset.getDefault() && _presets.contains(preset)) { - // Load the persisted configuration - C::load(_presets[preset].toMap()); - } - } - - QString getPreset() { return _preset.get(); } - - void setPreset(const QString& preset) { - _preset.set(preset); - if (_presets.contains(preset)) { - // Always start back at default to remain deterministic - QVariantMap config = _default; - QVariantMap presetConfig = _presets[preset].toMap(); - for (auto it = presetConfig.cbegin(); it != presetConfig.cend(); it++) { - config.insert(it.key(), it.value()); - } - C::load(config); - } - } - -protected: - QVariantMap _default; - QVariantMap _presets; - Setting::Handle _preset; -}; - -// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled) -class JobConfig : public QObject { - Q_OBJECT - Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) - - double _msCPURunTime{ 0.0 }; -public: - using Persistent = PersistentConfig; - - JobConfig() = default; - JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} - - bool isEnabled() { return alwaysEnabled || enabled; } - void setEnabled(bool enable) { enabled = alwaysEnabled || enable; } - - bool alwaysEnabled{ true }; - bool enabled{ true }; - - virtual void setPresetList(const QJsonObject& object) { - for (auto it = object.begin(); it != object.end(); it++) { - JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); - if (child) { - child->setPresetList(it.value().toObject()); - } - } - } - - // This must be named toJSON to integrate with the global scripting JSON object - Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } - Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } - - // Running Time measurement - // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated - void setCPURunTime(double mstime) { _msCPURunTime = mstime; emit newStats(); } - double getCPURunTime() const { return _msCPURunTime; } - -public slots: - void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } - -signals: - void loaded(); - void newStats(); -}; - -class TaskConfig : public JobConfig { - Q_OBJECT -public: - using QConfigPointer = std::shared_ptr; - - using Persistent = PersistentConfig; - - TaskConfig() = default ; - TaskConfig(bool enabled) : JobConfig(enabled) {} - - // getter for qml integration, prefer the templated getter - Q_INVOKABLE QObject* getConfig(const QString& name) { return QObject::findChild(name); } - // getter for cpp (strictly typed), prefer this getter - template typename T::Config* getConfig(std::string job = "") const { - QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string - return findChild(name); - } - - void connectChildConfig(QConfigPointer childConfig, const std::string& name); - void transferChildrenConfigs(QConfigPointer source); - - JobConcept* _task; - -public slots: - void refresh(); -}; - -template void jobConfigure(T& data, const C& configuration) { - data.configure(configuration); -} -template void jobConfigure(T&, const JobConfig&) { - // nop, as the default JobConfig was used, so the data does not need a configure method -} -template void jobConfigure(T&, const TaskConfig&) { - // nop, as the default TaskConfig was used, so the data does not need a configure method -} - -template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, JobNoIO& output) { - data.run(renderContext); -} -template void jobRun(T& data, const RC& renderContext, const I& input, JobNoIO& output) { - data.run(renderContext, input); -} -template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, O& output) { - data.run(renderContext, output); -} -template void jobRun(T& data, const RC& renderContext, const I& input, O& output) { - data.run(renderContext, input, output); -} - -// The guts of a job -class JobConcept { -public: - using Config = JobConfig; - using QConfigPointer = std::shared_ptr; - - JobConcept(QConfigPointer config) : _config(config) {} - virtual ~JobConcept() = default; - - virtual const Varying getInput() const { return Varying(); } - virtual const Varying getOutput() const { return Varying(); } - - virtual QConfigPointer& getConfiguration() { return _config; } - virtual void applyConfiguration() = 0; - - void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } - - QConfigPointer _config; -protected: -}; - -template -class Job { -public: - using Context = RC; - using ContextPointer = std::shared_ptr; - using Config = JobConfig; - using QConfigPointer = std::shared_ptr; - using None = JobNoIO; - - //template - class Concept : public JobConcept { - public: - Concept(QConfigPointer config) : JobConcept(config) {} - virtual ~Concept() = default; - - virtual void run(const ContextPointer& renderContext) = 0; - }; - using ConceptPointer = std::shared_ptr; - - template class Model : public Concept { - public: - using Data = T; - using Input = I; - using Output = O; - - Data _data; - Varying _input; - Varying _output; - - const Varying getInput() const override { return _input; } - const Varying getOutput() const override { return _output; } - - template - Model(const Varying& input, QConfigPointer config, A&&... args) : - Concept(config), - _data(Data(std::forward(args)...)), - _input(input), - _output(Output()) { - applyConfiguration(); - } - - template - static std::shared_ptr create(const Varying& input, A&&... args) { - return std::make_shared(input, std::make_shared(), std::forward(args)...); - } - - - void applyConfiguration() override { - jobConfigure(_data, *std::static_pointer_cast(_config)); - } - - void run(const ContextPointer& renderContext) override { - renderContext->jobConfig = std::static_pointer_cast(_config); - if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { - jobRun(_data, renderContext, _input.get(), _output.edit()); - } - renderContext->jobConfig.reset(); - } - }; - template using ModelI = Model; - template using ModelO = Model; - template using ModelIO = Model; - - Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} - - const Varying getInput() const { return _concept->getInput(); } - const Varying getOutput() const { return _concept->getOutput(); } - QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); } - void applyConfiguration() { return _concept->applyConfiguration(); } - - template T& edit() { - auto concept = std::static_pointer_cast(_concept); - assert(concept); - return concept->_data; - } - - virtual void run(const ContextPointer& renderContext) { - PerformanceTimer perfTimer(_name.c_str()); - PROFILE_RANGE(render, _name.c_str()); - auto start = usecTimestampNow(); - - _concept->run(renderContext); - - _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); - } - -protected: - ConceptPointer _concept; - std::string _name = ""; -}; - -// A task is a specialized job to run a collection of other jobs -// It can be created on any type T by aliasing the type JobModel in the class T -// using JobModel = Task::Model -// The class T is expected to have a "build" method acting as a constructor. -// The build method is where child Jobs can be added internally to the task -// where the input of the task can be setup to feed the child jobs -// and where the output of the task is defined -template -class Task : public Job { -public: - using Context = RC; - using ContextPointer = std::shared_ptr; - using Config = TaskConfig; - using _Job = Job; - using QConfigPointer = _Job::QConfigPointer; - using None = _Job::None; - using Concept = _Job::Concept; - using Jobs = std::vector<_Job>; - - Task(std::string name, ConceptPointer concept) : Job(name, concept) {} - - class TaskConcept : public Concept { - public: - Varying _input; - Varying _output; - Jobs _jobs; - - const Varying getInput() const override { return _input; } - const Varying getOutput() const override { return _output; } - - TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} - - // Create a new job in the container's queue; returns the job's output - template const Varying addJob(std::string name, const Varying& input, NA&&... args) { - _jobs.emplace_back(name, (NT::JobModel::create(input, std::forward(args)...))); - - // Conect the child config to this task's config - std::static_pointer_cast(getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); - - return _jobs.back().getOutput(); - } - template const Varying addJob(std::string name, NA&&... args) { - const auto input = Varying(typename NT::JobModel::Input()); - return addJob(name, input, std::forward(args)...); - } - }; - - template class TaskModel : public TaskConcept { - public: - using Data = T; - using Input = I; - using Output = O; - - Data _data; - - TaskModel(const Varying& input, QConfigPointer config) : - TaskConcept(input, config), - _data(Data()) {} - - template - static std::shared_ptr create(const Varying& input, A&&... args) { - auto model = std::make_shared(input, std::make_shared()); - - model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); - - // Recreate the Config to use the templated type - model->createConfiguration(); - model->applyConfiguration(); - - return model; - } - - template - static std::shared_ptr create(A&&... args) { - const auto input = Varying(Input()); - return create(input, std::forward(args)...); - } - - void createConfiguration() { - // A brand new config - auto config = std::make_shared(); - // Make sure we transfer the former children configs to the new config - config->transferChildrenConfigs(_config); - // swap - _config = config; - // Capture this - std::static_pointer_cast(_config)->_task = this; - } - - QConfigPointer& getConfiguration() override { - if (!_config) { - createConfiguration(); - } - return _config; - } - - void applyConfiguration() override { - jobConfigure(_data, *std::static_pointer_cast(_config)); - for (auto& job : _jobs) { - job.applyConfiguration(); - } - } - - void run(const ContextPointer& renderContext) override { - auto config = std::static_pointer_cast(_config); - if (config->alwaysEnabled || config->enabled) { - for (auto job : _jobs) { - job.run(renderContext); - } - } - } - }; - template using Model = TaskModel; - template using ModelI = TaskModel; - template using ModelO = TaskModel; - template using ModelIO = TaskModel; - - // Create a new job in the Task's queue; returns the job's output - template const Varying addJob(std::string name, const Varying& input, A&&... args) { - return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); - } - template const Varying addJob(std::string name, A&&... args) { - const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); - } - - std::shared_ptr getConfiguration() { - return std::static_pointer_cast(_concept->getConfiguration()); - } - -protected: -}; -} - -namespace render { -using JobConfig =task::JobConfig; -using TaskConfig =task::TaskConfig; - -template using PersistentConfig = task::PersistentConfig; - -using Job = task::Job; -using Task = task::Task; - -using Varying = task::Varying; -template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2; -template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3; -template < typename T0, typename T1, typename T2, typename T3 > using VaryingSet4 = task::VaryingSet4; -template < typename T0, typename T1, typename T2, typename T3, typename T4 > using VaryingSet5 = task::VaryingSet5; -template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5 > using VaryingSet6 = task::VaryingSet6; -template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > using VaryingSet7 = task::VaryingSet7; -template < class T, int NUM > using VaryingArray = task::VaryingArray; - -// Versions of the COnfig integrating a gpu & batch timer -class GPUJobConfig : public JobConfig { - Q_OBJECT - Q_PROPERTY(double gpuRunTime READ getGPURunTime) - Q_PROPERTY(double batchRunTime READ getBatchRunTime) - - double _msGPURunTime { 0.0 }; - double _msBatchRunTime { 0.0 }; -public: - using Persistent = PersistentConfig; - - GPUJobConfig() = default; - GPUJobConfig(bool enabled) : JobConfig(enabled) {} - - // Running Time measurement on GPU and for Batch execution - void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } - double getGPURunTime() const { return _msGPURunTime; } - double getBatchRunTime() const { return _msBatchRunTime; } -}; - -class GPUTaskConfig : public TaskConfig { - Q_OBJECT - Q_PROPERTY(double gpuRunTime READ getGPURunTime) - Q_PROPERTY(double batchRunTime READ getBatchRunTime) - - double _msGPURunTime { 0.0 }; - double _msBatchRunTime { 0.0 }; -public: - - using Persistent = PersistentConfig; - - - GPUTaskConfig() = default; - GPUTaskConfig(bool enabled) : TaskConfig(enabled) {} - - // Running Time measurement on GPU and for Batch execution - void setGPUBatchRunTime(double msGpuTime, double msBatchTime) { _msGPURunTime = msGpuTime; _msBatchRunTime = msBatchTime; } - double getGPURunTime() const { return _msGPURunTime; } - double getBatchRunTime() const { return _msBatchRunTime; } -}; - -} - -#endif // hifi_render_Task_h diff --git a/libraries/render/src/render/Task.cpp b/libraries/render/src/task/Config.cpp similarity index 95% rename from libraries/render/src/render/Task.cpp rename to libraries/render/src/task/Config.cpp index 13476f102e..587c4319af 100644 --- a/libraries/render/src/render/Task.cpp +++ b/libraries/render/src/task/Config.cpp @@ -1,6 +1,6 @@ // -// Task.cpp -// render/src/render +// Config.cpp +// render/src/task // // Created by Zach Pomerantz on 1/21/2016. // Copyright 2016 High Fidelity, Inc. @@ -8,12 +8,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -#include +#include "Config.h" #include "Task.h" +#include -using namespace render; +using namespace task; void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::string& name) { childConfig->setParent(this); diff --git a/libraries/render/src/task/Config.h b/libraries/render/src/task/Config.h new file mode 100644 index 0000000000..9e6f060d9d --- /dev/null +++ b/libraries/render/src/task/Config.h @@ -0,0 +1,169 @@ +// +// Config.h +// render/src/render +// +// Created by Zach Pomerantz on 1/6/2016. +// Copyright 2016 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_task_Config_h +#define hifi_task_Config_h + +#include +#include +#include +#include +#include + +#include "SettingHandle.h" + +#include "Logging.h" + +#include +#include + +namespace task { + +class JobConcept; + +template class PersistentConfig : public C { +public: + const QString DEFAULT = "Default"; + const QString NONE = "None"; + + PersistentConfig() = delete; + PersistentConfig(const QString& path) : + _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } + PersistentConfig(const QStringList& path) : + _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } + PersistentConfig(const QString& path, bool enabled) : C(enabled), + _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + PersistentConfig(const QStringList& path, bool enabled) : C(enabled), + _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + + QStringList getPresetList() { + if (_presets.empty()) { + setPresetList(QJsonObject()); + } + return _presets.keys(); + } + + virtual void setPresetList(const QJsonObject& list) override { + assert(_presets.empty()); + + _default = toJsonValue(*this).toObject().toVariantMap(); + + _presets.unite(list.toVariantMap()); + if (C::alwaysEnabled || C::enabled) { + _presets.insert(DEFAULT, _default); + } + if (!C::alwaysEnabled) { + _presets.insert(NONE, QVariantMap{{ "enabled", false }}); + } + + auto preset = _preset.get(); + if (preset != _preset.getDefault() && _presets.contains(preset)) { + // Load the persisted configuration + C::load(_presets[preset].toMap()); + } + } + + QString getPreset() { return _preset.get(); } + + void setPreset(const QString& preset) { + _preset.set(preset); + if (_presets.contains(preset)) { + // Always start back at default to remain deterministic + QVariantMap config = _default; + QVariantMap presetConfig = _presets[preset].toMap(); + for (auto it = presetConfig.cbegin(); it != presetConfig.cend(); it++) { + config.insert(it.key(), it.value()); + } + C::load(config); + } + } + +protected: + QVariantMap _default; + QVariantMap _presets; + Setting::Handle _preset; +}; + +// A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled) +class JobConfig : public QObject { + Q_OBJECT + Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + + double _msCPURunTime{ 0.0 }; +public: + using Persistent = PersistentConfig; + + JobConfig() = default; + JobConfig(bool enabled) : alwaysEnabled{ false }, enabled{ enabled } {} + + bool isEnabled() { return alwaysEnabled || enabled; } + void setEnabled(bool enable) { enabled = alwaysEnabled || enable; } + + bool alwaysEnabled{ true }; + bool enabled{ true }; + + virtual void setPresetList(const QJsonObject& object) { + for (auto it = object.begin(); it != object.end(); it++) { + JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); + if (child) { + child->setPresetList(it.value().toObject()); + } + } + } + + // This must be named toJSON to integrate with the global scripting JSON object + Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } + Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); } + + // Running Time measurement + // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated + void setCPURunTime(double mstime) { _msCPURunTime = mstime; emit newStats(); } + double getCPURunTime() const { return _msCPURunTime; } + +public slots: + void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); } + +signals: + void loaded(); + void newStats(); +}; + +class TaskConfig : public JobConfig { + Q_OBJECT +public: + using QConfigPointer = std::shared_ptr; + + using Persistent = PersistentConfig; + + TaskConfig() = default ; + TaskConfig(bool enabled) : JobConfig(enabled) {} + + // getter for qml integration, prefer the templated getter + Q_INVOKABLE QObject* getConfig(const QString& name) { return QObject::findChild(name); } + // getter for cpp (strictly typed), prefer this getter + template typename T::Config* getConfig(std::string job = "") const { + QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string + return findChild(name); + } + + void connectChildConfig(QConfigPointer childConfig, const std::string& name); + void transferChildrenConfigs(QConfigPointer source); + + JobConcept* _task; + +public slots: + void refresh(); +}; + +} + +#endif // hifi_task_Config_h diff --git a/libraries/render/src/task/Task.h b/libraries/render/src/task/Task.h new file mode 100644 index 0000000000..3fcea66786 --- /dev/null +++ b/libraries/render/src/task/Task.h @@ -0,0 +1,326 @@ +// +// Task.h +// render/src/task +// +// Created by Zach Pomerantz on 1/6/2016. +// Copyright 2016 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_task_Task_h +#define hifi_task_Task_h + +#include "Config.h" +#include "Varying.h" + +#include "SettingHandle.h" + +#include "Logging.h" + +#include +#include + +namespace task { + +class JobConcept; +template class JobT; +template class TaskT; +class JobNoIO {}; + +class JobContext { +public: + virtual ~JobContext() {} + + std::shared_ptr jobConfig { nullptr }; +}; +using JobContextPointer = std::shared_ptr; + +// The guts of a job +class JobConcept { +public: + using Config = JobConfig; + using QConfigPointer = std::shared_ptr; + + JobConcept(QConfigPointer config) : _config(config) {} + virtual ~JobConcept() = default; + + virtual const Varying getInput() const { return Varying(); } + virtual const Varying getOutput() const { return Varying(); } + + virtual QConfigPointer& getConfiguration() { return _config; } + virtual void applyConfiguration() = 0; + + void setCPURunTime(double mstime) { std::static_pointer_cast(_config)->setCPURunTime(mstime); } + + QConfigPointer _config; +protected: +}; + + +template void jobConfigure(T& data, const C& configuration) { + data.configure(configuration); +} +template void jobConfigure(T&, const JobConfig&) { + // nop, as the default JobConfig was used, so the data does not need a configure method +} +template void jobConfigure(T&, const TaskConfig&) { + // nop, as the default TaskConfig was used, so the data does not need a configure method +} + +template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, JobNoIO& output) { + data.run(renderContext); +} +template void jobRun(T& data, const RC& renderContext, const I& input, JobNoIO& output) { + data.run(renderContext, input); +} +template void jobRun(T& data, const RC& renderContext, const JobNoIO& input, O& output) { + data.run(renderContext, output); +} +template void jobRun(T& data, const RC& renderContext, const I& input, O& output) { + data.run(renderContext, input, output); +} + +template +class Job { +public: + using Context = RC; + using ContextPointer = std::shared_ptr; + using Config = JobConfig; + using QConfigPointer = std::shared_ptr; + using None = JobNoIO; + + //template + class Concept : public JobConcept { + public: + Concept(QConfigPointer config) : JobConcept(config) {} + virtual ~Concept() = default; + + virtual void run(const ContextPointer& renderContext) = 0; + }; + using ConceptPointer = std::shared_ptr; + + template class Model : public Concept { + public: + using Data = T; + using Input = I; + using Output = O; + + Data _data; + Varying _input; + Varying _output; + + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } + + template + Model(const Varying& input, QConfigPointer config, A&&... args) : + Concept(config), + _data(Data(std::forward(args)...)), + _input(input), + _output(Output()) { + applyConfiguration(); + } + + template + static std::shared_ptr create(const Varying& input, A&&... args) { + return std::make_shared(input, std::make_shared(), std::forward(args)...); + } + + + void applyConfiguration() override { + jobConfigure(_data, *std::static_pointer_cast(_config)); + } + + void run(const ContextPointer& renderContext) override { + renderContext->jobConfig = std::static_pointer_cast(_config); + if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { + jobRun(_data, renderContext, _input.get(), _output.edit()); + } + renderContext->jobConfig.reset(); + } + }; + template using ModelI = Model; + template using ModelO = Model; + template using ModelIO = Model; + + Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} + + const Varying getInput() const { return _concept->getInput(); } + const Varying getOutput() const { return _concept->getOutput(); } + QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); } + void applyConfiguration() { return _concept->applyConfiguration(); } + + template T& edit() { + auto concept = std::static_pointer_cast(_concept); + assert(concept); + return concept->_data; + } + + virtual void run(const ContextPointer& renderContext) { + PerformanceTimer perfTimer(_name.c_str()); + PROFILE_RANGE(render, _name.c_str()); + auto start = usecTimestampNow(); + + _concept->run(renderContext); + + _concept->setCPURunTime((double)(usecTimestampNow() - start) / 1000.0); + } + +protected: + ConceptPointer _concept; + std::string _name = ""; +}; + +// A task is a specialized job to run a collection of other jobs +// It can be created on any type T by aliasing the type JobModel in the class T +// using JobModel = Task::Model +// The class T is expected to have a "build" method acting as a constructor. +// The build method is where child Jobs can be added internally to the task +// where the input of the task can be setup to feed the child jobs +// and where the output of the task is defined +template +class Task : public Job { +public: + using Context = RC; + using ContextPointer = std::shared_ptr; + using Config = TaskConfig; + using _Job = Job; + using QConfigPointer = _Job::QConfigPointer; + using None = _Job::None; + using Concept = _Job::Concept; + using Jobs = std::vector<_Job>; + + Task(std::string name, ConceptPointer concept) : Job(name, concept) {} + + class TaskConcept : public Concept { + public: + Varying _input; + Varying _output; + Jobs _jobs; + + const Varying getInput() const override { return _input; } + const Varying getOutput() const override { return _output; } + + TaskConcept(const Varying& input, QConfigPointer config) : Concept(config), _input(input) {} + + // Create a new job in the container's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, NA&&... args) { + _jobs.emplace_back(name, (NT::JobModel::create(input, std::forward(args)...))); + + // Conect the child config to this task's config + std::static_pointer_cast(getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); + + return _jobs.back().getOutput(); + } + template const Varying addJob(std::string name, NA&&... args) { + const auto input = Varying(typename NT::JobModel::Input()); + return addJob(name, input, std::forward(args)...); + } + }; + + template class TaskModel : public TaskConcept { + public: + using Data = T; + using Input = I; + using Output = O; + + Data _data; + + TaskModel(const Varying& input, QConfigPointer config) : + TaskConcept(input, config), + _data(Data()) {} + + template + static std::shared_ptr create(const Varying& input, A&&... args) { + auto model = std::make_shared(input, std::make_shared()); + + model->_data.build(*(model), model->_input, model->_output, std::forward(args)...); + + // Recreate the Config to use the templated type + model->createConfiguration(); + model->applyConfiguration(); + + return model; + } + + template + static std::shared_ptr create(A&&... args) { + const auto input = Varying(Input()); + return create(input, std::forward(args)...); + } + + void createConfiguration() { + // A brand new config + auto config = std::make_shared(); + // Make sure we transfer the former children configs to the new config + config->transferChildrenConfigs(_config); + // swap + _config = config; + // Capture this + std::static_pointer_cast(_config)->_task = this; + } + + QConfigPointer& getConfiguration() override { + if (!_config) { + createConfiguration(); + } + return _config; + } + + void applyConfiguration() override { + jobConfigure(_data, *std::static_pointer_cast(_config)); + for (auto& job : _jobs) { + job.applyConfiguration(); + } + } + + void run(const ContextPointer& renderContext) override { + auto config = std::static_pointer_cast(_config); + if (config->alwaysEnabled || config->enabled) { + for (auto job : _jobs) { + job.run(renderContext); + } + } + } + }; + template using Model = TaskModel; + template using ModelI = TaskModel; + template using ModelO = TaskModel; + template using ModelIO = TaskModel; + + // Create a new job in the Task's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, A&&... args) { + return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); + } + template const Varying addJob(std::string name, A&&... args) { + const auto input = Varying(typename T::JobModel::Input()); + return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); + } + + std::shared_ptr getConfiguration() { + return std::static_pointer_cast(_concept->getConfiguration()); + } + +protected: +}; +} + + +#define Task_DeclareTypeAliases(ContextName) \ + using JobConfig = task::JobConfig; \ + using TaskConfig = task::TaskConfig; \ + template using PersistentConfig = task::PersistentConfig; \ + using Job = task::Job<##ContextName>; \ + using Task = task::Task<##ContextName>; \ + using Varying = task::Varying; \ + template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2; \ + template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3; \ + template < typename T0, typename T1, typename T2, typename T3 > using VaryingSet4 = task::VaryingSet4; \ + template < typename T0, typename T1, typename T2, typename T3, typename T4 > using VaryingSet5 = task::VaryingSet5; \ + template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5 > using VaryingSet6 = task::VaryingSet6; \ + template < typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 > using VaryingSet7 = task::VaryingSet7; \ + template < class T, int NUM > using VaryingArray = task::VaryingArray; + +#endif // hifi_task_Task_h diff --git a/libraries/render/src/task/Varying.h b/libraries/render/src/task/Varying.h new file mode 100644 index 0000000000..50f4acd414 --- /dev/null +++ b/libraries/render/src/task/Varying.h @@ -0,0 +1,287 @@ +// +// Varying.h +// render/src/task +// +// Created by Zach Pomerantz on 1/6/2016. +// Copyright 2016 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_task_Varying_h +#define hifi_task_Varying_h + +#include +#include + +namespace task { + +// A varying piece of data, to be used as Job/Task I/O +class Varying { +public: + Varying() {} + Varying(const Varying& var) : _concept(var._concept) {} + Varying& operator=(const Varying& var) { + _concept = var._concept; + return (*this); + } + template Varying(const T& data) : _concept(std::make_shared>(data)) {} + + template bool canCast() const { return !!std::dynamic_pointer_cast>(_concept); } + template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } + template T& edit() { return std::static_pointer_cast>(_concept)->_data; } + + + // access potential sub varyings contained in this one. + Varying operator[] (uint8_t index) const { return (*_concept)[index]; } + uint8_t length() const { return (*_concept).length(); } + + template Varying getN (uint8_t index) const { return get()[index]; } + template Varying editN (uint8_t index) { return edit()[index]; } + +protected: + class Concept { + public: + virtual ~Concept() = default; + + virtual Varying operator[] (uint8_t index) const = 0; + virtual uint8_t length() const = 0; + }; + template class Model : public Concept { + public: + using Data = T; + + Model(const Data& data) : _data(data) {} + virtual ~Model() = default; + + virtual Varying operator[] (uint8_t index) const override { + Varying var; + return var; + } + virtual uint8_t length() const override { return 0; } + + Data _data; + }; + + std::shared_ptr _concept; +}; + +using VaryingPairBase = std::pair; +template < typename T0, typename T1 > +class VaryingSet2 : public VaryingPairBase { +public: + using Parent = VaryingPairBase; + typedef void is_proxy_tag; + + VaryingSet2() : Parent(Varying(T0()), Varying(T1())) {} + VaryingSet2(const VaryingSet2& pair) : Parent(pair.first, pair.second) {} + VaryingSet2(const Varying& first, const Varying& second) : Parent(first, second) {} + + const T0& get0() const { return first.get(); } + T0& edit0() { return first.edit(); } + + const T1& get1() const { return second.get(); } + T1& edit1() { return second.edit(); } + + virtual Varying operator[] (uint8_t index) const { + if (index == 1) { + return std::get<1>((*this)); + } else { + return std::get<0>((*this)); + } + } + virtual uint8_t length() const { return 2; } + + Varying hasVarying() const { return Varying((*this)); } +}; + + +template +class VaryingSet3 : public std::tuple{ +public: + using Parent = std::tuple; + + VaryingSet3() : Parent(Varying(T0()), Varying(T1()), Varying(T2())) {} + VaryingSet3(const VaryingSet3& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src)) {} + VaryingSet3(const Varying& first, const Varying& second, const Varying& third) : Parent(first, second, third) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + virtual Varying operator[] (uint8_t index) const { + if (index == 2) { + return std::get<2>((*this)); + } else if (index == 1) { + return std::get<1>((*this)); + } else { + return std::get<0>((*this)); + } + } + virtual uint8_t length() const { return 3; } + + Varying hasVarying() const { return Varying((*this)); } +}; + +template +class VaryingSet4 : public std::tuple{ +public: + using Parent = std::tuple; + + VaryingSet4() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3())) {} + VaryingSet4(const VaryingSet4& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src)) {} + VaryingSet4(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth) : Parent(first, second, third, fourth) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + const T3& get3() const { return std::get<3>((*this)).template get(); } + T3& edit3() { return std::get<3>((*this)).template edit(); } + + virtual Varying operator[] (uint8_t index) const { + if (index == 3) { + return std::get<3>((*this)); + } else if (index == 2) { + return std::get<2>((*this)); + } else if (index == 1) { + return std::get<1>((*this)); + } else { + return std::get<0>((*this)); + } + } + virtual uint8_t length() const { return 4; } + + Varying hasVarying() const { return Varying((*this)); } +}; + + +template +class VaryingSet5 : public std::tuple{ +public: + using Parent = std::tuple; + + VaryingSet5() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4())) {} + VaryingSet5(const VaryingSet5& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src)) {} + VaryingSet5(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth) : Parent(first, second, third, fourth, fifth) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + const T3& get3() const { return std::get<3>((*this)).template get(); } + T3& edit3() { return std::get<3>((*this)).template edit(); } + + const T4& get4() const { return std::get<4>((*this)).template get(); } + T4& edit4() { return std::get<4>((*this)).template edit(); } + + virtual Varying operator[] (uint8_t index) const { + if (index == 4) { + return std::get<4>((*this)); + } else if (index == 3) { + return std::get<3>((*this)); + } else if (index == 2) { + return std::get<2>((*this)); + } else if (index == 1) { + return std::get<1>((*this)); + } else { + return std::get<0>((*this)); + } + } + virtual uint8_t length() const { return 5; } + + Varying hasVarying() const { return Varying((*this)); } +}; + +template +class VaryingSet6 : public std::tuple{ +public: + using Parent = std::tuple; + + VaryingSet6() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5())) {} + VaryingSet6(const VaryingSet6& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src)) {} + VaryingSet6(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth) : Parent(first, second, third, fourth, fifth, sixth) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + const T3& get3() const { return std::get<3>((*this)).template get(); } + T3& edit3() { return std::get<3>((*this)).template edit(); } + + const T4& get4() const { return std::get<4>((*this)).template get(); } + T4& edit4() { return std::get<4>((*this)).template edit(); } + + const T5& get5() const { return std::get<5>((*this)).template get(); } + T5& edit5() { return std::get<5>((*this)).template edit(); } + + Varying hasVarying() const { return Varying((*this)); } +}; + +template +class VaryingSet7 : public std::tuple{ +public: + using Parent = std::tuple; + + VaryingSet7() : Parent(Varying(T0()), Varying(T1()), Varying(T2()), Varying(T3()), Varying(T4()), Varying(T5()), Varying(T6())) {} + VaryingSet7(const VaryingSet7& src) : Parent(std::get<0>(src), std::get<1>(src), std::get<2>(src), std::get<3>(src), std::get<4>(src), std::get<5>(src), std::get<6>(src)) {} + VaryingSet7(const Varying& first, const Varying& second, const Varying& third, const Varying& fourth, const Varying& fifth, const Varying& sixth, const Varying& seventh) : Parent(first, second, third, fourth, fifth, sixth, seventh) {} + + const T0& get0() const { return std::get<0>((*this)).template get(); } + T0& edit0() { return std::get<0>((*this)).template edit(); } + + const T1& get1() const { return std::get<1>((*this)).template get(); } + T1& edit1() { return std::get<1>((*this)).template edit(); } + + const T2& get2() const { return std::get<2>((*this)).template get(); } + T2& edit2() { return std::get<2>((*this)).template edit(); } + + const T3& get3() const { return std::get<3>((*this)).template get(); } + T3& edit3() { return std::get<3>((*this)).template edit(); } + + const T4& get4() const { return std::get<4>((*this)).template get(); } + T4& edit4() { return std::get<4>((*this)).template edit(); } + + const T5& get5() const { return std::get<5>((*this)).template get(); } + T5& edit5() { return std::get<5>((*this)).template edit(); } + + const T6& get6() const { return std::get<6>((*this)).template get(); } + T6& edit6() { return std::get<6>((*this)).template edit(); } + + Varying hasVarying() const { return Varying((*this)); } +}; + + +template < class T, int NUM > +class VaryingArray : public std::array { +public: + VaryingArray() { + for (size_t i = 0; i < NUM; i++) { + (*this)[i] = Varying(T()); + } + } +}; +} + +#endif // hifi_task_Varying_h diff --git a/tests/gpu-test/src/TestWindow.h b/tests/gpu-test/src/TestWindow.h index fd059f3e32..bceb29305e 100644 --- a/tests/gpu-test/src/TestWindow.h +++ b/tests/gpu-test/src/TestWindow.h @@ -17,7 +17,7 @@ #include #include -#include +#include #define DEFERRED_LIGHTING From 3691be4a2f498a6322b021634c24d2c89c38e380 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Sat, 22 Apr 2017 08:53:13 -0700 Subject: [PATCH 014/292] PUshing function to cpp --- libraries/render/src/task/Config.cpp | 9 +++++++++ libraries/render/src/task/Config.h | 12 +----------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/libraries/render/src/task/Config.cpp b/libraries/render/src/task/Config.cpp index 587c4319af..cb2c4f1e3c 100644 --- a/libraries/render/src/task/Config.cpp +++ b/libraries/render/src/task/Config.cpp @@ -15,6 +15,15 @@ using namespace task; +void JobConfig::setPresetList(const QJsonObject& object) { + for (auto it = object.begin(); it != object.end(); it++) { + JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); + if (child) { + child->setPresetList(it.value().toObject()); + } + } +} + void TaskConfig::connectChildConfig(QConfigPointer childConfig, const std::string& name) { childConfig->setParent(this); childConfig->setObjectName(name.c_str()); diff --git a/libraries/render/src/task/Config.h b/libraries/render/src/task/Config.h index 9e6f060d9d..203a0e4688 100644 --- a/libraries/render/src/task/Config.h +++ b/libraries/render/src/task/Config.h @@ -22,9 +22,6 @@ #include "Logging.h" -#include -#include - namespace task { class JobConcept; @@ -111,14 +108,7 @@ public: bool alwaysEnabled{ true }; bool enabled{ true }; - virtual void setPresetList(const QJsonObject& object) { - for (auto it = object.begin(); it != object.end(); it++) { - JobConfig* child = findChild(it.key(), Qt::FindDirectChildrenOnly); - if (child) { - child->setPresetList(it.value().toObject()); - } - } - } + virtual void setPresetList(const QJsonObject& object); // This must be named toJSON to integrate with the global scripting JSON object Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); } From 59950236d3cb777aed2e4a72e06928cc5b153b38 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Apr 2017 16:22:00 -0700 Subject: [PATCH 015/292] attempt to make hinge motor work better --- .../physics/src/ObjectConstraintHinge.cpp | 118 ++++++++++++++++-- libraries/physics/src/ObjectConstraintHinge.h | 35 ++++++ 2 files changed, 144 insertions(+), 9 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 6c55d9c5dd..c3044c5431 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -48,6 +48,50 @@ QList ObjectConstraintHinge::getRigidBodies() { return result; } +void ObjectConstraintHinge::prepareForPhysicsSimulation() { + // setting the motor velocity doesn't appear to work for anyone. constantly adjusting the + // target angle seems to work. + // https://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7020 + uint64_t now = usecTimestampNow(); + withWriteLock([&]{ + btHingeConstraint* constraint = static_cast(_constraint); + if (!constraint) { + return; + } + if (_previousMotorTime == 0) { + _previousMotorTime = now; + return; + } + if (_motorVelocity != 0.0f) { + if (_startMotorTime == 0) { + _startMotorTime = now; + } + float dt = (float)(now - _previousMotorTime) / (float)USECS_PER_SECOND; + float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; + float motorTarget = _motorVelocity * t; + while (motorTarget > PI) { + motorTarget -= PI; + } + while (motorTarget < PI) { + motorTarget += PI; + } + if (!_motorEnabled) { + constraint->enableMotor(true); + _motorEnabled = true; + } + constraint->setMaxMotorImpulse(_maxImpulse); + constraint->setMotorTarget(motorTarget, dt); + } else if (_motorTargetTimeScale > 0.0f) { + // XXX + } else if (_motorEnabled) { + constraint->enableMotor(false); + _motorEnabled = false; + _startMotorTime = 0; + } + _previousMotorTime = now; + }); +} + void ObjectConstraintHinge::updateHinge() { btHingeConstraint* constraint { nullptr }; float low; @@ -56,28 +100,45 @@ void ObjectConstraintHinge::updateHinge() { float biasFactor; float relaxationFactor; float motorVelocity; + float motorTarget; + float motorTargetTimeScale; + float maxImpulse; withReadLock([&]{ constraint = static_cast(_constraint); low = _low; high = _high; - softness = _softness; + maxImpulse = _maxImpulse; biasFactor = _biasFactor; relaxationFactor = _relaxationFactor; + + // under the hood, motorTarget sets a veloctiy and must be called repeatedly to maintain + // a velocity. _motorTargetTimeScale of 0.0f means the target isn't set. motorVelocity is + // only considered when motorTarget isn't set. motorVelocity = _motorVelocity; + motorTarget = _motorTarget; + motorTargetTimeScale = _motorTargetTimeScale; + softness = _softness; }); if (!constraint) { return; } - constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); - if (motorVelocity != 0.0f) { - constraint->setMotorTargetVelocity(motorVelocity); - constraint->enableMotor(true); - } else { - constraint->enableMotor(false); - } + // constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); + // if (motorTargetTimeScale > 0.0f) { + // qDebug() << "--- setting motor target:" << motorTarget << motorTargetTimeScale; + // constraint->setMotorTarget(motorTarget, motorTargetTimeScale); + // constraint->enableMotor(true); + // withWriteLock([&]{ + // _motorTargetTimeScale = 0.0f; // it's a one-shot. + // }); + // } else if (motorVelocity != 0.0f) { + // // constraint->setMotorTargetVelocity(motorVelocity); + // // constraint->enableMotor(true); + // } else { + // constraint->enableMotor(false); + // } } @@ -151,6 +212,9 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { float biasFactor; float relaxationFactor; float motorVelocity; + float motorTarget; + float motorTargetTimeScale; + float maxImpulse; bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); @@ -224,6 +288,27 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { motorVelocity = _motorVelocity; } + ok = true; + motorTarget = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, + "motorTarget", ok, false); + if (!ok) { + motorTarget = _motorTarget; + } + + ok = true; + motorTargetTimeScale = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, + "motorTargetTimeScale", ok, false); + if (!ok) { + motorTargetTimeScale = _motorTargetTimeScale; + } + + ok = true; + maxImpulse = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "maxImpulse", ok, false); + if (!ok) { + maxImpulse = _maxImpulse; + } + + if (somethingChanged || pivotInA != _pivotInA || axisInA != _axisInA || @@ -235,7 +320,10 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { softness != _softness || biasFactor != _biasFactor || relaxationFactor != _relaxationFactor || - motorVelocity != _motorVelocity) { + motorVelocity != _motorVelocity || + motorTarget != _motorTarget || + motorTargetTimeScale != _motorTargetTimeScale || + maxImpulse != _maxImpulse) { // something changed needUpdate = true; } @@ -254,6 +342,9 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { _biasFactor = biasFactor; _relaxationFactor = relaxationFactor; _motorVelocity = motorVelocity; + _motorTarget = motorTarget; + _motorTargetTimeScale = motorTargetTimeScale; + _maxImpulse = maxImpulse; _active = true; @@ -285,6 +376,9 @@ QVariantMap ObjectConstraintHinge::getArguments() { arguments["biasFactor"] = _biasFactor; arguments["relaxationFactor"] = _relaxationFactor; arguments["motorVelocity"] = _motorVelocity; + arguments["motorTarget"] = _motorTarget; + arguments["motorTargetTimeScale"] = _motorTargetTimeScale; + arguments["maxImpulse"] = _maxImpulse; arguments["angle"] = static_cast(_constraint)->getHingeAngle(); // [-PI,PI] } }); @@ -315,6 +409,9 @@ QByteArray ObjectConstraintHinge::serialize() const { dataStream << _tag; dataStream << _motorVelocity; + dataStream << _motorTarget; + dataStream << _motorTargetTimeScale; + dataStream << _maxImpulse; }); return serializedConstraintArguments; @@ -357,6 +454,9 @@ void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) { dataStream >> _tag; dataStream >> _motorVelocity; + dataStream >> _motorTarget; + dataStream >> _motorTargetTimeScale; + dataStream >> _maxImpulse; _active = true; }); diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h index 7d2cac7511..3dbd3e8152 100644 --- a/libraries/physics/src/ObjectConstraintHinge.h +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -21,6 +21,8 @@ public: ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity); virtual ~ObjectConstraintHinge(); + virtual void prepareForPhysicsSimulation() override; + virtual bool updateArguments(QVariantMap arguments) override; virtual QVariantMap getArguments() override; @@ -44,10 +46,43 @@ protected: float _low { -2.0f * PI }; float _high { 2.0f * PI }; + + // https://gamedev.stackexchange.com/questions/71436/what-are-the-parameters-for-bthingeconstraintsetlimit + // + // softness: a negative measure of the friction that determines how much the hinge rotates for a given force. A high + // softness would make the hinge rotate easily like it's oiled then. + // biasFactor: an offset for the relaxed rotation of the hinge. It won't be right in the middle of the low and high angles + // anymore. 1.0f is the neural value. + // relaxationFactor: a measure of how much force is applied internally to bring the hinge in its central rotation. + // This is right in the middle of the low and high angles. For example, consider a western swing door. After + // walking through it will swing in both directions but at the end it stays right in the middle. + + // http://javadoc.jmonkeyengine.org/com/jme3/bullet/joints/HingeJoint.html + // + // _softness - the factor at which the velocity error correction starts operating, i.e. a softness of 0.9 means that + // the vel. corr starts at 90% of the limit range. + // _biasFactor - the magnitude of the position correction. It tells you how strictly the position error (drift) is + // corrected. + // _relaxationFactor - the rate at which velocity errors are corrected. This can be seen as the strength of the + // limits. A low value will make the the limits more spongy. + + + float _maxImpulse { 1.0f }; + float _softness { 0.9f }; + float _biasFactor { 0.3f }; + float _relaxationFactor { 1.0f }; + float _motorVelocity { 0.0f }; + float _motorTarget { 0.0f }; + float _motorTargetTimeScale { 0.0f }; + + uint64_t _startMotorTime { 0 }; + uint64_t _previousMotorTime { 0 }; + + bool _motorEnabled { false }; }; #endif // hifi_ObjectConstraintHinge_h From e9461f812d77111cc70f065e14d83111d24eb777 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Apr 2017 17:09:26 -0700 Subject: [PATCH 016/292] don't run motor for other's hinge --- .../physics/src/ObjectConstraintHinge.cpp | 19 +++++++++++++------ .../system/libraries/entitySelectionTool.js | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index c3044c5431..036adbc915 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -52,6 +52,13 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { // setting the motor velocity doesn't appear to work for anyone. constantly adjusting the // target angle seems to work. // https://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7020 + + if (!isMine()) { + // XXX + // don't activate motor for someone else's action? + // maybe don't if this interface isn't the sim owner? + return; + } uint64_t now = usecTimestampNow(); withWriteLock([&]{ btHingeConstraint* constraint = static_cast(_constraint); @@ -69,12 +76,12 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { float dt = (float)(now - _previousMotorTime) / (float)USECS_PER_SECOND; float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; float motorTarget = _motorVelocity * t; - while (motorTarget > PI) { - motorTarget -= PI; - } - while (motorTarget < PI) { - motorTarget += PI; - } + + // brige motorTarget into the range of [-PI, PI] + motorTarget += PI; + motorTarget = fmodf(motorTarget, 2.0f * PI); + motorTarget -= PI; + if (!_motorEnabled) { constraint->enableMotor(true); _motorEnabled = true; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 79d45d5cd2..bb3d3a57c1 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -2490,6 +2490,7 @@ SelectionDisplay = (function() { y: 0, z: vector.z }); + print("translateXZTool " + JSON.stringify(newPosition)); Entities.editEntity(SelectionManager.selections[i], { position: newPosition, }); From 25070c3bcaf973aaafba029eec7e7c5020ddfeff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Apr 2017 17:56:40 -0700 Subject: [PATCH 017/292] try not bringing motor target into -PI,PI range --- .../physics/src/ObjectConstraintHinge.cpp | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 036adbc915..e14c467228 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -77,10 +77,10 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; float motorTarget = _motorVelocity * t; - // brige motorTarget into the range of [-PI, PI] - motorTarget += PI; - motorTarget = fmodf(motorTarget, 2.0f * PI); - motorTarget -= PI; + // // bring motorTarget into the range of [-PI, PI] + // motorTarget += PI; + // motorTarget = fmodf(motorTarget, 2.0f * PI); + // motorTarget -= PI; if (!_motorEnabled) { constraint->enableMotor(true); @@ -88,6 +88,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { } constraint->setMaxMotorImpulse(_maxImpulse); constraint->setMotorTarget(motorTarget, dt); + } else if (_motorTargetTimeScale > 0.0f) { // XXX } else if (_motorEnabled) { @@ -131,21 +132,6 @@ void ObjectConstraintHinge::updateHinge() { if (!constraint) { return; } - - // constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); - // if (motorTargetTimeScale > 0.0f) { - // qDebug() << "--- setting motor target:" << motorTarget << motorTargetTimeScale; - // constraint->setMotorTarget(motorTarget, motorTargetTimeScale); - // constraint->enableMotor(true); - // withWriteLock([&]{ - // _motorTargetTimeScale = 0.0f; // it's a one-shot. - // }); - // } else if (motorVelocity != 0.0f) { - // // constraint->setMotorTargetVelocity(motorVelocity); - // // constraint->enableMotor(true); - // } else { - // constraint->enableMotor(false); - // } } From f172730a38ed63a7308486e8cf5f777064d3e4ab Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Apr 2017 20:09:14 -0700 Subject: [PATCH 018/292] try to allow axisInA to be adjusted on the fly --- libraries/physics/src/ObjectConstraintHinge.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index e14c467228..55d9181059 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -102,6 +102,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { void ObjectConstraintHinge::updateHinge() { btHingeConstraint* constraint { nullptr }; + glm::vec3 axisInA; float low; float high; float softness; @@ -113,6 +114,7 @@ void ObjectConstraintHinge::updateHinge() { float maxImpulse; withReadLock([&]{ + axisInA = _axisInA; constraint = static_cast(_constraint); low = _low; high = _high; @@ -132,6 +134,9 @@ void ObjectConstraintHinge::updateHinge() { if (!constraint) { return; } + + auto bulletAxisInA = glmToBullet(axisInA); + constraint->setAxis(bulletAxisInA); } From b65b34a81b2a58030f228f45a291e6753bc4ca06 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Apr 2017 20:45:23 -0700 Subject: [PATCH 019/292] try to allow axisInA to be adjusted on the fly --- libraries/physics/src/ObjectConstraintHinge.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 55d9181059..6599f74867 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -192,7 +192,7 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { forceBodyNonStatic(); activateBody(); - updateHinge(); + // updateHinge(); return constraint; } From 33d770c24e27fb3fb4eafe2627e134bf08095482 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Mon, 24 Apr 2017 00:52:09 -0700 Subject: [PATCH 020/292] removing the explicit SettingHandle path to REnder?engine --- libraries/render-utils/src/AmbientOcclusionEffect.h | 2 +- libraries/render-utils/src/RenderShadowTask.h | 2 +- libraries/render/src/task/Config.h | 8 ++------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 92d0f1d375..3643e608ed 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -71,7 +71,7 @@ class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_PROPERTY(int blurRadius MEMBER blurRadius WRITE setBlurRadius) public: - AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent("Ambient Occlusion", false) {} + AmbientOcclusionEffectConfig() : render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false) {} const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 6; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index a044028e4d..031f44a42d 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -35,7 +35,7 @@ class RenderShadowTaskConfig : public render::Task::Config::Persistent { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty) public: - RenderShadowTaskConfig() : render::Task::Config::Persistent("Shadows", false) {} + RenderShadowTaskConfig() : render::Task::Config::Persistent(QStringList() << "Render" << "Engine" << "Shadows", false) {} signals: void dirty(); diff --git a/libraries/render/src/task/Config.h b/libraries/render/src/task/Config.h index 203a0e4688..2825a06c15 100644 --- a/libraries/render/src/task/Config.h +++ b/libraries/render/src/task/Config.h @@ -32,14 +32,10 @@ public: const QString NONE = "None"; PersistentConfig() = delete; - PersistentConfig(const QString& path) : - _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } PersistentConfig(const QStringList& path) : - _preset(QStringList() << "Render" << "Engine" << path, DEFAULT) { } - PersistentConfig(const QString& path, bool enabled) : C(enabled), - _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + _preset(path, DEFAULT) { } PersistentConfig(const QStringList& path, bool enabled) : C(enabled), - _preset(QStringList() << "Render" << "Engine" << path, enabled ? DEFAULT : NONE) { } + _preset(path, enabled ? DEFAULT : NONE) { } QStringList getPresetList() { if (_presets.empty()) { From c36f8da4c00afbdf0c3af5ca4365cf021a168758 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 24 Apr 2017 10:46:29 -0700 Subject: [PATCH 021/292] Fixing build on mac --- interface/src/avatar/CauterizedModel.cpp | 3 +- libraries/render-utils/src/Model.cpp | 2 +- libraries/render/src/render/Engine.h | 2 + libraries/render/src/task/Config.h | 2 + libraries/render/src/task/Task.h | 51 +++++++++++------------- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/interface/src/avatar/CauterizedModel.cpp b/interface/src/avatar/CauterizedModel.cpp index c7d25d3151..d7b7e0fedf 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/interface/src/avatar/CauterizedModel.cpp @@ -16,6 +16,7 @@ #include #include "CauterizedMeshPartPayload.h" +#include "RenderUtilsLogging.h" CauterizedModel::CauterizedModel(RigPointer rig, QObject* parent) : @@ -51,7 +52,7 @@ void CauterizedModel::createVisibleRenderItemSet() { // all of our mesh vectors must match in size if ((int)meshes.size() != _meshStates.size()) { - qCDebug(renderlogging) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 309182b25e..eddda41d5e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1194,7 +1194,7 @@ void Model::createVisibleRenderItemSet() { // all of our mesh vectors must match in size if ((int)meshes.size() != _meshStates.size()) { - qCDebug(renderlogging) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; return; } diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index d4dba0e340..240693b422 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -100,6 +100,8 @@ namespace render { protected: RenderContextPointer _renderContext; + + void run(const RenderContextPointer& context) override { assert(_renderContext); Task::run(_renderContext); } }; using EnginePointer = std::shared_ptr; diff --git a/libraries/render/src/task/Config.h b/libraries/render/src/task/Config.h index 2825a06c15..c78a3f3bfe 100644 --- a/libraries/render/src/task/Config.h +++ b/libraries/render/src/task/Config.h @@ -150,6 +150,8 @@ public slots: void refresh(); }; +using QConfigPointer = std::shared_ptr; + } #endif // hifi_task_Config_h diff --git a/libraries/render/src/task/Task.h b/libraries/render/src/task/Task.h index 3fcea66786..ed335150a7 100644 --- a/libraries/render/src/task/Task.h +++ b/libraries/render/src/task/Task.h @@ -41,7 +41,6 @@ using JobContextPointer = std::shared_ptr; class JobConcept { public: using Config = JobConfig; - using QConfigPointer = std::shared_ptr; JobConcept(QConfigPointer config) : _config(config) {} virtual ~JobConcept() = default; @@ -88,10 +87,8 @@ public: using Context = RC; using ContextPointer = std::shared_ptr; using Config = JobConfig; - using QConfigPointer = std::shared_ptr; using None = JobNoIO; - //template class Concept : public JobConcept { public: Concept(QConfigPointer config) : JobConcept(config) {} @@ -130,11 +127,11 @@ public: void applyConfiguration() override { - jobConfigure(_data, *std::static_pointer_cast(_config)); + jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); } void run(const ContextPointer& renderContext) override { - renderContext->jobConfig = std::static_pointer_cast(_config); + renderContext->jobConfig = std::static_pointer_cast(Concept::_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->isEnabled()) { jobRun(_data, renderContext, _input.get(), _output.edit()); } @@ -186,13 +183,13 @@ public: using Context = RC; using ContextPointer = std::shared_ptr; using Config = TaskConfig; - using _Job = Job; - using QConfigPointer = _Job::QConfigPointer; - using None = _Job::None; - using Concept = _Job::Concept; - using Jobs = std::vector<_Job>; + using JobType = Job; + using None = typename JobType::None; + using Concept = typename JobType::Concept; + using ConceptPointer = typename JobType::ConceptPointer; + using Jobs = std::vector; - Task(std::string name, ConceptPointer concept) : Job(name, concept) {} + Task(std::string name, ConceptPointer concept) : JobType(name, concept) {} class TaskConcept : public Concept { public: @@ -210,7 +207,7 @@ public: _jobs.emplace_back(name, (NT::JobModel::create(input, std::forward(args)...))); // Conect the child config to this task's config - std::static_pointer_cast(getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); + std::static_pointer_cast(Concept::getConfiguration())->connectChildConfig(_jobs.back().getConfiguration(), name); return _jobs.back().getOutput(); } @@ -255,31 +252,31 @@ public: // A brand new config auto config = std::make_shared(); // Make sure we transfer the former children configs to the new config - config->transferChildrenConfigs(_config); + config->transferChildrenConfigs(Concept::_config); // swap - _config = config; + Concept::_config = config; // Capture this - std::static_pointer_cast(_config)->_task = this; + std::static_pointer_cast(Concept::_config)->_task = this; } QConfigPointer& getConfiguration() override { - if (!_config) { + if (!Concept::_config) { createConfiguration(); } - return _config; + return Concept::_config; } void applyConfiguration() override { - jobConfigure(_data, *std::static_pointer_cast(_config)); - for (auto& job : _jobs) { + jobConfigure(_data, *std::static_pointer_cast(Concept::_config)); + for (auto& job : TaskConcept::_jobs) { job.applyConfiguration(); } } void run(const ContextPointer& renderContext) override { - auto config = std::static_pointer_cast(_config); + auto config = std::static_pointer_cast(Concept::_config); if (config->alwaysEnabled || config->enabled) { - for (auto job : _jobs) { + for (auto job : TaskConcept::_jobs) { job.run(renderContext); } } @@ -292,15 +289,15 @@ public: // Create a new job in the Task's queue; returns the job's output template const Varying addJob(std::string name, const Varying& input, A&&... args) { - return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); } template const Varying addJob(std::string name, A&&... args) { const auto input = Varying(typename T::JobModel::Input()); - return std::static_pointer_cast( _concept)->addJob(name, input, std::forward(args)...); + return std::static_pointer_cast(JobType::_concept)->template addJob(name, input, std::forward(args)...); } std::shared_ptr getConfiguration() { - return std::static_pointer_cast(_concept->getConfiguration()); + return std::static_pointer_cast(JobType::_concept->getConfiguration()); } protected: @@ -308,12 +305,12 @@ protected: } -#define Task_DeclareTypeAliases(ContextName) \ +#define Task_DeclareTypeAliases(ContextType) \ using JobConfig = task::JobConfig; \ using TaskConfig = task::TaskConfig; \ template using PersistentConfig = task::PersistentConfig; \ - using Job = task::Job<##ContextName>; \ - using Task = task::Task<##ContextName>; \ + using Job = task::Job; \ + using Task = task::Task; \ using Varying = task::Varying; \ template < typename T0, typename T1 > using VaryingSet2 = task::VaryingSet2; \ template < typename T0, typename T1, typename T2 > using VaryingSet3 = task::VaryingSet3; \ From 0cd2863df452a39b309a298b14c68322b7b73ad7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 18 Apr 2017 14:42:32 -0700 Subject: [PATCH 022/292] start on routing HMD position and rotation through controller interface (cherry picked from commit bca5241bae7674ff7e41c037d84bb78e5152046a) --- interface/resources/controllers/standard.json | 7 +- interface/src/Application.cpp | 7 ++ interface/src/avatar/MyAvatar.cpp | 68 ++++++++++++++++++- interface/src/avatar/MyAvatar.h | 17 ++++- .../controllers/src/controllers/Actions.cpp | 3 + .../controllers/src/controllers/Actions.h | 3 + 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 53285ea974..62eec9bc3c 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -61,6 +61,11 @@ { "from": "Standard.RightHand", "to": "Actions.RightHand" }, { "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" }, - { "from": "Standard.RightFoot", "to": "Actions.RightFoot" } + { "from": "Standard.RightFoot", "to": "Actions.RightFoot" }, + + { "from": "Standard.Hips", "to": "Actions.Hips" }, + { "from": "Standard.Spine2", "to": "Actions.Spine2" }, + + { "from": "Standard.Head", "to": "Actions.Head" } ] } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c6cd185034..5b014f7009 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4395,6 +4395,13 @@ void Application::update(float deltaTime) { controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT); myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix)); + controller::Pose hipsPose = userInputMapper->getPoseState(controller::Action::HIPS); + controller::Pose spine2Pose = userInputMapper->getPoseState(controller::Action::SPINE2); + myAvatar->setSpineControllerPosesInSensorFrame(hipsPose.transform(avatarToSensorMatrix), spine2Pose.transform(avatarToSensorMatrix)); + + controller::Pose headPose = userInputMapper->getPoseState(controller::Action::HEAD); + myAvatar->setHeadControllerPoseInSensorFrame(headPose.transform(avatarToSensorMatrix)); + updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index da09bfba7e..0906dc7f7f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -642,6 +642,7 @@ void MyAvatar::updateSensorToWorldMatrix() { updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); } + // Update avatar head rotation with sensor data void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; @@ -660,8 +661,9 @@ void MyAvatar::updateFromTrackers(float deltaTime) { estimatedPosition.x *= -1.0f; _trackedHeadPosition = estimatedPosition; - const float OCULUS_LEAN_SCALE = 0.05f; - estimatedPosition /= OCULUS_LEAN_SCALE; + // wut + // const float OCULUS_LEAN_SCALE = 0.05f; + // estimatedPosition /= OCULUS_LEAN_SCALE; } else if (inFacetracker) { estimatedPosition = tracker->getHeadTranslation(); _trackedHeadPosition = estimatedPosition; @@ -1384,6 +1386,68 @@ controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const { return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix); } +void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) { + if (controller::InputDevice::getLowVelocityFilter()) { + auto oldHipsPose = getHipsControllerPoseInSensorFrame(); + auto oldSpine2Pose = getSpine2ControllerPoseInSensorFrame(); + _hipsControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldHipsPose, hips)); + _spine2ControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldSpine2Pose, spine2)); + } else { + _hipsControllerPoseInSensorFrameCache.set(hips); + _spine2ControllerPoseInSensorFrameCache.set(spine2); + } +} + +controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const { + return _hipsControllerPoseInSensorFrameCache.get(); +} + +controller::Pose MyAvatar::getSpine2ControllerPoseInSensorFrame() const { + return _spine2ControllerPoseInSensorFrameCache.get(); +} + +controller::Pose MyAvatar::getHipsControllerPoseInWorldFrame() const { + return _hipsControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); +} + +controller::Pose MyAvatar::getSpine2ControllerPoseInWorldFrame() const { + return _spine2ControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); +} + +controller::Pose MyAvatar::getHipsControllerPoseInAvatarFrame() const { + glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); + return getHipsControllerPoseInWorldFrame().transform(invAvatarMatrix); +} + +controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const { + glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); + return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix); +} + +void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& headPose) { + bool inHmd = qApp->isHMDMode(); + Head* head = getHead(); + if (inHmd) { + _headControllerPoseInSensorFrameCache.set(headPose); + _trackedHeadPosition = headPose.translation; + head->setDeltaPitch(headPose.rotation.x); + head->setDeltaYaw(headPose.rotation.y); + head->setDeltaRoll(headPose.rotation.z); + } +} + +controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const { + return _headControllerPoseInSensorFrameCache.get(); +} + +controller::Pose MyAvatar::getHeadControllerPoseInWorldFrame() const { + return _headControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); +} + +controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const { + glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); + return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix); +} void MyAvatar::updateMotors() { _characterController.clearMotors(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0146bd11a4..01b4dc824b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -453,6 +453,19 @@ public: controller::Pose getLeftFootControllerPoseInAvatarFrame() const; controller::Pose getRightFootControllerPoseInAvatarFrame() const; + void setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2); + controller::Pose getHipsControllerPoseInSensorFrame() const; + controller::Pose getSpine2ControllerPoseInSensorFrame() const; + controller::Pose getHipsControllerPoseInWorldFrame() const; + controller::Pose getSpine2ControllerPoseInWorldFrame() const; + controller::Pose getHipsControllerPoseInAvatarFrame() const; + controller::Pose getSpine2ControllerPoseInAvatarFrame() const; + + void setHeadControllerPoseInSensorFrame(const controller::Pose& headPose); + controller::Pose getHeadControllerPoseInSensorFrame() const; + controller::Pose getHeadControllerPoseInWorldFrame() const; + controller::Pose getHeadControllerPoseInAvatarFrame() const; + bool hasDriveInput() const; Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); @@ -693,9 +706,11 @@ private: // These are stored in SENSOR frame ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() }; ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache{ controller::Pose() }; ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache{ controller::Pose() }; + ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache{ controller::Pose() }; + ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache{ controller::Pose() }; + ThreadSafeValueCache _headControllerPoseInSensorFrameCache{ controller::Pose() }; bool _hmdLeanRecenterEnabled = true; diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 300fa684d9..62a10c851f 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -53,6 +53,9 @@ namespace controller { makePosePair(Action::RIGHT_HAND, "RightHand"), makePosePair(Action::LEFT_FOOT, "LeftFoot"), makePosePair(Action::RIGHT_FOOT, "RightFoot"), + makePosePair(Action::HIPS, "Hips"), + makePosePair(Action::SPINE2, "Spine2"), + makePosePair(Action::HEAD, "Head"), makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index edf3dee07a..534f01d865 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -44,6 +44,9 @@ enum class Action { RIGHT_HAND, LEFT_FOOT, RIGHT_FOOT, + HIPS, + SPINE2, + HEAD, LEFT_HAND_CLICK, RIGHT_HAND_CLICK, From 23592f4a53294ff2c24b44a97f87abc053a5beb3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Apr 2017 07:21:50 -0700 Subject: [PATCH 023/292] get rid of _trackedHeadPosition (cherry picked from commit e909938b232e11400832ae7dd29de16968967668) --- interface/src/avatar/MyAvatar.cpp | 8 -------- interface/src/avatar/MyAvatar.h | 1 - 2 files changed, 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0906dc7f7f..659077188f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -642,7 +642,6 @@ void MyAvatar::updateSensorToWorldMatrix() { updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); } - // Update avatar head rotation with sensor data void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; @@ -659,14 +658,8 @@ void MyAvatar::updateFromTrackers(float deltaTime) { if (inHmd) { estimatedPosition = extractTranslation(getHMDSensorMatrix()); estimatedPosition.x *= -1.0f; - _trackedHeadPosition = estimatedPosition; - - // wut - // const float OCULUS_LEAN_SCALE = 0.05f; - // estimatedPosition /= OCULUS_LEAN_SCALE; } else if (inFacetracker) { estimatedPosition = tracker->getHeadTranslation(); - _trackedHeadPosition = estimatedPosition; estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); } @@ -1429,7 +1422,6 @@ void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& headPo Head* head = getHead(); if (inHmd) { _headControllerPoseInSensorFrameCache.set(headPose); - _trackedHeadPosition = headPose.translation; head->setDeltaPitch(headPose.rotation.x); head->setDeltaYaw(headPose.rotation.y); head->setDeltaRoll(headPose.rotation.z); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 01b4dc824b..cea5962638 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -353,7 +353,6 @@ public: eyeContactTarget getEyeContactTarget(); - Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } 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(); } From dc19f3772643eff3516e19435136c043bf9fb27c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Apr 2017 15:04:47 -0700 Subject: [PATCH 024/292] input/controller routing thru skeleton model and rig. --- interface/src/avatar/SkeletonModel.cpp | 39 +++++++++++++++++++------- libraries/animation/src/Rig.cpp | 2 +- libraries/animation/src/Rig.h | 12 ++++---- libraries/shared/src/GLMHelpers.cpp | 5 ++++ libraries/shared/src/GLMHelpers.h | 7 +++++ 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 3cf866fb6b..ccee65d3e6 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -108,8 +108,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::HeadParameters headParams; if (qApp->isHMDMode()) { - headParams.isInHMD = true; - // 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()); @@ -119,18 +117,39 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.rigHeadPosition = extractTranslation(rigHMDMat); headParams.rigHeadOrientation = extractRotation(rigHMDMat); headParams.worldHeadOrientation = extractRotation(worldHMDMat); + headParams.headEnabled = true; + } else { + 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.worldHeadOrientation = myAvatar->getHeadControllerPoseInWorldFrame().getTranslation(); + 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. + headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame(); + headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); + headParams.headEnabled = false; + } + } - // TODO: if hips target sensor is valid. - // Copy it into headParams.hipsMatrix, and set headParams.hipsEnabled to true. - - headParams.hipsEnabled = 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; - headParams.isInHMD = false; + } - // We don't have a valid localHeadPosition. - headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame(); - headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); + 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.neckJointIndex = geometry.neckJointIndex; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 116758b1ba..9ef2a554c6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1082,7 +1082,7 @@ void Rig::computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositi void Rig::updateNeckJoint(int index, const HeadParameters& params) { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - if (params.isInHMD) { + if (params.headEnabled) { glm::vec3 headPos, neckPos; glm::quat headRot, neckRot; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 89f0d624f9..b66ca95042 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,12 +42,14 @@ public: }; struct HeadParameters { - glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) - glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward) - glm::vec3 rigHeadPosition = glm::vec3(); // rig space - glm::mat4 hipsMatrix = glm::mat4(); // rig space + glm::mat4 hipsMatrix = glm::mat4(); // rig space + glm::mat4 spine2Matrix = glm::mat4(); // rig space + glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) + glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward) + glm::vec3 rigHeadPosition = glm::vec3(); // rig space bool hipsEnabled = false; - bool isInHMD = false; + bool headEnabled = false; + bool spine2Enabled = false; int neckJointIndex = -1; bool isTalking = false; }; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index ec244553f8..db42fef8bc 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -38,6 +38,11 @@ const quat Quaternions::X_180{ 0.0f, 1.0f, 0.0f, 0.0f }; const quat Quaternions::Y_180{ 0.0f, 0.0f, 1.0f, 0.0f }; const quat Quaternions::Z_180{ 0.0f, 0.0f, 0.0f, 1.0f }; +const mat4 Matrices::IDENTITY { glm::mat4() }; +const mat4 Matrices::X_180 { createMatFromQuatAndPos(Quaternions::X_180, Vectors::ZERO) }; +const mat4 Matrices::Y_180 { createMatFromQuatAndPos(Quaternions::Y_180, Vectors::ZERO) }; +const mat4 Matrices::Z_180 { createMatFromQuatAndPos(Quaternions::Z_180, Vectors::ZERO) }; + // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index deb87930fc..fd75fa416c 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -54,6 +54,13 @@ const glm::vec3 IDENTITY_FORWARD = glm::vec3( 0.0f, 0.0f,-1.0f); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); +class Matrices { +public: + static const mat4 IDENTITY; + static const mat4 X_180; + static const mat4 Y_180; + static const mat4 Z_180; +}; class Quaternions { public: From 1931a8d1f6982b9264b650e7166905249799f709 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Apr 2017 15:05:52 -0700 Subject: [PATCH 025/292] viveMotionCapture.js: can now disable puck control, with a second controller squeeze --- scripts/developer/tests/viveMotionCapture.js | 130 +++++++++---------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/scripts/developer/tests/viveMotionCapture.js b/scripts/developer/tests/viveMotionCapture.js index 6cb0f92b9b..5496b475be 100644 --- a/scripts/developer/tests/viveMotionCapture.js +++ b/scripts/developer/tests/viveMotionCapture.js @@ -8,7 +8,7 @@ var TRACKED_OBJECT_POSES = [ "TrackedObject12", "TrackedObject13", "TrackedObject14", "TrackedObject15" ]; -var calibrated = false; +var triggerPressHandled = false; var rightTriggerPressed = false; var leftTriggerPressed = false; @@ -43,21 +43,6 @@ var SENSOR_CONFIG_NAMES = [ "Auto" ]; -var ANIM_VARS = [ - "leftFootType", - "leftFootPosition", - "leftFootRotation", - "rightFootType", - "rightFootPosition", - "rightFootRotation", - "hipsType", - "hipsPosition", - "hipsRotation", - "spine2Type", - "spine2Position", - "spine2Rotation" -]; - var sensorConfig = AUTO; var Y_180 = {x: 0, y: 1, z: 0, w: 0}; @@ -86,7 +71,7 @@ function computeDefaultToReferenceXform() { return defaultToReferenceXform; } else { - return new Xform.ident(); + return Xform.ident(); } } @@ -200,71 +185,86 @@ function computeIKTargetXform(jointInfo) { function update(dt) { if (rightTriggerPressed && leftTriggerPressed) { - if (!calibrated) { - calibrate(); - calibrated = true; - + if (!triggerPressHandled) { + triggerPressHandled = true; if (handlerId) { - MyAvatar.removeAnimationStateHandler(handlerId); - } + print("AJT: UN-CALIBRATE!"); - handlerId = MyAvatar.addAnimationStateHandler(function (props) { - - var result = {}, xform; - if (rightFoot) { - xform = computeIKTargetXform(rightFoot); - result.rightFootType = ikTypes.RotationAndPosition; - result.rightFootPosition = xform.pos; - result.rightFootRotation = xform.rot; - } else { - result.rightFootType = props.rightFootType; - result.rightFootPosition = props.rightFootPosition; - result.rightFootRotation = props.rightFootRotation; + // go back to normal, vive pucks will be ignored. + leftFoot = undefined; + rightFoot = undefined; + hips = undefined; + spine2 = undefined; + if (handlerId) { + print("AJT: un-hooking animation state handler"); + MyAvatar.removeAnimationStateHandler(handlerId); + handlerId = undefined; } + } else { + print("AJT: CALIBRATE!"); + calibrate(); + + var animVars = []; if (leftFoot) { - xform = computeIKTargetXform(leftFoot); - result.leftFootType = ikTypes.RotationAndPosition; - result.leftFootPosition = xform.pos; - result.leftFootRotation = xform.rot; - } else { - result.leftFootType = props.leftFootType; - result.leftFootPosition = props.leftFootPosition; - result.leftFootRotation = props.leftFootRotation; + animVars.push("leftFootType"); + animVars.push("leftFootPosition"); + animVars.push("leftFootRotation"); + } + if (rightFoot) { + animVars.push("rightFootType"); + animVars.push("rightFootPosition"); + animVars.push("rightFootRotation"); } - if (hips) { - xform = computeIKTargetXform(hips); - result.hipsType = ikTypes.RotationAndPosition; - result.hipsPosition = xform.pos; - result.hipsRotation = xform.rot; - } else { - result.hipsType = props.hipsType; - result.hipsPosition = props.hipsPosition; - result.hipsRotation = props.hipsRotation; + animVars.push("hipsType"); + animVars.push("hipsPosition"); + animVars.push("hipsRotation"); } - if (spine2) { - xform = computeIKTargetXform(spine2); - result.spine2Type = ikTypes.RotationAndPosition; - result.spine2Position = xform.pos; - result.spine2Rotation = xform.rot; - } else { - result.spine2Type = ikTypes.Off; + animVars.push("spine2Type"); + animVars.push("spine2Position"); + animVars.push("spine2Rotation"); } - return result; - }, ANIM_VARS); - + // hook up new anim state handler that maps vive pucks to ik system. + handlerId = MyAvatar.addAnimationStateHandler(function (props) { + var result = {}, xform; + if (rightFoot) { + xform = computeIKTargetXform(rightFoot); + result.rightFootType = ikTypes.RotationAndPosition; + result.rightFootPosition = xform.pos; + result.rightFootRotation = xform.rot; + } + if (leftFoot) { + xform = computeIKTargetXform(leftFoot); + result.leftFootType = ikTypes.RotationAndPosition; + result.leftFootPosition = xform.pos; + result.leftFootRotation = xform.rot; + } + if (hips) { + xform = computeIKTargetXform(hips); + result.hipsType = ikTypes.RotationAndPosition; + result.hipsPosition = xform.pos; + result.hipsRotation = xform.rot; + } + if (spine2) { + xform = computeIKTargetXform(spine2); + result.spine2Type = ikTypes.RotationAndPosition; + result.spine2Position = xform.pos; + result.spine2Rotation = xform.rot; + } + return result; + }, animVars); + } } } else { - calibrated = false; + triggerPressHandled = false; } var drawMarkers = false; if (drawMarkers) { var RED = {x: 1, y: 0, z: 0, w: 1}; - var GREEN = {x: 0, y: 1, z: 0, w: 1}; var BLUE = {x: 0, y: 0, z: 1, w: 1}; if (leftFoot) { @@ -304,4 +304,4 @@ Script.scriptEnding.connect(function () { Controller.disableMapping(MAPPING_NAME); Script.update.disconnect(update); }); -var TRIGGER_OFF_VALUE = 0.1; + From 1fc9f4c93dc7b8654653667128ea8ac383875793 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Apr 2017 17:22:47 -0700 Subject: [PATCH 026/292] Hips and Spine are routed thru input system properly --- libraries/animation/src/Rig.cpp | 22 +++++++++++++++---- .../src/controllers/StandardController.cpp | 3 +++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9ef2a554c6..53f76d82ff 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1028,13 +1028,18 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { if (params.hipsEnabled) { _animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition); _animVars.set("hipsPosition", extractTranslation(params.hipsMatrix)); - _animVars.set("hipsRotation", glmExtractRotation(params.hipsMatrix) * Quaternions::Y_180); + _animVars.set("hipsRotation", glmExtractRotation(params.hipsMatrix)); } else { _animVars.set("hipsType", (int)IKTarget::Type::Unknown); } - // by default this IK target is disabled. - _animVars.set("spine2Type", (int)IKTarget::Type::Unknown); + if (params.spine2Enabled) { + _animVars.set("spine2Type", (int)IKTarget::Type::RotationAndPosition); + _animVars.set("spine2Position", extractTranslation(params.spine2Matrix)); + _animVars.set("spine2Rotation", glmExtractRotation(params.spine2Matrix)); + } else { + _animVars.set("spine2Type", (int)IKTarget::Type::Unknown); + } } void Rig::updateFromEyeParameters(const EyeParameters& params) { @@ -1105,7 +1110,16 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); - _animVars.set("headType", (int)IKTarget::Type::HmdHead); + + if (params.hipsEnabled) { + // Since there is an explicit hips ik target, switch the head to use the more generic RotationAndPosition IK chain type. + // this will allow the spine to bend more, ensuring that it can reach the head target position. + _animVars.set("headType", (int)IKTarget::Type::RotationAndPosition); + } else { + // When there is no hips IK target, use the HmdHead IK chain type. This will make the spine very stiff, + // but because the IK _hipsOffset is enabled, the hips will naturally follow underneath the head. + _animVars.set("headType", (int)IKTarget::Type::HmdHead); + } _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index cc90ee7b49..d8c98eb63b 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -104,6 +104,9 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(RIGHT_HAND, "RightHand"), makePair(LEFT_FOOT, "LeftFoot"), makePair(RIGHT_FOOT, "RightFoot"), + makePair(HIPS, "Hips"), + makePair(SPINE2, "Spine2"), + makePair(HEAD, "Head"), // Aliases, PlayStation style names makePair(LB, "L1"), From 1b9e60944762b74981a621b5abb8573771a21aaa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Apr 2017 17:23:15 -0700 Subject: [PATCH 027/292] viveMotionCapture.js: updated to route pucks through input system Removed use of animationStateHandler() to set IK targets. --- scripts/developer/tests/viveMotionCapture.js | 119 +++++++++---------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/scripts/developer/tests/viveMotionCapture.js b/scripts/developer/tests/viveMotionCapture.js index 5496b475be..4aa5cbf86f 100644 --- a/scripts/developer/tests/viveMotionCapture.js +++ b/scripts/developer/tests/viveMotionCapture.js @@ -11,18 +11,20 @@ var TRACKED_OBJECT_POSES = [ var triggerPressHandled = false; var rightTriggerPressed = false; var leftTriggerPressed = false; +var calibrationCount = 0; -var MAPPING_NAME = "com.highfidelity.viveMotionCapture"; - -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RTClick]).peek().to(function (value) { +var TRIGGER_MAPPING_NAME = "com.highfidelity.viveMotionCapture.triggers"; +var triggerMapping = Controller.newMapping(TRIGGER_MAPPING_NAME); +triggerMapping.from([Controller.Standard.RTClick]).peek().to(function (value) { rightTriggerPressed = (value !== 0) ? true : false; }); -mapping.from([Controller.Standard.LTClick]).peek().to(function (value) { +triggerMapping.from([Controller.Standard.LTClick]).peek().to(function (value) { leftTriggerPressed = (value !== 0) ? true : false; }); +Controller.enableMapping(TRIGGER_MAPPING_NAME); -Controller.enableMapping(MAPPING_NAME); +var CONTROLLER_MAPPING_NAME = "com.highfidelity.viveMotionCapture.controller"; +var controllerMapping; var leftFoot; var rightFoot; @@ -92,7 +94,8 @@ function calibrate() { if (pose.valid) { poses.push({ channel: channel, - pose: pose + pose: pose, + lastestPose: pose }); } }); @@ -177,85 +180,76 @@ var ikTypes = { var handlerId; -function computeIKTargetXform(jointInfo) { - var pose = Controller.getPoseValue(jointInfo.channel); +function convertJointInfoToPose(jointInfo) { + var latestPose = jointInfo.latestPose; var offsetXform = jointInfo.offsetXform; - return Xform.mul(Y_180_XFORM, Xform.mul(new Xform(pose.rotation, pose.translation), offsetXform)); + var xform = Xform.mul(new Xform(latestPose.rotation, latestPose.translation), offsetXform); + return { + valid: true, + translation: xform.pos, + rotation: xform.rot, + velocity: Vec3.sum(latestPose.velocity, Vec3.cross(latestPose.angularVelocity, Vec3.subtract(xform.pos, latestPose.translation))), + angularVelocity: latestPose.angularVelocity + }; } function update(dt) { if (rightTriggerPressed && leftTriggerPressed) { if (!triggerPressHandled) { triggerPressHandled = true; - if (handlerId) { - print("AJT: UN-CALIBRATE!"); + if (controllerMapping) { // go back to normal, vive pucks will be ignored. + print("AJT: UN-CALIBRATE!"); + leftFoot = undefined; rightFoot = undefined; hips = undefined; spine2 = undefined; - if (handlerId) { - print("AJT: un-hooking animation state handler"); - MyAvatar.removeAnimationStateHandler(handlerId); - handlerId = undefined; - } + + Controller.disableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + controllerMapping = undefined; + } else { print("AJT: CALIBRATE!"); calibrate(); + calibrationCount++; - var animVars = []; + controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME + calibrationCount); if (leftFoot) { - animVars.push("leftFootType"); - animVars.push("leftFootPosition"); - animVars.push("leftFootRotation"); + controllerMapping.from(leftFoot.channel).to(function (pose) { + leftFoot.latestPose = pose; + }); + controllerMapping.from(function () { + return convertJointInfoToPose(leftFoot); + }).to(Controller.Standard.LeftFoot); } if (rightFoot) { - animVars.push("rightFootType"); - animVars.push("rightFootPosition"); - animVars.push("rightFootRotation"); + controllerMapping.from(rightFoot.channel).to(function (pose) { + rightFoot.latestPose = pose; + }); + controllerMapping.from(function () { + return convertJointInfoToPose(rightFoot); + }).to(Controller.Standard.RightFoot); } if (hips) { - animVars.push("hipsType"); - animVars.push("hipsPosition"); - animVars.push("hipsRotation"); + controllerMapping.from(hips.channel).to(function (pose) { + hips.latestPose = pose; + }); + controllerMapping.from(function () { + return convertJointInfoToPose(hips); + }).to(Controller.Standard.Hips); } if (spine2) { - animVars.push("spine2Type"); - animVars.push("spine2Position"); - animVars.push("spine2Rotation"); + controllerMapping.from(spine2.channel).to(function (pose) { + spine2.latestPose = pose; + }); + controllerMapping.from(function () { + return convertJointInfoToPose(spine2); + }).to(Controller.Standard.Spine2); } - - // hook up new anim state handler that maps vive pucks to ik system. - handlerId = MyAvatar.addAnimationStateHandler(function (props) { - var result = {}, xform; - if (rightFoot) { - xform = computeIKTargetXform(rightFoot); - result.rightFootType = ikTypes.RotationAndPosition; - result.rightFootPosition = xform.pos; - result.rightFootRotation = xform.rot; - } - if (leftFoot) { - xform = computeIKTargetXform(leftFoot); - result.leftFootType = ikTypes.RotationAndPosition; - result.leftFootPosition = xform.pos; - result.leftFootRotation = xform.rot; - } - if (hips) { - xform = computeIKTargetXform(hips); - result.hipsType = ikTypes.RotationAndPosition; - result.hipsPosition = xform.pos; - result.hipsRotation = xform.rot; - } - if (spine2) { - xform = computeIKTargetXform(spine2); - result.spine2Type = ikTypes.RotationAndPosition; - result.spine2Position = xform.pos; - result.spine2Rotation = xform.rot; - } - return result; - }, animVars); + Controller.enableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); } } } else { @@ -301,7 +295,10 @@ function update(dt) { Script.update.connect(update); Script.scriptEnding.connect(function () { - Controller.disableMapping(MAPPING_NAME); + Controller.disableMapping(TRIGGER_MAPPING_NAME); + if (controllerMapping) { + Controller.disableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + } Script.update.disconnect(update); }); From 37233b9fdb8e2a2d68a3cba1d6afd95d8857f85c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Apr 2017 10:19:50 -0700 Subject: [PATCH 028/292] 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 029/292] 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 ef43ff4b99565a9fb4d485b5ece1d47536a3331f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Apr 2017 11:55:24 -0700 Subject: [PATCH 030/292] Add default pose matrices to InputCalibrationData structure --- interface/src/Application.cpp | 8 +- interface/src/avatar/MyAvatar.cpp | 100 ++++++++++++++++-- interface/src/avatar/MyAvatar.h | 8 ++ libraries/controllers/src/controllers/Input.h | 12 ++- 4 files changed, 116 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5b014f7009..a9f882f73f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4347,7 +4347,13 @@ void Application::update(float deltaTime) { controller::InputCalibrationData calibrationData = { myAvatar->getSensorToWorldMatrix(), createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()), - myAvatar->getHMDSensorMatrix() + myAvatar->getHMDSensorMatrix(), + myAvatar->getCenterEyeCalibrationMat(), + myAvatar->getHeadCalibrationMat(), + myAvatar->getSpine2CalibrationMat(), + myAvatar->getHipsCalibrationMat(), + myAvatar->getLeftFootCalibrationMat(), + myAvatar->getRightFootCalibrationMat() }; InputPluginPointer keyboardMousePlugin; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 659077188f..9acbf14242 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -83,6 +83,22 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; +// 2 meter tall dude (in avatar coordinates) +static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.9f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; +static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.8f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; +static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.7f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_NECK_ROT { Quaternions::Y_180 }; +static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.5f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180}; +static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.05f, 0.0f }; +static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; +static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.1f, -0.9f, 0.0f }; // AJT: TODO: WRONG FIX ME +static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { Quaternions::IDENTITY }; // AJT: TODO: WRONG FIX ME +static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.1f, -0.9f, 0.0f }; // AJT: TODO: WRONG FIX ME +static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { Quaternions::IDENTITY }; // AJT: TODO: WRONG FIX ME + MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), _wasPushing(false), @@ -2283,22 +2299,17 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - // 2 meter tall dude (in rig coordinates) - const glm::vec3 DEFAULT_RIG_MIDDLE_EYE_POS(0.0f, 0.9f, 0.0f); - const glm::vec3 DEFAULT_RIG_NECK_POS(0.0f, 0.70f, 0.0f); - const glm::vec3 DEFAULT_RIG_HIPS_POS(0.0f, 0.05f, 0.0f); - int rightEyeIndex = _rig->indexOfJoint("RightEye"); int leftEyeIndex = _rig->indexOfJoint("LeftEye"); int neckIndex = _rig->indexOfJoint("Neck"); int hipsIndex = _rig->indexOfJoint("Hips"); - glm::vec3 rigMiddleEyePos = DEFAULT_RIG_MIDDLE_EYE_POS; + glm::vec3 rigMiddleEyePos = DEFAULT_AVATAR_MIDDLE_EYE_POS; if (leftEyeIndex >= 0 && rightEyeIndex >= 0) { rigMiddleEyePos = (_rig->getAbsoluteDefaultPose(leftEyeIndex).trans() + _rig->getAbsoluteDefaultPose(rightEyeIndex).trans()) / 2.0f; } - glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_RIG_NECK_POS; - glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_RIG_HIPS_POS; + glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_AVATAR_NECK_POS; + glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans() : DEFAULT_AVATAR_HIPS_POS; glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos); glm::vec3 localNeck = (rigNeckPos - rigHipsPos); @@ -2662,6 +2673,79 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const { } } +glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int rightEyeIndex = _rig->indexOfJoint("RightEye"); + int leftEyeIndex = _rig->indexOfJoint("LeftEye"); + if (rightEyeIndex >= 0 && leftEyeIndex >= 0) { + auto centerEyePos = (getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex) + getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)) * 0.5f; + auto centerEyeRot = Quaternions::Y_180; + return createMatFromQuatAndPos(centerEyeRot, centerEyePos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_POS, DEFAULT_AVATAR_MIDDLE_EYE_POS); + } +} + +glm::mat4 MyAvatar::getHeadCalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int headIndex = _rig->indexOfJoint("Head"); + if (headIndex >= 0) { + auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex); + auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex); + return createMatFromQuatAndPos(headRot, headPos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_POS, DEFAULT_AVATAR_HEAD_POS); + } +} + +glm::mat4 MyAvatar::getSpine2CalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int spine2Index = _rig->indexOfJoint("Spine2"); + if (spine2Index >= 0) { + auto spine2Pos = getAbsoluteDefaultJointTranslationInObjectFrame(spine2Index); + auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index); + return createMatFromQuatAndPos(spine2Rot, spine2Pos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_POS, DEFAULT_AVATAR_SPINE2_POS); + } +} + +glm::mat4 MyAvatar::getHipsCalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int hipsIndex = _rig->indexOfJoint("Hips"); + if (hipsIndex >= 0) { + auto hipsPos = getAbsoluteDefaultJointTranslationInObjectFrame(hipsIndex); + auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex); + return createMatFromQuatAndPos(hipsRot, hipsPos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_POS, DEFAULT_AVATAR_HIPS_POS); + } +} + +glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int leftFootIndex = _rig->indexOfJoint("LeftFoot"); + if (leftFootIndex >= 0) { + auto leftFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(leftFootIndex); + auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex); + return createMatFromQuatAndPos(leftFootRot, leftFootPos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_POS, DEFAULT_AVATAR_LEFTFOOT_POS); + } +} + +glm::mat4 MyAvatar::getRightFootCalibrationMat() const { + // TODO: as an optimization cache this computation, then invalidate the cache when the avatar model is changed. + int rightFootIndex = _rig->indexOfJoint("RightFoot"); + if (rightFootIndex >= 0) { + auto rightFootPos = getAbsoluteDefaultJointTranslationInObjectFrame(rightFootIndex); + auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex); + return createMatFromQuatAndPos(rightFootRot, rightFootPos); + } else { + return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_POS, DEFAULT_AVATAR_RIGHTFOOT_POS); + } +} + bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& orientation) { auto hipsIndex = getJointIndex("Hips"); if (index != hipsIndex) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cea5962638..01b6496e12 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -473,6 +473,14 @@ public: virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; + // all calibration matrices are in absolute avatar space. + glm::mat4 getCenterEyeCalibrationMat() const; + glm::mat4 getHeadCalibrationMat() const; + glm::mat4 getSpine2CalibrationMat() const; + glm::mat4 getHipsCalibrationMat() const; + glm::mat4 getLeftFootCalibrationMat() const; + glm::mat4 getRightFootCalibrationMat() const; + void addHoldAction(AvatarActionHold* holdAction); // thread-safe void removeHoldAction(AvatarActionHold* holdAction); // thread-safe void updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose); diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index 9c7f09d526..65c78cd6ea 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -16,9 +16,15 @@ namespace controller { struct InputCalibrationData { - glm::mat4 sensorToWorldMat; - glm::mat4 avatarMat; - glm::mat4 hmdSensorMat; + glm::mat4 sensorToWorldMat; // sensor to world + glm::mat4 avatarMat; // avatar to world + glm::mat4 hmdSensorMat; // hmd pos and orientation in sensor space + glm::mat4 defaultCenterEyeMat; // default pose for the center of the eyes in avatar space. + glm::mat4 defaultHeadMat; // default pose for head joint in avatar space + glm::mat4 defaultSpine2; // default pose for spine2 joint in avatar space + glm::mat4 defaultHips; // default pose for hips joint in avatar space + glm::mat4 defaultLeftFoot; // default pose for leftFoot joint in avatar space + glm::mat4 defaultRightFoot; // default pose for leftFoot joint in avatar space }; enum class ChannelType { From 4f8f3a86566463bc331354fd8cfb46dd03a9d2fd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 26 Apr 2017 18:50:28 -0700 Subject: [PATCH 031/292] debugging the mip not beeing copied correctly on 4.1 --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 42 +++++++++++++++++-- libraries/image/src/image/Image.cpp | 4 +- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 1210112a78..c5c9b90eb2 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -48,7 +48,7 @@ BackendPointer GLBackend::createBackend() { // Where the gpuContext is initialized and where the TRUE Backend is created and assigned auto version = QOpenGLContextWrapper::currentContextVersion(); std::shared_ptr result; - if (!disableOpenGL45 && version >= 0x0405) { + if (false && !disableOpenGL45 && version >= 0x0405) { qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; result = std::make_shared(); } else { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index bff5bf3f2c..54d3c87194 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -300,7 +300,7 @@ void GL41VariableAllocationTexture::promote() { // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); - + /* withPreservedTexture([&] { GLuint fbo { 0 }; glGenFramebuffers(1, &fbo); @@ -325,7 +325,24 @@ void GL41VariableAllocationTexture::promote() { glDeleteFramebuffers(1, &fbo); syncSampler(); - }); + });*/ + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } // destroy the old texture glDeleteTextures(1, &oldId); @@ -343,7 +360,7 @@ void GL41VariableAllocationTexture::demote() { uint16_t oldAllocatedMip = _allocatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); - + /* withPreservedTexture([&] { GLuint fbo { 0 }; glCreateFramebuffers(1, &fbo); @@ -368,7 +385,24 @@ void GL41VariableAllocationTexture::demote() { glDeleteFramebuffers(1, &fbo); syncSampler(); - }); + });*/ + + // copy pre-existing mips + uint16_t mips = _gpuObject.getNumMips(); + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } // destroy the old texture diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 707a2e4496..e60187d230 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -28,9 +28,9 @@ using namespace gpu; #define CPU_MIPMAPS 1 -#define COMPRESS_COLOR_TEXTURES 0 +#define COMPRESS_COLOR_TEXTURES 1 #define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now -#define COMPRESS_GRAYSCALE_TEXTURES 0 +#define COMPRESS_GRAYSCALE_TEXTURES 1 #define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now static const glm::uvec2 SPARSE_PAGE_SIZE(128); From d99f63588076820a36eff989adc7b036461fdd59 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Thu, 27 Apr 2017 02:08:48 -0700 Subject: [PATCH 032/292] still finding a good implementation for the compressed texel copy --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 14 ++ libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h | 2 + libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 + .../src/gpu/gl41/GL41BackendTexture.cpp | 165 +++++++++--------- 4 files changed, 102 insertions(+), 80 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index b0c53caa1a..7f724cce65 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -11,6 +11,20 @@ using namespace gpu; using namespace gpu::gl; +bool GLTexelFormat::isCompressed() const { + switch (internalFormat) { + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_RG_RGTC2: + return true; + break; + default: + return false; + break; + } +} GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { GLenum result = GL_RGBA8; diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h index 94ded3dc23..1edc10395c 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h @@ -18,6 +18,8 @@ public: GLenum format; GLenum type; + bool isCompressed() const; + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { return evalGLTexelFormat(dstFormat, dstFormat); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 19979a1778..94ded8b23d 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -109,6 +109,7 @@ public: Size size() const override { return _size; } Size _size { 0 }; + GLTexelFormat _texelFormat; }; class GL41ResourceTexture : public GL41VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 0b03f8eec6..be820b66b6 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -240,7 +240,10 @@ GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture) { +GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : + GL41Texture(backend, texture), + _texelFormat(GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), texture.getStoredMipFormat())) +{ auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; _maxAllocatedMip = _populatedMip = mipLevels; @@ -321,49 +324,50 @@ void GL41VariableAllocationTexture::promote() { // allocate storage for new level allocateStorage(targetAllocatedMip); - /* + withPreservedTexture([&] { - GLuint fbo { 0 }; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - - uint16_t mips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); - (void)CHECK_GL_ERROR(); + if (false && _texelFormat.isCompressed()) { + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } } + } else { + GLuint fbo { 0 }; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); } - - // destroy the transfer framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - syncSampler(); - });*/ - - uint16_t mips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } + }); // destroy the old texture glDeleteTextures(1, &oldId); @@ -381,50 +385,51 @@ void GL41VariableAllocationTexture::demote() { uint16_t oldAllocatedMip = _allocatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); - /* + withPreservedTexture([&] { - GLuint fbo { 0 }; - glCreateFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - - uint16_t mips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); - (void)CHECK_GL_ERROR(); + if (_texelFormat.isCompressed()) { + + // copy pre-existing mips + uint16_t mips = _gpuObject.getNumMips(); + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } } + } else { + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); } - - // destroy the transfer framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - syncSampler(); - });*/ - - // copy pre-existing mips - uint16_t mips = _gpuObject.getNumMips(); - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } - + }); // destroy the old texture glDeleteTextures(1, &oldId); From b196dd082b0ef9eab222e48d2619a202a9cb6690 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 07:56:35 -0700 Subject: [PATCH 033/292] expose bullet slider constraint to javascript --- interface/src/InterfaceDynamicFactory.cpp | 3 + .../entities/src/EntityDynamicInterface.cpp | 5 + .../entities/src/EntityDynamicInterface.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + .../physics/src/ObjectConstraintHinge.cpp | 9 +- libraries/physics/src/ObjectConstraintHinge.h | 4 +- .../physics/src/ObjectConstraintSlider.cpp | 410 ++++++++++++++++++ .../physics/src/ObjectConstraintSlider.h | 63 +++ 9 files changed, 489 insertions(+), 11 deletions(-) create mode 100644 libraries/physics/src/ObjectConstraintSlider.cpp create mode 100644 libraries/physics/src/ObjectConstraintSlider.h diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index 5951ccef9e..8f8d57994a 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "InterfaceDynamicFactory.h" @@ -38,6 +39,8 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid return std::make_shared(id, ownerEntity); case DYNAMIC_TYPE_FAR_GRAB: return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_SLIDER: + return std::make_shared(id, ownerEntity); } Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type"); diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index bed3185b8f..5538639053 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -117,6 +117,9 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT if (normalizedDynamicTypeString == "fargrab") { return DYNAMIC_TYPE_FAR_GRAB; } + if (normalizedDynamicTypeString == "slider") { + return DYNAMIC_TYPE_SLIDER; + } qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name" << dynamicTypeString; @@ -139,6 +142,8 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp return "hinge"; case DYNAMIC_TYPE_FAR_GRAB: return "far-grab"; + case DYNAMIC_TYPE_SLIDER: + return "slider"; } assert(false); return "none"; diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index 93d9ffa43e..cfb82dbdf8 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -31,7 +31,8 @@ enum EntityDynamicType { DYNAMIC_TYPE_HOLD = 3000, DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000, DYNAMIC_TYPE_HINGE = 5000, - DYNAMIC_TYPE_FAR_GRAB = 6000 + DYNAMIC_TYPE_FAR_GRAB = 6000, + DYNAMIC_TYPE_SLIDER = 7000 }; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 3ad4dbf28d..706a523367 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_HINGE_CONSTRAINT; + return VERSION_ENTITIES_SLIDER_CONSTRAINT; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 074876862f..affc7fd0b0 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -207,6 +207,7 @@ const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66; const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; +const PacketVersion VERSION_ENTITIES_SLIDER_CONSTRAINT = 70; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 6599f74867..9f591da18b 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -54,7 +54,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { // https://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7020 if (!isMine()) { - // XXX + // TODO // don't activate motor for someone else's action? // maybe don't if this interface isn't the sim owner? return; @@ -77,11 +77,6 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; float motorTarget = _motorVelocity * t; - // // bring motorTarget into the range of [-PI, PI] - // motorTarget += PI; - // motorTarget = fmodf(motorTarget, 2.0f * PI); - // motorTarget -= PI; - if (!_motorEnabled) { constraint->enableMotor(true); _motorEnabled = true; @@ -90,7 +85,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { constraint->setMotorTarget(motorTarget, dt); } else if (_motorTargetTimeScale > 0.0f) { - // XXX + // TODO -- we probably want a spring-like action here } else if (_motorEnabled) { constraint->enableMotor(false); _motorEnabled = false; diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h index 3dbd3e8152..cd5f6ac7c2 100644 --- a/libraries/physics/src/ObjectConstraintHinge.h +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -44,8 +44,8 @@ protected: glm::vec3 _pivotInB; glm::vec3 _axisInB; - float _low { -2.0f * PI }; - float _high { 2.0f * PI }; + float _low { -TWO_PI }; + float _high { TWO_PI }; // https://gamedev.stackexchange.com/questions/71436/what-are-the-parameters-for-bthingeconstraintsetlimit // diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp new file mode 100644 index 0000000000..1e48d20a86 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -0,0 +1,410 @@ +// +// ObjectConstraintSlider.cpp +// libraries/physics/src +// +// Created by Seth Alves 2017-4-23 +// Copyright 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 "QVariantGLM.h" + +#include "EntityTree.h" +#include "ObjectConstraintSlider.h" +#include "PhysicsLogging.h" + + +const uint16_t ObjectConstraintSlider::constraintVersion = 1; + + +ObjectConstraintSlider::ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectConstraint(DYNAMIC_TYPE_SLIDER, id, ownerEntity), + _pointInA(glm::vec3(0.0f)), + _axisInA(glm::vec3(0.0f)) +{ + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintSlider::ObjectConstraintSlider"; + #endif +} + +ObjectConstraintSlider::~ObjectConstraintSlider() { + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintSlider::~ObjectConstraintSlider"; + #endif +} + +QList ObjectConstraintSlider::getRigidBodies() { + QList result; + result += getRigidBody(); + QUuid otherEntityID; + withReadLock([&]{ + otherEntityID = _otherEntityID; + }); + if (!otherEntityID.isNull()) { + result += getOtherRigidBody(otherEntityID); + } + return result; +} + +void ObjectConstraintSlider::prepareForPhysicsSimulation() { +} + +void ObjectConstraintSlider::updateSlider() { + btSliderConstraint* constraint { nullptr }; + + withReadLock([&]{ + // TODO -- write this + constraint = static_cast(_constraint); + }); + + if (!constraint) { + return; + } + + + + // constraint->setFrames (const btTransform &frameA, const btTransform &frameB); + constraint->setLowerLinLimit(_linearLow); + constraint->setUpperLinLimit(_linearHigh); + constraint->setLowerAngLimit(_angularLow); + constraint->setUpperAngLimit(_angularHigh); + +} + + +btTypedConstraint* ObjectConstraintSlider::getConstraint() { + btSliderConstraint* constraint { nullptr }; + QUuid otherEntityID; + glm::vec3 pointInA; + glm::vec3 axisInA; + glm::vec3 pointInB; + glm::vec3 axisInB; + + withReadLock([&]{ + constraint = static_cast(_constraint); + pointInA = _pointInA; + axisInA = _axisInA; + otherEntityID = _otherEntityID; + pointInB = _pointInB; + axisInB = _axisInB; + }); + if (constraint) { + return constraint; + } + + btRigidBody* rigidBodyA = getRigidBody(); + if (!rigidBodyA) { + qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyA"; + return nullptr; + } + + if (!otherEntityID.isNull()) { + // This slider is between two entities... find the other rigid body. + + glm::quat rotA = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + glm::quat rotB = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInB)); + + btTransform frameInA(glmToBullet(rotA), glmToBullet(pointInA)); + btTransform frameInB(glmToBullet(rotB), glmToBullet(pointInB)); + + btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); + if (!rigidBodyB) { + return nullptr; + } + + constraint = new btSliderConstraint(*rigidBodyA, *rigidBodyB, frameInA, frameInB, true); + } else { + // This slider is between an entity and the world-frame. + + glm::quat rot = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + + btTransform frameInA(glmToBullet(rot), glmToBullet(pointInA)); + + constraint = new btSliderConstraint(*rigidBodyA, frameInA, true); + } + + withWriteLock([&]{ + _constraint = constraint; + }); + + // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network + forceBodyNonStatic(); + activateBody(); + + // updateSlider(); + + return constraint; +} + + +bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { + glm::vec3 pointInA; + glm::vec3 axisInA; + QUuid otherEntityID; + glm::vec3 pointInB; + glm::vec3 axisInB; + float linearLow; + float linearHigh; + float angularLow; + float angularHigh; + float linearTarget; + float linearTimeScale; + bool linearTargetSet; + float angularTarget; + float angularTimeScale; + bool angularTargetSet; + + + bool needUpdate = false; + bool somethingChanged = ObjectDynamic::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + pointInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "point", ok, false); + if (!ok) { + pointInA = _pointInA; + } + + ok = true; + axisInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "axis", ok, false); + if (!ok) { + axisInA = _axisInA; + } + + ok = true; + otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("slider constraint", + arguments, "otherEntityID", ok, false)); + if (!ok) { + otherEntityID = _otherEntityID; + } + + ok = true; + pointInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherPoint", ok, false); + if (!ok) { + pointInB = _pointInB; + } + + ok = true; + axisInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherAxis", ok, false); + if (!ok) { + axisInB = _axisInB; + } + + ok = true; + linearLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearLow", ok, false); + if (!ok) { + linearLow = _linearLow; + } + + ok = true; + linearHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearHigh", ok, false); + if (!ok) { + linearHigh = _linearHigh; + } + + ok = true; + angularLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularLow", ok, false); + if (!ok) { + angularLow = _angularLow; + } + + ok = true; + angularHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularHigh", ok, false); + if (!ok) { + angularHigh = _angularHigh; + } + + + ok = true; + linearTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "linearTarget", ok, false); + if (!ok) { + linearTarget = _linearTarget; + linearTargetSet = _linearTargetSet; + } else { + linearTargetSet = true; + } + + + ok = true; + linearTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "linearTimeScale", ok, false); + if (!ok) { + linearTimeScale = _linearTimeScale; + } + + ok = true; + angularTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "angularTarget", ok, false); + if (!ok) { + angularTarget = _angularTarget; + angularTargetSet = _angularTargetSet; + } else { + angularTargetSet = true; + } + + + ok = true; + angularTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "angularTimeScale", ok, false); + if (!ok) { + angularTimeScale = _angularTimeScale; + } + + + if (somethingChanged || + pointInA != _pointInA || + axisInA != _axisInA || + otherEntityID != _otherEntityID || + pointInB != _pointInB || + axisInB != _axisInB || + linearLow != _linearLow || + linearHigh != _linearHigh || + angularLow != _angularLow || + angularHigh != _angularHigh || + linearTarget != _linearTarget || + linearTimeScale != _linearTimeScale || + linearTargetSet != _linearTargetSet || + angularTarget != _angularTarget || + angularTimeScale != _angularTimeScale || + angularTargetSet != _angularTargetSet) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { + withWriteLock([&] { + _pointInA = pointInA; + _axisInA = axisInA; + _otherEntityID = otherEntityID; + _pointInB = pointInB; + _axisInB = axisInB; + _linearLow = linearLow; + _linearHigh = linearHigh; + _angularLow = angularLow; + _angularHigh = angularHigh; + _linearTarget = linearTarget; + _linearTimeScale = linearTimeScale; + _linearTargetSet = linearTargetSet; + _angularTarget = angularTarget; + _angularTimeScale = angularTimeScale; + _angularTargetSet = angularTargetSet; + + _active = true; + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); + } + }); + + updateSlider(); + } + + return true; +} + +QVariantMap ObjectConstraintSlider::getArguments() { + QVariantMap arguments = ObjectDynamic::getArguments(); + withReadLock([&] { + if (_constraint) { + arguments["point"] = glmToQMap(_pointInA); + arguments["axis"] = glmToQMap(_axisInA); + arguments["otherEntityID"] = _otherEntityID; + arguments["otherPoint"] = glmToQMap(_pointInB); + arguments["otherAxis"] = glmToQMap(_axisInB); + arguments["linearLow"] = _linearLow; + arguments["linearHigh"] = _linearHigh; + arguments["angularLow"] = _angularLow; + arguments["angularHigh"] = _angularHigh; + arguments["linearTarget"] = _linearTarget; + arguments["linearTimeScale"] = _linearTimeScale; + arguments["linearTargetSet"] = _linearTargetSet; + arguments["angularTarget"] = _angularTarget; + arguments["angularTimeScale"] = _angularTimeScale; + arguments["angularTargetSet"] = _angularTargetSet; + arguments["linearPosition"] = static_cast(_constraint)->getLinearPos(); + arguments["angularPosition"] = static_cast(_constraint)->getAngularPos(); + } + }); + return arguments; +} + +QByteArray ObjectConstraintSlider::serialize() const { + QByteArray serializedConstraintArguments; + QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_SLIDER; + dataStream << getID(); + dataStream << ObjectConstraintSlider::constraintVersion; + + withReadLock([&] { + dataStream << localTimeToServerTime(_expires); + dataStream << _tag; + + dataStream << _pointInA; + dataStream << _axisInA; + dataStream << _otherEntityID; + dataStream << _pointInB; + dataStream << _axisInB; + dataStream << _linearLow; + dataStream << _linearHigh; + dataStream << _angularLow; + dataStream << _angularHigh; + dataStream << _linearTarget; + dataStream << _linearTimeScale; + dataStream << _linearTargetSet; + dataStream << _angularTarget; + dataStream << _angularTimeScale; + dataStream << _angularTargetSet; + }); + + return serializedConstraintArguments; +} + +void ObjectConstraintSlider::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectConstraintSlider::constraintVersion) { + assert(false); + return; + } + + withWriteLock([&] { + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; + + dataStream >> _pointInA; + dataStream >> _axisInA; + dataStream >> _otherEntityID; + dataStream >> _pointInB; + dataStream >> _axisInB; + dataStream >> _linearLow; + dataStream >> _linearHigh; + dataStream >> _angularLow; + dataStream >> _angularHigh; + dataStream >> _linearTarget; + dataStream >> _linearTimeScale; + dataStream >> _linearTargetSet; + dataStream >> _angularTarget; + dataStream >> _angularTimeScale; + dataStream >> _angularTargetSet; + + _active = true; + }); +} diff --git a/libraries/physics/src/ObjectConstraintSlider.h b/libraries/physics/src/ObjectConstraintSlider.h new file mode 100644 index 0000000000..d0746b0aba --- /dev/null +++ b/libraries/physics/src/ObjectConstraintSlider.h @@ -0,0 +1,63 @@ +// +// ObjectConstraintSlider.h +// libraries/physics/src +// +// Created by Seth Alves 2017-4-23 +// Copyright 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_ObjectConstraintSlider_h +#define hifi_ObjectConstraintSlider_h + +#include "ObjectConstraint.h" + +// http://bulletphysics.org/Bullet/BulletFull/classbtSliderConstraint.html + +class ObjectConstraintSlider : public ObjectConstraint { +public: + ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraintSlider(); + + virtual void prepareForPhysicsSimulation() override; + + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; + + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; + + virtual QList getRigidBodies() override; + virtual btTypedConstraint* getConstraint() override; + +protected: + static const uint16_t constraintVersion; + + void updateSlider(); + + glm::vec3 _pointInA; + glm::vec3 _axisInA; + + EntityItemID _otherEntityID; + glm::vec3 _pointInB; + glm::vec3 _axisInB; + + float _linearLow { std::numeric_limits::max() }; + float _linearHigh { std::numeric_limits::min() }; + + float _angularLow { -TWO_PI }; + float _angularHigh { TWO_PI }; + + float _linearTarget { 0.0f }; + float _linearTimeScale { 0.0f }; + bool _linearTargetSet { false }; + + float _angularTarget { 0.0f }; + float _angularTimeScale { 0.0f }; + bool _angularTargetSet { false }; + +}; + +#endif // hifi_ObjectConstraintSlider_h From 15661987be71f732859a39cdb91a4c4bd55a02f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 10:16:08 -0700 Subject: [PATCH 034/292] 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 69a90d6f861f5c6667f174168d8a4a058abef24e Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 27 Apr 2017 11:24:09 -0700 Subject: [PATCH 035/292] work in progress --- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 5 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 66 ++++++++++++------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 94ded8b23d..ef409d37f4 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -37,8 +37,9 @@ class GL41Backend : public GLBackend { public: static const GLint TRANSFORM_OBJECT_SLOT { 31 }; static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 }; - static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 33 }; - static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 34 }; + static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 }; + static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 }; + static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 }; explicit GL41Backend(bool syncCache) : Parent(syncCache) {} GL41Backend() : Parent() {} diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index be820b66b6..d9fa2ad067 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -386,28 +386,47 @@ void GL41VariableAllocationTexture::demote() { allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); - withPreservedTexture([&] { - if (_texelFormat.isCompressed()) { - - // copy pre-existing mips - uint16_t mips = _gpuObject.getNumMips(); - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } + if (_texelFormat.isCompressed()) { + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + glBindTexture(_target, oldId); + + glGetTexLevelParameteriv(_target, sourceMip, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size); + + // copy pre-existing mips + uint16_t mips = _gpuObject.getNumMips(); + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + GLint size { 0 }; + GLint internalFormat; + glGetTexLevelParameteriv(_target, sourceMip, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size); + + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); } - } else { + } + + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glDeleteBuffers(1, &pbo); + + } else { + withPreservedTexture([&] { GLuint fbo { 0 }; - glCreateFramebuffers(1, &fbo); + glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); uint16_t mips = _gpuObject.getNumMips(); @@ -427,9 +446,10 @@ void GL41VariableAllocationTexture::demote() { // destroy the transfer framebuffer glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); - } - syncSampler(); - }); + + syncSampler(); + }); + } // destroy the old texture glDeleteTextures(1, &oldId); From 42da429414b23c16b4b6307dd08213fb0692cc5d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:37 -0700 Subject: [PATCH 036/292] 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 149f87e734e454d09c3754e4a35058d8c18e9b2d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:55 -0700 Subject: [PATCH 037/292] 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 351cd54b420f44585a48510867ffdda8751c6d8f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 12:58:42 -0700 Subject: [PATCH 038/292] remove debug print --- scripts/system/libraries/entitySelectionTool.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index bb3d3a57c1..79d45d5cd2 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -2490,7 +2490,6 @@ SelectionDisplay = (function() { y: 0, z: vector.z }); - print("translateXZTool " + JSON.stringify(newPosition)); Entities.editEntity(SelectionManager.selections[i], { position: newPosition, }); From d8e4604b18ccdcf3d8c714ff1a265fc49321d1a0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:04 -0700 Subject: [PATCH 039/292] 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 4b0bd80c270254ead7f22ffe09934a9f99bfff6d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 13:03:42 -0700 Subject: [PATCH 040/292] 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 b0c12dd84ae7eaf4866928e2bef10bdb44ac93bb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 13:04:47 -0700 Subject: [PATCH 041/292] warn rather than crash if we get unexpected action data --- interface/src/InterfaceDynamicFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index 8f8d57994a..71f6b572e0 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -43,7 +43,7 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid return std::make_shared(id, ownerEntity); } - Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type"); + qDebug() << "Unknown entity dynamic type"; return EntityDynamicPointer(); } From 4c212fb1e56450c57f4f70792fd3c3041fd77da4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 10 Apr 2017 17:28:21 -0700 Subject: [PATCH 042/292] pull qApp spaghetti out of Avatar class some ends of spaghetti get pushed into AvatarManger class split Camera class into Camera base and FancyCamera derivation Application::getCamera() returns Camera by refence instead of pointer --- interface/src/FancyCamera.h | 3 --- interface/src/avatar/Avatar.cpp | 1 - 2 files changed, 4 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index cd231cd929..9ffa6cafd8 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -13,9 +13,6 @@ #include "Camera.h" -#include - -// TODO: come up with a better name than "FancyCamera" class FancyCamera : public Camera { Q_OBJECT diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ce8ec44f6c..9360421417 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -59,7 +59,6 @@ namespace render { auto avatarPtr = static_pointer_cast(avatar); if (avatarPtr->isInitialized() && args) { PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload"); - // TODO AVATARS_RENDERER: remove need for qApp avatarPtr->render(args); } } From 65682a914da69a55ed7c75e1fd3a1165e345f291 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 14 Apr 2017 10:22:32 -0700 Subject: [PATCH 043/292] remove cufty AvatarManager LocalLights feature --- interface/src/Application.cpp | 6 ----- interface/src/avatar/Avatar.cpp | 31 +++++------------------ interface/src/avatar/AvatarManager.cpp | 35 -------------------------- interface/src/avatar/AvatarManager.h | 15 ----------- 4 files changed, 6 insertions(+), 81 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 39a4b8ee7c..8801989e44 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5451,12 +5451,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri entityScriptingInterface->setPacketSender(&_entityEditSender); entityScriptingInterface->setEntityTree(getEntities()->getTree()); - // AvatarManager has some custom types - AvatarManager::registerMetaTypes(scriptEngine); - - // give the script engine to the RecordingScriptingInterface for its callbacks - DependencyManager::get()->setScriptEngine(scriptEngine); - if (property(hifi::properties::TEST).isValid()) { scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9360421417..a639b6880b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -638,36 +638,17 @@ void Avatar::render(RenderArgs* renderArgs) { glm::vec3 toTarget = frustum.getPosition() - getPosition(); float distanceToTarget = glm::length(toTarget); - { - fixupModelsInScene(renderArgs->_scene); + fixupModelsInScene(renderArgs->_scene); - if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) { - // add local lights - const float BASE_LIGHT_DISTANCE = 2.0f; - const float LIGHT_FALLOFF_RADIUS = 0.01f; - const float LIGHT_EXPONENT = 1.0f; - const float LIGHT_CUTOFF = glm::radians(80.0f); - float distance = BASE_LIGHT_DISTANCE * getUniformScale(); - glm::vec3 position = _skeletonModel->getTranslation(); - glm::quat orientation = getOrientation(); - foreach (const AvatarManager::LocalLight& light, DependencyManager::get()->getLocalLights()) { - glm::vec3 direction = orientation * light.direction; - DependencyManager::get()->addSpotLight(position - direction * distance, - distance * 2.0f, light.color, 0.5f, LIGHT_FALLOFF_RADIUS, orientation, LIGHT_EXPONENT, LIGHT_CUTOFF); - } - } - - bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); - if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { - PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); - const float BOUNDING_SHAPE_ALPHA = 0.7f; - _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); - } + bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); + if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); + const float BOUNDING_SHAPE_ALPHA = 0.7f; + _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); } const float DISPLAYNAME_DISTANCE = 20.0f; setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); - if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { auto& frustum = renderArgs->getViewFrustum(); auto textPosition = getDisplayNamePosition(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c4bcb67a16..ef57211d5c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -50,23 +50,6 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / // We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key. const QUuid MY_AVATAR_KEY; // NULL key -static QScriptValue localLightToScriptValue(QScriptEngine* engine, const AvatarManager::LocalLight& light) { - QScriptValue object = engine->newObject(); - object.setProperty("direction", vec3toScriptValue(engine, light.direction)); - object.setProperty("color", vec3toScriptValue(engine, light.color)); - return object; -} - -static void localLightFromScriptValue(const QScriptValue& value, AvatarManager::LocalLight& light) { - vec3FromScriptValue(value.property("direction"), light.direction); - vec3FromScriptValue(value.property("color"), light.color); -} - -void AvatarManager::registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, localLightToScriptValue, localLightFromScriptValue); - qScriptRegisterSequenceMetaType >(engine); -} - AvatarManager::AvatarManager(QObject* parent) : _avatarsToFade(), _myAvatar(std::make_shared(qApp->thread(), std::make_shared())) @@ -387,24 +370,6 @@ void AvatarManager::deleteAllAvatars() { } } -void AvatarManager::setLocalLights(const QVector& localLights) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setLocalLights", Q_ARG(const QVector&, localLights)); - return; - } - _localLights = localLights; -} - -QVector AvatarManager::getLocalLights() const { - if (QThread::currentThread() != thread()) { - QVector result; - QMetaObject::invokeMethod(const_cast(this), "getLocalLights", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QVector, result)); - return result; - } - return _localLights; -} - void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { result.clear(); result.swap(_motionStatesToRemoveFromPhysics); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 45f1a597eb..074e0f4fba 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -64,16 +64,6 @@ public: bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } - class LocalLight { - public: - glm::vec3 color; - glm::vec3 direction; - }; - - Q_INVOKABLE void setLocalLights(const QVector& localLights); - Q_INVOKABLE QVector getLocalLights() const; - - void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); @@ -116,8 +106,6 @@ private: std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. - QVector _localLights; - bool _shouldShowReceiveStats = false; std::list> _collisionInjectors; @@ -129,7 +117,4 @@ private: bool _shouldRender { true }; }; -Q_DECLARE_METATYPE(AvatarManager::LocalLight) -Q_DECLARE_METATYPE(QVector) - #endif // hifi_AvatarManager_h From 63939728740a7fea32d30f21695b87a3b8b1190b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 14 Apr 2017 13:36:20 -0700 Subject: [PATCH 044/292] remove AvatarManager dependency from Avatar --- interface/src/avatar/Avatar.cpp | 16 ++++++++++------ interface/src/avatar/Avatar.h | 3 ++- interface/src/avatar/AvatarManager.h | 6 +----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a639b6880b..84638f8008 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -28,7 +28,6 @@ #include #include -#include "AvatarManager.h" #include "AvatarMotionState.h" #include "Camera.h" #include "Menu.h" @@ -73,6 +72,12 @@ namespace render { } } +// static +bool showReceiveStats = false; +void Avatar::setShowReceiveStats(bool receiveStats) { + showReceiveStats = receiveStats; +} + Avatar::Avatar(QThread* thread, RigPointer rig) : AvatarData(), _skeletonOffset(0.0f), @@ -582,7 +587,6 @@ void Avatar::render(RenderArgs* renderArgs) { bool havePosition, haveRotation; if (_handState & LEFT_HAND_POINTING_FLAG) { - if (_handState & IS_FINGER_POINTING_FLAG) { int leftIndexTip = getJointIndex("LeftHandIndex4"); int leftIndexTipJoint = getJointIndex("LeftHandIndex3"); @@ -809,7 +813,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const g void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const { PROFILE_RANGE_BATCH(batch, __FUNCTION__); - bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); + bool shouldShowReceiveStats = showReceiveStats && !isMyAvatar(); // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return static const float CLIP_DISTANCE = 0.2f; @@ -1473,16 +1477,16 @@ QList Avatar::getSkeleton() { void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) { if (scene) { - render::Transaction transaction; auto nodelist = DependencyManager::get(); if (DependencyManager::get()->shouldRenderAvatars() && !nodelist->isIgnoringNode(getSessionUUID()) && !nodelist->isRadiusIgnoringNode(getSessionUUID())) { + render::Transaction transaction; addToScene(myHandle, scene, transaction); + scene->enqueueTransaction(transaction); } - scene->enqueueTransaction(transaction); } else { - qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown"; + qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown"; } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 14d1da530a..9bb8010b0d 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -66,6 +66,8 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: + static void setShowReceiveStats(bool receiveStats); + explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); @@ -251,7 +253,6 @@ public slots: void setModelURLFinished(bool success); protected: - friend class AvatarManager; const float SMOOTH_TIME_POSITION = 0.125f; const float SMOOTH_TIME_ORIENTATION = 0.075f; diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 074e0f4fba..1832bc7126 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -62,8 +62,6 @@ public: void clearOtherAvatars(); void deleteAllAvatars(); - bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } - void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); @@ -85,7 +83,7 @@ public: float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } public slots: - void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } + void setShouldShowReceiveStats(bool shouldShowReceiveStats) const { Avatar::setShowReceiveStats(shouldShowReceiveStats); } void updateAvatarRenderStatus(bool shouldRenderAvatars); private: @@ -106,8 +104,6 @@ private: std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. - bool _shouldShowReceiveStats = false; - std::list> _collisionInjectors; RateCounter<> _myAvatarSendRate; From 010d1dfa222286744959e47a6d4a8fb370d3198c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 14 Apr 2017 14:39:33 -0700 Subject: [PATCH 045/292] remove Menu dependency from Avatar class --- interface/src/avatar/Avatar.cpp | 34 ++++++++++++++++++++------ interface/src/avatar/Avatar.h | 21 ++++++++++------ interface/src/avatar/AvatarManager.cpp | 6 +++++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 84638f8008..2234630504 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -30,7 +30,6 @@ #include "AvatarMotionState.h" #include "Camera.h" -#include "Menu.h" #include "InterfaceLogging.h" #include "SceneScriptingInterface.h" #include "SoftAttachmentModel.h" @@ -78,6 +77,26 @@ void Avatar::setShowReceiveStats(bool receiveStats) { showReceiveStats = receiveStats; } +// static +bool renderMyLookAtVectors = false; +bool renderOtherLookAtVectors = false; +void Avatar::setShowLookAtVectors(bool showMine, bool showOthers) { + renderMyLookAtVectors = showMine; + renderOtherLookAtVectors = showOthers; +} + +// static +bool renderCollisionShapes = false; +void Avatar::setRenderCollisionShapes(bool render) { + renderCollisionShapes = render; +} + +// static +bool showNamesAboveHeads = false; +void Avatar::setShowNamesAboveHeads(bool show) { + showNamesAboveHeads = show; +} + Avatar::Avatar(QThread* thread, RigPointer rig) : AvatarData(), _skeletonOffset(0.0f), @@ -354,7 +373,7 @@ void Avatar::simulate(float deltaTime, bool inView) { _smoothPositionTimer += deltaTime; if (_smoothPositionTimer < _smoothPositionTime) { AvatarData::setPosition( - lerp(_smoothPositionInitial, + lerp(_smoothPositionInitial, _smoothPositionTarget, easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f))) ); @@ -367,7 +386,7 @@ void Avatar::simulate(float deltaTime, bool inView) { _smoothOrientationTimer += deltaTime; if (_smoothOrientationTimer < _smoothOrientationTime) { AvatarData::setOrientation( - slerp(_smoothOrientationInitial, + slerp(_smoothOrientationInitial, _smoothOrientationTarget, easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f))) ); @@ -541,9 +560,9 @@ void Avatar::postUpdate(float deltaTime) { bool renderLookAtVectors; if (isMyAvatar()) { - renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors); + renderLookAtVectors = renderMyLookAtVectors; } else { - renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors); + renderLookAtVectors = renderOtherLookAtVectors; } if (renderLookAtVectors) { @@ -644,8 +663,7 @@ void Avatar::render(RenderArgs* renderArgs) { fixupModelsInScene(renderArgs->_scene); - bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); - if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { + if (renderCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); const float BOUNDING_SHAPE_ALPHA = 0.7f; _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); @@ -1269,7 +1287,7 @@ float Avatar::getPelvisFloatingHeight() const { } void Avatar::setShowDisplayName(bool showDisplayName) { - if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) { + if (!showNamesAboveHeads) { _displayNameAlpha = 0.0f; return; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9bb8010b0d..116e5aad11 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -67,6 +67,9 @@ class Avatar : public AvatarData { public: static void setShowReceiveStats(bool receiveStats); + static void setShowLookAtVectors(bool showMine, bool showOthers); + static void setRenderCollisionShapes(bool render); + static void setShowNamesAboveHeads(bool show); explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); @@ -240,6 +243,13 @@ public: return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f); } + float getBoundingRadius() const; + + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); + void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); + bool isInScene() const { return render::Item::isValidID(_renderItemID); } + + void setMotionState(AvatarMotionState* motionState); public slots: @@ -261,8 +271,6 @@ protected: QString _empty{}; virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! - void setMotionState(AvatarMotionState* motionState); - SkeletonModelPointer _skeletonModel; glm::vec3 _skeletonOffset; std::vector> _attachmentModels; @@ -316,16 +324,13 @@ protected: ThreadSafeValueCache _rightPalmPositionCache { glm::vec3() }; ThreadSafeValueCache _rightPalmRotationCache { glm::quat() }; - void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); - void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); - bool isInScene() const { return render::Item::isValidID(_renderItemID); } - // Some rate tracking support RateCounter<> _simulationRate; RateCounter<> _simulationInViewRate; RateCounter<> _skeletonModelSimulationRate; RateCounter<> _jointDataSimulationRate; +<<<<<<< 4318cce04a59543d80a9364c86aab79408dcb50e // Smoothing data for blending from one position/orientation to another on remote agents. float _smoothPositionTime; float _smoothPositionTimer; @@ -336,6 +341,8 @@ protected: glm::quat _smoothOrientationInitial; glm::quat _smoothOrientationTarget; +======= +>>>>>>> remove Menu dependency from Avatar class private: class AvatarEntityDataHash { public: @@ -355,8 +362,6 @@ private: bool _isLookAtTarget { false }; bool _isAnimatingScale { false }; - float getBoundingRadius() const; - static int _jointConesID; int _voiceSphereID; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ef57211d5c..35a5681213 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -147,6 +147,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); + // HACK: update Avatar namespace settings + Avatar::setShowLookAtVectors( + Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors), + Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors)); + Avatar::setRenderCollisionShapes(Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)); + std::priority_queue sortedAvatars; AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, From aceac12398674418786cc99165bc01556d5b0b27 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 18 Apr 2017 13:36:33 -0700 Subject: [PATCH 046/292] use 'using' instead of 'typedef' --- libraries/physics/src/PhysicsEngine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9f2f1aff5c..07de0e7b5c 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -41,8 +41,8 @@ public: void* _b; // ObjectMotionState pointer }; -typedef std::map ContactMap; -typedef std::vector CollisionEvents; +using ContactMap = std::map; +using CollisionEvents = std::vector; class PhysicsEngine { public: From 2441536de387637b48531fcbec6121d68206d968 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 18 Apr 2017 13:37:36 -0700 Subject: [PATCH 047/292] remove Avatar dependency on AvatarMotionState --- interface/src/avatar/Avatar.cpp | 25 ++++--- interface/src/avatar/Avatar.h | 14 ++-- interface/src/avatar/AvatarManager.cpp | 76 ++++++++++++++-------- interface/src/avatar/AvatarManager.h | 4 +- interface/src/avatar/AvatarMotionState.cpp | 6 +- interface/src/avatar/AvatarMotionState.h | 8 +-- libraries/physics/src/PhysicsEngine.cpp | 1 + 7 files changed, 79 insertions(+), 55 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2234630504..91d8dfb447 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -149,11 +149,6 @@ Avatar::~Avatar() { }); } - if (_motionState) { - delete _motionState; - _motionState = nullptr; - } - auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_nameRectGeometryID); @@ -1202,8 +1197,8 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD; - if (_moving && _motionState) { - _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); + if (_moving) { + addPhysicsFlags(Simulation::DIRTY_POSITION); } if (_moving || _hasNewJointData) { locationChanged(); @@ -1325,14 +1320,18 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) { radius = halfExtents.x; } -void Avatar::setMotionState(AvatarMotionState* motionState) { - _motionState = motionState; -} - // virtual void Avatar::rebuildCollisionShape() { - if (_motionState) { - _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); + addPhysicsFlags(Simulation::DIRTY_SHAPE); +} + +void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) { + _physicsCallback = cb; +} + +void Avatar::addPhysicsFlags(uint32_t flags) { + if (_physicsCallback) { + _physicsCallback(flags); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 116e5aad11..17b5ea926e 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -11,6 +11,7 @@ #ifndef hifi_Avatar_h #define hifi_Avatar_h +#include #include #include @@ -48,9 +49,10 @@ enum ScreenTintLayer { NUM_SCREEN_TINT_LAYERS }; -class AvatarMotionState; class Texture; +using AvatarPhysicsCallback = std::function; + class Avatar : public AvatarData { Q_OBJECT @@ -190,7 +192,7 @@ public: virtual void computeShapeInfo(ShapeInfo& shapeInfo); void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); - AvatarMotionState* getMotionState() { return _motionState; } + //AvatarMotionState* getMotionState() { return _motionState; } using SpatiallyNestable::setPosition; virtual void setPosition(const glm::vec3& position) override; @@ -248,8 +250,12 @@ public: void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); bool isInScene() const { return render::Item::isValidID(_renderItemID); } + bool isMoving() const { return _moving; } - void setMotionState(AvatarMotionState* motionState); + //void setMotionState(AvatarMotionState* motionState); + void setPhysicsCallback(AvatarPhysicsCallback cb); + void addPhysicsFlags(uint32_t flags); + bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } public slots: @@ -366,7 +372,7 @@ private: int _voiceSphereID; - AvatarMotionState* _motionState = nullptr; + AvatarPhysicsCallback _physicsCallback { nullptr }; }; #endif // hifi_Avatar_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 35a5681213..41cf797eba 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -74,6 +74,7 @@ AvatarManager::AvatarManager(QObject* parent) : } AvatarManager::~AvatarManager() { + assert(_motionStates.empty()); } void AvatarManager::init() { @@ -128,10 +129,9 @@ float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString& float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const { auto avatar = std::static_pointer_cast(getAvatarBySessionID(sessionID)); - return avatar ? avatar->getSimulationRate(rateName) : 0.0f; + return avatar ? avatar->getSimulationRate(rateName) : 0.0f; } - void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); @@ -189,16 +189,15 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } - if (!avatar->getMotionState()) { + if (!avatar->isInPhysicsSimulation()) { ShapeInfo shapeInfo; avatar->computeShapeInfo(shapeInfo); btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); if (shape) { - // don't add to the simulation now, instead put it on a list to be added later - AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape); - avatar->setMotionState(motionState); + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); }); + _motionStates.insert(avatar.get(), motionState); _motionStatesToAddToPhysics.insert(motionState); - _motionStatesThatMightUpdate.insert(motionState); } } avatar->animateScaleChanges(deltaTime); @@ -283,30 +282,34 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { const float MIN_FADE_SCALE = MIN_AVATAR_SCALE; QReadLocker locker(&_hashLock); - QVector::iterator itr = _avatarsToFade.begin(); - while (itr != _avatarsToFade.end()) { - auto avatar = std::static_pointer_cast(*itr); + QVector::iterator avatarItr = _avatarsToFade.begin(); + while (avatarItr != _avatarsToFade.end()) { + auto avatar = std::static_pointer_cast(*avatarItr); avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE); avatar->animateScaleChanges(deltaTime); if (avatar->getTargetScale() <= MIN_FADE_SCALE) { - // fading to zero is such a rare event we push unique transaction for each one + // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; - avatar->removeFromScene(*itr, scene, transaction); + avatar->removeFromScene(*avatarItr, scene, transaction); scene->enqueueTransaction(transaction); } - // only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine - if (_motionStatesToRemoveFromPhysics.empty()) { - itr = _avatarsToFade.erase(itr); - } else { - ++itr; + // delete the motionState + // TODO: use SharedPointer technology to make this happen automagically + assert(!avatar->isInPhysicsSimulation()); + AvatarMotionStateMap::iterator motionStateItr = _motionStates.find(avatar.get()); + if (motionStateItr != _motionStates.end()) { + delete *motionStateItr; + _motionStates.erase(motionStateItr); } + + avatarItr = _avatarsToFade.erase(avatarItr); } else { const bool inView = true; // HACK avatar->simulate(deltaTime, inView); - ++itr; + ++avatarItr; } } } @@ -315,19 +318,24 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return std::make_shared(qApp->thread(), std::make_shared()); } +void AvatarManager::removeAvatarFromPhysicsSimulation(Avatar* avatar) { + assert(avatar); + avatar->setPhysicsCallback(nullptr); + AvatarMotionStateMap::iterator itr = _motionStates.find(avatar); + if (itr != _motionStates.end()) { + AvatarMotionState* motionState = *itr; + _motionStatesToAddToPhysics.remove(motionState); + _motionStatesToRemoveFromPhysics.push_back(motionState); + } +} + void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); // removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar // class in this context so we can call methods that don't exist at the base class. auto avatar = std::static_pointer_cast(removedAvatar); - - AvatarMotionState* motionState = avatar->getMotionState(); - if (motionState) { - _motionStatesThatMightUpdate.remove(motionState); - _motionStatesToAddToPhysics.remove(motionState); - _motionStatesToRemoveFromPhysics.push_back(motionState); - } + removeAvatarFromPhysicsSimulation(avatar.get()); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); @@ -362,11 +370,21 @@ void AvatarManager::clearOtherAvatars() { ++avatarIterator; } } + assert(scene); scene->enqueueTransaction(transaction); _myAvatar->clearLookAtTargetAvatar(); } void AvatarManager::deleteAllAvatars() { + // delete motionStates + // TODO: use shared_ptr technology to make this work automagically + AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); + while (motionStateItr != _motionStates.end()) { + delete *motionStateItr; + ++motionStateItr; + } + _motionStates.clear(); + QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { @@ -391,10 +409,12 @@ void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { result.clear(); - for (auto state : _motionStatesThatMightUpdate) { - if (state->_dirtyFlags > 0) { - result.push_back(state); + AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); + while (motionStateItr != _motionStates.end()) { + if ((*motionStateItr)->getIncomingDirtyFlags() != 0) { + result.push_back(*motionStateItr); } + ++motionStateItr; } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 1832bc7126..5c8935417b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -93,11 +93,13 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; + void removeAvatarFromPhysicsSimulation(Avatar* avatar); void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; - QSet _motionStatesThatMightUpdate; + using AvatarMotionStateMap = QMap; + AvatarMotionStateMap _motionStates; VectorOfMotionStates _motionStatesToRemoveFromPhysics; SetOfMotionStates _motionStatesToAddToPhysics; diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 335245670b..ffa99e3990 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -17,7 +17,7 @@ #include "AvatarMotionState.h" #include "BulletUtil.h" -AvatarMotionState::AvatarMotionState(Avatar* avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { +AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; if (_shape) { @@ -49,7 +49,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { // virtual and protected const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; - _avatar->computeShapeInfo(shapeInfo); + std::static_pointer_cast(_avatar)->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); } @@ -130,7 +130,7 @@ glm::vec3 AvatarMotionState::getObjectAngularVelocity() const { // virtual glm::vec3 AvatarMotionState::getObjectGravity() const { - return _avatar->getAcceleration(); + return std::static_pointer_cast(_avatar)->getAcceleration(); } // virtual diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 98b2b69373..a8dd7327ca 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -20,7 +20,7 @@ class Avatar; class AvatarMotionState : public ObjectMotionState { public: - AvatarMotionState(Avatar* avatar, const btCollisionShape* shape); + AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape); virtual PhysicsMotionType getMotionType() const override { return _motionType; } @@ -74,11 +74,7 @@ protected: virtual bool isReadyToComputeShape() const override { return true; } virtual const btCollisionShape* computeNewShape() override; - // The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState - // instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor. - // In other words, it is impossible for the Avatar to be deleted out from under its MotionState. - // In conclusion: weak pointer shennanigans would be pure overhead. - Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer + AvatarSharedPointer _avatar; uint32_t _dirtyFlags; }; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index ca6889485a..87a15eb264 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -220,6 +220,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) { body->setMotionState(nullptr); delete body; } + object->clearIncomingDirtyFlags(); } } From caf6a77baf15c8f6cacd420ef5c273f651b8b093 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 18 Apr 2017 16:35:32 -0700 Subject: [PATCH 048/292] repair bad merge during rebase --- interface/src/FancyCamera.h | 2 ++ interface/src/avatar/Avatar.h | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 9ffa6cafd8..66f7a07dbd 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -13,6 +13,8 @@ #include "Camera.h" +#include + class FancyCamera : public Camera { Q_OBJECT diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 17b5ea926e..e777460ecb 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -336,7 +336,6 @@ protected: RateCounter<> _skeletonModelSimulationRate; RateCounter<> _jointDataSimulationRate; -<<<<<<< 4318cce04a59543d80a9364c86aab79408dcb50e // Smoothing data for blending from one position/orientation to another on remote agents. float _smoothPositionTime; float _smoothPositionTimer; @@ -347,8 +346,6 @@ protected: glm::quat _smoothOrientationInitial; glm::quat _smoothOrientationTarget; -======= ->>>>>>> remove Menu dependency from Avatar class private: class AvatarEntityDataHash { public: From 72f5860538de8f3467b432d2141b53e8e09f2a5d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Apr 2017 10:37:49 -0700 Subject: [PATCH 049/292] restore RecordingScriptingInterface to Application --- interface/src/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8801989e44..5fd0b30d01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5451,6 +5451,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri entityScriptingInterface->setPacketSender(&_entityEditSender); entityScriptingInterface->setEntityTree(getEntities()->getTree()); + // give the script engine to the RecordingScriptingInterface for its callbacks + DependencyManager::get()->setScriptEngine(scriptEngine); + if (property(hifi::properties::TEST).isValid()) { scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance()); } From 823f3a73c6b96d1afee64304e7afd8a66fe355ae Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Apr 2017 11:00:29 -0700 Subject: [PATCH 050/292] remove commented out cruft --- interface/src/avatar/Avatar.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index e777460ecb..b323954049 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -192,8 +192,6 @@ public: virtual void computeShapeInfo(ShapeInfo& shapeInfo); void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); - //AvatarMotionState* getMotionState() { return _motionState; } - using SpatiallyNestable::setPosition; virtual void setPosition(const glm::vec3& position) override; using SpatiallyNestable::setOrientation; From 00b05ed137bf064aaa21e23ed9c9deca2f80180c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Apr 2017 09:31:33 -0700 Subject: [PATCH 051/292] connect menu options to avatar render features --- interface/src/Menu.cpp | 11 +++--- interface/src/Menu.h | 8 ++--- interface/src/avatar/Avatar.cpp | 46 +++++++++++--------------- interface/src/avatar/Avatar.h | 10 +++--- interface/src/avatar/AvatarManager.cpp | 6 ---- interface/src/avatar/AvatarManager.h | 1 - 6 files changed, 36 insertions(+), 46 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8754951317..97e566309f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -504,11 +504,14 @@ Menu::Menu() { #endif addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false, - avatarManager.data(), SLOT(setShouldShowReceiveStats(bool))); + avatar.get(), SLOT(setShowReceiveStats(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false, + avatar.get(), SLOT(setShowCollisionShapes(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false, + avatar.get(), SLOT(setShowMyLookAtVectors(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false, + avatar.get(), SLOT(setShowOtherLookAtVectors(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool))); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index eeffcac7ca..d46c2736a4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -146,9 +146,6 @@ namespace MenuOption { const QString Quit = "Quit"; const QString ReloadAllScripts = "Reload All Scripts"; const QString ReloadContent = "Reload Content (Clears all caches)"; - const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; - const QString RenderMyLookAtVectors = "Show My Eye Vectors"; - const QString RenderOtherLookAtVectors = "Show Other Eye Vectors"; const QString RenderMaxTextureMemory = "Maximum Texture Memory"; const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; const QString RenderMaxTexture4MB = "4 MB"; @@ -174,8 +171,11 @@ namespace MenuOption { const QString SendWrongDSConnectVersion = "Send wrong DS connect version"; const QString SendWrongProtocolVersion = "Send wrong protocol version"; const QString SetHomeLocation = "Set Home Location"; - const QString ShowDSConnectTable = "Show Domain Connection Timing"; const QString ShowBordersEntityNodes = "Show Entity Nodes"; + const QString ShowBoundingCollisionShapes = "Show Bounding Collision Shapes"; + const QString ShowDSConnectTable = "Show Domain Connection Timing"; + const QString ShowMyLookAtVectors = "Show My Eye Vectors"; + const QString ShowOtherLookAtVectors = "Show Other Eye Vectors"; const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; const QString SimulateEyeTracking = "Simulate"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 91d8dfb447..1e759ed396 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -71,28 +71,27 @@ namespace render { } } -// static -bool showReceiveStats = false; +static bool showReceiveStats = false; void Avatar::setShowReceiveStats(bool receiveStats) { showReceiveStats = receiveStats; } -// static -bool renderMyLookAtVectors = false; -bool renderOtherLookAtVectors = false; -void Avatar::setShowLookAtVectors(bool showMine, bool showOthers) { - renderMyLookAtVectors = showMine; - renderOtherLookAtVectors = showOthers; +static bool showMyLookAtVectors = false; +void Avatar::setShowMyLookAtVectors(bool showMine) { + showMyLookAtVectors = showMine; } -// static -bool renderCollisionShapes = false; -void Avatar::setRenderCollisionShapes(bool render) { - renderCollisionShapes = render; +static bool showOtherLookAtVectors = false; +void Avatar::setShowOtherLookAtVectors(bool showOthers) { + showOtherLookAtVectors = showOthers; } -// static -bool showNamesAboveHeads = false; +static bool showCollisionShapes = false; +void Avatar::setShowCollisionShapes(bool render) { + showCollisionShapes = render; +} + +static bool showNamesAboveHeads = false; void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } @@ -553,14 +552,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) { void Avatar::postUpdate(float deltaTime) { - bool renderLookAtVectors; - if (isMyAvatar()) { - renderLookAtVectors = renderMyLookAtVectors; - } else { - renderLookAtVectors = renderOtherLookAtVectors; - } - - if (renderLookAtVectors) { + if (isMyAvatar() ? showMyLookAtVectors : showOtherLookAtVectors) { const float EYE_RAY_LENGTH = 10.0; const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f); const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f); @@ -653,17 +645,18 @@ void Avatar::render(RenderArgs* renderArgs) { return; } - glm::vec3 toTarget = frustum.getPosition() - getPosition(); - float distanceToTarget = glm::length(toTarget); - fixupModelsInScene(renderArgs->_scene); - if (renderCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { + if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); const float BOUNDING_SHAPE_ALPHA = 0.7f; _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); } +#if 0 /// -------------- REMOVED FOR NOW -------------- + // removed CPU calculations as per removal of menu option + glm::vec3 toTarget = frustum.getPosition() - getPosition(); + float distanceToTarget = glm::length(toTarget); const float DISPLAYNAME_DISTANCE = 20.0f; setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { @@ -673,6 +666,7 @@ void Avatar::render(RenderArgs* renderArgs) { renderDisplayName(batch, frustum, textPosition); } } +#endif } glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b323954049..b2b0120b9a 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -68,11 +68,6 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: - static void setShowReceiveStats(bool receiveStats); - static void setShowLookAtVectors(bool showMine, bool showOthers); - static void setRenderCollisionShapes(bool render); - static void setShowNamesAboveHeads(bool show); - explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); @@ -256,6 +251,11 @@ public: bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } public slots: + void setShowReceiveStats(bool receiveStats); + void setShowMyLookAtVectors(bool showMine); + void setShowOtherLookAtVectors(bool showOthers); + void setShowCollisionShapes(bool render); + void setShowNamesAboveHeads(bool show); // FIXME - these should be migrated to use Pose data instead // thread safe, will return last valid palm from cache diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 41cf797eba..ec22c3403a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -147,12 +147,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - // HACK: update Avatar namespace settings - Avatar::setShowLookAtVectors( - Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors), - Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors)); - Avatar::setRenderCollisionShapes(Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)); - std::priority_queue sortedAvatars; AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars, diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 5c8935417b..e5b2117beb 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -83,7 +83,6 @@ public: float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } public slots: - void setShouldShowReceiveStats(bool shouldShowReceiveStats) const { Avatar::setShowReceiveStats(shouldShowReceiveStats); } void updateAvatarRenderStatus(bool shouldRenderAvatars); private: From 442080dec7134ba01c38703f7cae2b9c7bc74d54 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Apr 2017 09:32:07 -0700 Subject: [PATCH 052/292] remove unused RenderArgs::_cameraMode hack --- interface/src/Application.cpp | 1 - libraries/shared/src/RenderArgs.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5fd0b30d01..dff50fd616 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5119,7 +5119,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se QMutexLocker viewLocker(&_viewMutex); renderArgs->setViewFrustum(_displayViewFrustum); } - renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK renderArgs->_scene = getMain3DScene(); _renderEngine->getRenderContext()->args = renderArgs; diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 10a9a20287..9d81913078 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -130,8 +130,7 @@ public: bool _enableTexturing { true }; RenderDetails _details; - render::ScenePointer _scene; // HACK - int8_t _cameraMode { -1 }; // HACK + render::ScenePointer _scene; }; #endif // hifi_RenderArgs_h From 75b563d5982b5fa0651c40ad2ae4dcc4acf831ec Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Apr 2017 11:07:12 -0700 Subject: [PATCH 053/292] restore rendering of avatar receive stats --- interface/src/Application.cpp | 1 + interface/src/avatar/Avatar.cpp | 27 +++++++++++++-------------- interface/src/avatar/Avatar.h | 6 +++++- libraries/avatars/src/AvatarData.cpp | 2 -- libraries/avatars/src/AvatarData.h | 3 --- libraries/shared/src/RenderArgs.h | 1 + 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dff50fd616..5fd0b30d01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5119,6 +5119,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se QMutexLocker viewLocker(&_viewMutex); renderArgs->setViewFrustum(_displayViewFrustum); } + renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK renderArgs->_scene = getMain3DScene(); _renderEngine->getRenderContext()->args = renderArgs; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1e759ed396..3bd4c663d2 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -653,20 +653,19 @@ void Avatar::render(RenderArgs* renderArgs) { _skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA); } -#if 0 /// -------------- REMOVED FOR NOW -------------- - // removed CPU calculations as per removal of menu option - glm::vec3 toTarget = frustum.getPosition() - getPosition(); - float distanceToTarget = glm::length(toTarget); - const float DISPLAYNAME_DISTANCE = 20.0f; - setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE); - if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { - auto& frustum = renderArgs->getViewFrustum(); - auto textPosition = getDisplayNamePosition(); - if (frustum.pointIntersectsFrustum(textPosition)) { - renderDisplayName(batch, frustum, textPosition); + if (showReceiveStats || showNamesAboveHeads) { + glm::vec3 toTarget = frustum.getPosition() - getPosition(); + float distanceToTarget = glm::length(toTarget); + const float DISPLAYNAME_DISTANCE = 20.0f; + updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE); + if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { + auto& frustum = renderArgs->getViewFrustum(); + auto textPosition = getDisplayNamePosition(); + if (frustum.pointIntersectsFrustum(textPosition)) { + renderDisplayName(batch, frustum, textPosition); + } } } -#endif } glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { @@ -1275,8 +1274,8 @@ float Avatar::getPelvisFloatingHeight() const { return -_skeletonModel->getBindExtents().minimum.y; } -void Avatar::setShowDisplayName(bool showDisplayName) { - if (!showNamesAboveHeads) { +void Avatar::updateDisplayNameAlpha(bool showDisplayName) { + if (!(showNamesAboveHeads || showReceiveStats)) { _displayNameAlpha = 0.0f; return; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b2b0120b9a..192de146b9 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -150,7 +150,7 @@ public: virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override; virtual void setAttachmentData(const QVector& attachmentData) override; - void setShowDisplayName(bool showDisplayName); + void updateDisplayNameAlpha(bool showDisplayName); virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op virtual int parseDataFromBuffer(const QByteArray& buffer) override; @@ -368,6 +368,10 @@ private: int _voiceSphereID; AvatarPhysicsCallback _physicsCallback { nullptr }; + + float _displayNameTargetAlpha { 1.0f }; + float _displayNameAlpha { 1.0f }; + }; #endif // hifi_Avatar_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1427ce6359..55c8cc3b65 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -63,8 +63,6 @@ AvatarData::AvatarData() : _keyState(NO_KEY_DOWN), _forceFaceTrackerConnected(false), _headData(NULL), - _displayNameTargetAlpha(1.0f), - _displayNameAlpha(1.0f), _errorLogExpiry(0), _owningAvatarMixer(), _targetVelocity(0.0f) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 545a5f1f8c..f325b6bce7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -692,9 +692,6 @@ protected: QString _sessionDisplayName { }; QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; - float _displayNameTargetAlpha; - float _displayNameAlpha; - QHash _jointIndices; ///< 1-based, since zero is returned for missing keys QStringList _jointNames; ///< in order of depth-first traversal diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 9d81913078..f44d736e1a 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -131,6 +131,7 @@ public: RenderDetails _details; render::ScenePointer _scene; + int8_t _cameraMode { -1 }; }; #endif // hifi_RenderArgs_h From aa90a6bd028e729060fd1ead9bb99c51a67ebfe8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Apr 2017 11:41:00 -0700 Subject: [PATCH 054/292] use static methods for setting avatar debug options --- interface/src/Menu.cpp | 16 ++++++++-------- interface/src/avatar/Avatar.h | 11 ++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 97e566309f..1fbf3f9cf2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -503,14 +503,14 @@ Menu::Menu() { qApp, SLOT(setActiveEyeTracker())); #endif - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false, - avatar.get(), SLOT(setShowReceiveStats(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false, - avatar.get(), SLOT(setShowCollisionShapes(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false, - avatar.get(), SLOT(setShowMyLookAtVectors(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false, - avatar.get(), SLOT(setShowOtherLookAtVectors(bool))); + action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false); + connect(action, &QAction::triggered, []{ Avatar::setShowReceiveStats(Menu::getInstance()->isOptionChecked(MenuOption::AvatarReceiveStats)); }); + action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false); + connect(action, &QAction::triggered, []{ Avatar::setShowCollisionShapes(Menu::getInstance()->isOptionChecked(MenuOption::ShowBoundingCollisionShapes)); }); + action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false); + connect(action, &QAction::triggered, []{ Avatar::setShowMyLookAtVectors(Menu::getInstance()->isOptionChecked(MenuOption::ShowMyLookAtVectors)); }); + action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false); + connect(action, &QAction::triggered, []{ Avatar::setShowOtherLookAtVectors(Menu::getInstance()->isOptionChecked(MenuOption::ShowOtherLookAtVectors)); }); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 192de146b9..f86bf35bd9 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -68,6 +68,12 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: + static void setShowReceiveStats(bool receiveStats); + static void setShowMyLookAtVectors(bool showMine); + static void setShowOtherLookAtVectors(bool showOthers); + static void setShowCollisionShapes(bool render); + static void setShowNamesAboveHeads(bool show); + explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); @@ -251,11 +257,6 @@ public: bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } public slots: - void setShowReceiveStats(bool receiveStats); - void setShowMyLookAtVectors(bool showMine); - void setShowOtherLookAtVectors(bool showOthers); - void setShowCollisionShapes(bool render); - void setShowNamesAboveHeads(bool show); // FIXME - these should be migrated to use Pose data instead // thread safe, will return last valid palm from cache From 5cf233db3a7e0c16bc97d158f03dc74f053bdb97 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Apr 2017 16:17:53 -0700 Subject: [PATCH 055/292] less labyrinth --- interface/src/Menu.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1fbf3f9cf2..4ce9085053 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -504,13 +504,13 @@ Menu::Menu() { #endif action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false); - connect(action, &QAction::triggered, []{ Avatar::setShowReceiveStats(Menu::getInstance()->isOptionChecked(MenuOption::AvatarReceiveStats)); }); + connect(action, &QAction::triggered, [this]{ Avatar::setShowReceiveStats(isOptionChecked(MenuOption::AvatarReceiveStats)); }); action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false); - connect(action, &QAction::triggered, []{ Avatar::setShowCollisionShapes(Menu::getInstance()->isOptionChecked(MenuOption::ShowBoundingCollisionShapes)); }); + connect(action, &QAction::triggered, [this]{ Avatar::setShowCollisionShapes(isOptionChecked(MenuOption::ShowBoundingCollisionShapes)); }); action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false); - connect(action, &QAction::triggered, []{ Avatar::setShowMyLookAtVectors(Menu::getInstance()->isOptionChecked(MenuOption::ShowMyLookAtVectors)); }); + connect(action, &QAction::triggered, [this]{ Avatar::setShowMyLookAtVectors(isOptionChecked(MenuOption::ShowMyLookAtVectors)); }); action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false); - connect(action, &QAction::triggered, []{ Avatar::setShowOtherLookAtVectors(Menu::getInstance()->isOptionChecked(MenuOption::ShowOtherLookAtVectors)); }); + connect(action, &QAction::triggered, [this]{ Avatar::setShowOtherLookAtVectors(isOptionChecked(MenuOption::ShowOtherLookAtVectors)); }); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, From ef27d30e85cf36c3847f30bc25cd64b18689d077 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 12:01:37 -0700 Subject: [PATCH 056/292] 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 057/292] 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 058/292] 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 059/292] 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 060/292] 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 061/292] 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 21df08aa8831fbec9bca9c7afe8e9891c5c16813 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 14:02:38 -0700 Subject: [PATCH 062/292] set limits when slider constraint is first created --- libraries/physics/src/ObjectConstraintSlider.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index 1e48d20a86..fb8d8b9f68 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -63,8 +63,6 @@ void ObjectConstraintSlider::updateSlider() { return; } - - // constraint->setFrames (const btTransform &frameA, const btTransform &frameB); constraint->setLowerLinLimit(_linearLow); constraint->setUpperLinLimit(_linearHigh); @@ -133,7 +131,7 @@ btTypedConstraint* ObjectConstraintSlider::getConstraint() { forceBodyNonStatic(); activateBody(); - // updateSlider(); + updateSlider(); return constraint; } From 2faa8fb671508dcf85841ba82a3fd6b0bfd1fe99 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 27 Apr 2017 14:01:55 -0700 Subject: [PATCH 063/292] 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 7aee95c758..12ec6eca24 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -342,7 +342,7 @@ NetworkTexture::~NetworkTexture() { _ktxMipRequest->deleteLater(); _ktxMipRequest = nullptr; } - ResourceCache::requestCompleted(_self); + TextureCache::requestCompleted(_self); } } From a21a34a4a46b0c10ee87203f36f057962b846654 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Apr 2017 16:51:24 -0700 Subject: [PATCH 064/292] add entities file replacement to DS and ES --- assignment-client/src/octree/OctreeServer.cpp | 76 ++++++++++++++++--- assignment-client/src/octree/OctreeServer.h | 2 + .../resources/web/content/index.shtml | 41 ++++++++++ .../resources/web/content/js/content.js | 45 +++++++++++ domain-server/resources/web/header.html | 1 + .../web/{settings => }/js/sweetalert.min.js | 0 .../resources/web/settings/index.shtml | 2 +- domain-server/src/DomainServer.cpp | 49 +++++++++++- domain-server/src/DomainServer.h | 4 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/octree/src/OctreePersistThread.cpp | 37 +++++++++ libraries/octree/src/OctreePersistThread.h | 2 + 13 files changed, 248 insertions(+), 16 deletions(-) create mode 100644 domain-server/resources/web/content/index.shtml create mode 100644 domain-server/resources/web/content/js/content.js rename domain-server/resources/web/{settings => }/js/sweetalert.min.js (100%) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index f2dbe5d1d2..af5f2c904e 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -11,12 +11,14 @@ #include "OctreeServer.h" +#include #include #include #include #include +#include #include #include #include @@ -924,6 +926,57 @@ void OctreeServer::handleJurisdictionRequestPacket(QSharedPointerqueueReceivedPacket(message, senderNode); } +void OctreeServer::handleOctreeFileReplacement(QSharedPointer message) { + if (!_isFinished && !_isShuttingDown) { + // these messages are only allowed to come from the domain server, so make sure that is the case + auto nodeList = DependencyManager::get(); + if (message->getSenderSockAddr() == nodeList->getDomainHandler().getSockAddr()) { + // it's far cleaner to load up the new content upon server startup + // so here we just store a special file at our persist path + // and then force a stop of the server so that it can pick it up when it relaunches + if (!_persistAbsoluteFilePath.isEmpty()) { + + // before we restart the server and make it try and load this data, let's make sure it is valid + auto compressedOctree = message->getMessage(); + QByteArray jsonOctree; + + // assume we have GZipped content + bool wasCompressed = gunzip(compressedOctree, jsonOctree); + if (!wasCompressed) { + // the source was not compressed, assume we were sent regular JSON data + jsonOctree = compressedOctree; + } + + // check the JSON data to verify it is an object + if (QJsonDocument::fromJson(jsonOctree).isObject()) { + if (!wasCompressed) { + // source was not compressed, we compress it before we write it locally + gzip(jsonOctree, compressedOctree); + } + + // write the compressed octree data to a special file + auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION); + QFile replacementFile(replacementFilePath); + if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) { + // we've now written our replacement file, time to take the server down so it can + // process it when it comes back up + qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server"; + setFinished(true); + } else { + qWarning() << "Could not write replacement octree data to file - refusing to process"; + } + } else { + qDebug() << "Received replacement octree file that is invalid - refusing to process"; + } + } else { + qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known"; + } + } else { + qDebug() << "Received an octree file replacement that was not from our domain server - refusing to process"; + } + } +} + bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) { result = false; // assume it doesn't exist bool optionAvailable = false; @@ -1148,6 +1201,7 @@ void OctreeServer::domainSettingsRequestComplete() { packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket"); packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket"); packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket"); + packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement"); readConfiguration(); @@ -1173,25 +1227,25 @@ void OctreeServer::domainSettingsRequestComplete() { // If persist filename does not exist, let's see if there is one beside the application binary // If there is, let's copy it over to our target persist directory QDir persistPath { _persistFilePath }; - QString persistAbsoluteFilePath = persistPath.absolutePath(); + _persistAbsoluteFilePath = persistPath.absolutePath(); if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the // default data directory - persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + _persistAbsoluteFilePath = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; // force the persist file to end with .json.gz - if (!persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { - persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION; + if (!_persistAbsoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { + _persistAbsoluteFilePath += ENTITY_PERSIST_EXTENSION; } else { // make sure the casing of .json.gz is correct - persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); + _persistAbsoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); } - if (!QFile::exists(persistAbsoluteFilePath)) { + if (!QFile::exists(_persistAbsoluteFilePath)) { qDebug() << "Persist file does not exist, checking for existence of persist file next to application"; static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz"; @@ -1217,7 +1271,7 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } - QDir persistFileDirectory { QDir::cleanPath(persistAbsoluteFilePath + "/..") }; + QDir persistFileDirectory { QDir::cleanPath(_persistAbsoluteFilePath + "/..") }; if (!persistFileDirectory.exists()) { qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); @@ -1225,15 +1279,15 @@ void OctreeServer::domainSettingsRequestComplete() { } if (shouldCopy) { - qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistAbsoluteFilePath; + qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << _persistAbsoluteFilePath; - QFile::copy(pathToCopyFrom, persistAbsoluteFilePath); + QFile::copy(pathToCopyFrom, _persistAbsoluteFilePath); } else { qDebug() << "No existing persist file found"; } } - auto persistFileDirectory = QFileInfo(persistAbsoluteFilePath).absolutePath(); + auto persistFileDirectory = QFileInfo(_persistAbsoluteFilePath).absolutePath(); if (_backupDirectoryPath.isEmpty()) { // Use the persist file's directory to store backups _backupDirectoryPath = persistFileDirectory; @@ -1264,7 +1318,7 @@ void OctreeServer::domainSettingsRequestComplete() { qDebug() << "Backups will be stored in: " << _backupDirectoryPath; // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, + _persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3bb327eb06..8db8d845de 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -136,6 +136,7 @@ private slots: void handleOctreeQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleOctreeDataNackPacket(QSharedPointer message, SharedNodePointer senderNode); void handleJurisdictionRequestPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleOctreeFileReplacement(QSharedPointer message); void removeSendThread(); protected: @@ -172,6 +173,7 @@ protected: QString _statusHost; QString _persistFilePath; + QString _persistAbsoluteFilePath; QString _persistAsFileType; QString _backupDirectoryPath; int _packetsPerClientPerInterval; diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml new file mode 100644 index 0000000000..1d8d3fe1ab --- /dev/null +++ b/domain-server/resources/web/content/index.shtml @@ -0,0 +1,41 @@ + + +
+
+
+ +
+
+ +
+
+
+
+

Upload Entities File

+
+
+
+

+ Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
+ Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed. +

+

+ If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored in C:\Users\[user_name]\AppData\Roaming\High Fidelity\assignment-client. +

+ + +
+ +
+ +
+
+
+
+ + + + + diff --git a/domain-server/resources/web/content/js/content.js b/domain-server/resources/web/content/js/content.js new file mode 100644 index 0000000000..2e6e084164 --- /dev/null +++ b/domain-server/resources/web/content/js/content.js @@ -0,0 +1,45 @@ +$(document).ready(function(){ + + function showSpinnerAlert(title) { + swal({ + title: title, + text: '
', + html: true, + showConfirmButton: false, + allowEscapeKey: false + }); + } + + var frm = $('#upload-form'); + frm.submit(function (ev) { + $.ajax({ + type: frm.attr('method'), + url: frm.attr('action'), + data: new FormData($(this)[0]), + cache: false, + contentType: false, + processData: false, + success: function (data) { + swal({ + title: 'Uploaded', + type: 'success', + text: 'Your Entity Server is restarting to replace its local content with the uploaded file.', + confirmButtonText: 'OK' + }) + }, + error: function (data) { + swal({ + title: '', + type: 'error', + text: 'Your entities file could not be transferred to the Entity Server.
Verify that the file is a .json or .json.gz entities file and try again.', + html: true, + confirmButtonText: 'OK', + }); + } + }); + + ev.preventDefault(); + + showSpinnerAlert("Uploading Entities File"); + }); +}); diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html index b4eee406f2..965f86b0a1 100644 --- a/domain-server/resources/web/header.html +++ b/domain-server/resources/web/header.html @@ -36,6 +36,7 @@
  • New Assignment
  • +
  • Content
  • Settings
  • diff --git a/domain-server/resources/web/settings/js/sweetalert.min.js b/domain-server/resources/web/js/sweetalert.min.js similarity index 100% rename from domain-server/resources/web/settings/js/sweetalert.min.js rename to domain-server/resources/web/js/sweetalert.min.js diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index 3eb7a53726..1812c52dad 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -99,7 +99,7 @@ - + diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 620b11d8ad..233cb02dff 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1633,6 +1633,15 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) { return directory.absoluteFilePath(uuidStringWithoutCurlyBraces(assignmentUUID)); } +QString DomainServer::pathForRedirect(QString path) const { + // make sure the passed path has a leading slash + if (!path.startsWith('/')) { + path.insert(0, '/'); + } + + return "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()) + path; +} + const QString URI_OAUTH = "/oauth"; bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { const QString JSON_MIME_TYPE = "application/json"; @@ -1640,6 +1649,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url const QString URI_ASSIGNMENT = "/assignment"; const QString URI_NODES = "/nodes"; const QString URI_SETTINGS = "/settings"; + const QString URI_ENTITY_FILE_UPLOAD = "/content/upload"; const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; @@ -1869,6 +1879,25 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url // respond with a 200 code for successful upload connection->respond(HTTPConnection::StatusCode200); + return true; + } else if (url.path() == URI_ENTITY_FILE_UPLOAD) { + // this is an entity file upload, ask the HTTPConnection to parse the data + QList formData = connection->parseFormData(); + + Headers redirectHeaders; + + if (formData.size() > 0 && formData[0].second.size() > 0) { + // invoke our method to hand the new octree file off to the octree server + QMetaObject::invokeMethod(this, "handleOctreeFileReplacement", + Qt::QueuedConnection, Q_ARG(QByteArray, formData[0].second)); + + // respond with a 200 for success + connection->respond(HTTPConnection::StatusCode200); + } else { + // respond with a 400 for failure + connection->respond(HTTPConnection::StatusCode400); + } + return true; } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { @@ -2159,8 +2188,7 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR cookieHeaders.insert("Set-Cookie", cookieString.toUtf8()); // redirect the user back to the homepage so they can present their cookie and be authenticated - QString redirectString = "http://" + _hostname + ":" + QString::number(_httpManager.serverPort()); - cookieHeaders.insert("Location", redirectString.toUtf8()); + cookieHeaders.insert("Location", pathForRedirect().toUtf8()); return cookieHeaders; } @@ -2560,3 +2588,20 @@ void DomainServer::setupGroupCacheRefresh() { _metaverseGroupCacheTimer->start(REFRESH_GROUPS_INTERVAL_MSECS); } } + +void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { + // enumerate the nodes and find any octree type servers with active sockets + + auto limitedNodeList = DependencyManager::get(); + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node){ + return node->getType() == NodeType::EntityServer && node->getActiveSocket(); + }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode){ + // setup a packet to send to this octree server with the new octree file data + auto octreeFilePacketList = NLPacketList::create(PacketType::OctreeFileReplacement, QByteArray(), true, true); + octreeFilePacketList->write(octreeFile); + + qDebug() << "Sending an octree file replacement of" << octreeFile.size() << "bytes to" << octreeNode; + + limitedNodeList->sendPacketList(std::move(octreeFilePacketList), *octreeNode); + }); +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 4c5c42acee..63b82cb37d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -100,6 +100,8 @@ private slots: void handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply); void handleFailedICEServerAddressUpdate(QNetworkReply& requestReply); + void handleOctreeFileReplacement(QByteArray octreeFile); + signals: void iceServerChanged(); void userConnected(); @@ -161,6 +163,8 @@ private: void setupGroupCacheRefresh(); + QString pathForRedirect(QString path = QString()) const; + SubnetList _acSubnetWhitelist; DomainGatekeeper _gatekeeper; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index bd30cdd29c..adaa7a848c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -39,7 +39,7 @@ const QSet NON_SOURCED_PACKETS = QSet() << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat << PacketType::ICEServerHeartbeatACK << PacketType::ICEPing << PacketType::ICEPingReply << PacketType::ICEServerHeartbeatDenied << PacketType::AssignmentClientStatus << PacketType::StopNode - << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply; + << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply << PacketType::OctreeFileReplacement; PacketVersion versionForPacketType(PacketType packetType) { switch (packetType) { diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d312427ca7..f803b83887 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -113,7 +113,8 @@ public: EntityPhysics, EntityServerScriptLog, AdjustAvatarSorting, - LAST_PACKET_TYPE = AdjustAvatarSorting + OctreeFileReplacement, + LAST_PACKET_TYPE = OctreeFileReplacement }; }; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 7034790eaf..b79ce5537f 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -33,6 +33,7 @@ #include "OctreePersistThread.h" const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds +const QString OctreePersistThread::REPLACEMENT_FILE_EXTENSION = ".replace"; OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval, bool wantBackup, const QJsonObject& settings, bool debugTimestampNow, @@ -131,10 +132,46 @@ quint64 OctreePersistThread::getMostRecentBackupTimeInUsecs(const QString& forma return mostRecentBackupInUsecs; } +void OctreePersistThread::possiblyReplaceContent() { + // before we load the normal file, check if there's a pending replacement file + auto replacementFileName = _filename + REPLACEMENT_FILE_EXTENSION; + + QFile replacementFile { replacementFileName }; + if (replacementFile.exists()) { + // we have a replacement file to process + qDebug() << "Replacing models file with" << replacementFileName; + + // first take the current models file and move it to a different filename, appended with the timestamp + QFile currentFile { _filename }; + if (currentFile.exists()) { + static const QString FILENAME_TIMESTAMP_FORMAT = "yyyyMMdd-hhmmss"; + auto backupFileName = _filename + ".backup." + QDateTime::currentDateTime().toString(FILENAME_TIMESTAMP_FORMAT); + + if (currentFile.rename(backupFileName)) { + qDebug() << "Moved previous models file to" << backupFileName; + } else { + qWarning() << "Could not backup previous models file to" << backupFileName << "- removing replacement models file"; + + if (!QFile::remove(replacementFileName)) { + qWarning() << "Could not remove replacement models file from" << replacementFileName + << "- replacement will be re-attempted on next server restart"; + } + } + } + + // rename the replacement file to match what the persist thread is just about to read + if (!replacementFile.rename(_filename)) { + qWarning() << "Could not replace models file with" << replacementFileName << "- starting with empty models file"; + } + } +} + bool OctreePersistThread::process() { if (!_initialLoadComplete) { + possiblyReplaceContent(); + quint64 loadStarted = usecTimestampNow(); qCDebug(octree) << "loading Octrees from file: " << _filename << "..."; diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 927304e862..2441223467 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -32,6 +32,7 @@ public: }; static const int DEFAULT_PERSIST_INTERVAL; + static const QString REPLACEMENT_FILE_EXTENSION; OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false, @@ -60,6 +61,7 @@ protected: bool getMostRecentBackup(const QString& format, QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime); quint64 getMostRecentBackupTimeInUsecs(const QString& format); void parseSettings(const QJsonObject& settings); + void possiblyReplaceContent(); private: OctreePointer _tree; From 7c15609136372e27696a0f3c64f87c9f542b13d3 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 27 Apr 2017 17:28:10 -0700 Subject: [PATCH 065/292] Fixing the broken texture copy (during texture streaming) for compressed format in GL41Backend --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 +- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 3 +- libraries/gpu-gl/src/gpu/gl/GLTexture.h | 1 + libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 1 - .../src/gpu/gl41/GL41BackendTexture.cpp | 192 ++++++++++++++---- libraries/gpu/src/gpu/Texture.cpp | 8 +- libraries/gpu/src/gpu/Texture.h | 4 +- 7 files changed, 160 insertions(+), 51 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index c5c9b90eb2..1210112a78 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -48,7 +48,7 @@ BackendPointer GLBackend::createBackend() { // Where the gpuContext is initialized and where the TRUE Backend is created and assigned auto version = QOpenGLContextWrapper::currentContextVersion(); std::shared_ptr result; - if (false && !disableOpenGL45 && version >= 0x0405) { + if (!disableOpenGL45 && version >= 0x0405) { qCDebug(gpugllogging) << "Using OpenGL 4.5 backend"; result = std::make_shared(); } else { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 7f075a1698..95815993c2 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -102,7 +102,8 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : GLObject(backend, texture, id), _source(texture.source()), - _target(getGLTextureType(texture)) + _target(getGLTextureType(texture)), + _texelFormat(GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), texture.getStoredMipFormat())) { Backend::setGPUObject(texture, this); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index e0b8a63a99..99df5d94cf 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -153,6 +153,7 @@ public: const GLuint& _texture { _id }; const std::string _source; const GLenum _target; + GLTexelFormat _texelFormat; static const std::vector& getFaceTargets(GLenum textureType); static uint8_t getFaceCount(GLenum textureType); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index ef409d37f4..3662253a66 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -110,7 +110,6 @@ public: Size size() const override { return _size; } Size _size { 0 }; - GLTexelFormat _texelFormat; }; class GL41ResourceTexture : public GL41VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index d9fa2ad067..f199d210b3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -241,8 +241,7 @@ GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : - GL41Texture(backend, texture), - _texelFormat(GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), texture.getStoredMipFormat())) + GL41Texture(backend, texture) { auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; @@ -325,25 +324,87 @@ void GL41VariableAllocationTexture::promote() { // allocate storage for new level allocateStorage(targetAllocatedMip); - withPreservedTexture([&] { - if (false && _texelFormat.isCompressed()) { - uint16_t mips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); + if (_texelFormat.isCompressed()) { + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + uint16_t numMips = _gpuObject.getNumMips(); + struct MipDesc { + GLint _faceSize; + GLint _size; + GLint _offset; + gpu::Vec3u _dims; + }; + std::vector sourceMips(numMips); + + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + glBindTexture(_target, oldId); + const auto& faceTargets = getFaceTargets(_target); + GLint internalFormat { 0 }; + (void)CHECK_GL_ERROR(); + + // Collect the mip description from the source texture + GLint bufferOffset { 0 }; + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + sourceMip._dims = _gpuObject.evalMipDimensions(mip); + + uint16_t sourceLevel = mip - oldAllocatedMip; + + // Grab internal format once + if (internalFormat == 0) { + glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + } + + // Collect the size of the first face, and then compute the total size offset needed for this mip level + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); + sourceMip._size = faceTargets.size() * sourceMip._faceSize; + sourceMip._offset = bufferOffset; + bufferOffset += sourceMip._size; + } + (void)CHECK_GL_ERROR(); + + // Allocate the PBO to accomodate for all the mips to copy + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); + (void)CHECK_GL_ERROR(); + + // Transfer from source texture to pbo + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - oldAllocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + } + (void)CHECK_GL_ERROR(); + + } + + // Now populate the new texture from the pbo + glBindTexture(_target, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + + withPreservedTexture([&] { + // Transfer from pbo to new texture + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t destLevel = mip - _allocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._dims.x, sourceMip._dims.y, internalFormat, + sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); } } - } else { + syncSampler(); + }); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pbo); + } else { + withPreservedTexture([&] { GLuint fbo { 0 }; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); @@ -365,9 +426,10 @@ void GL41VariableAllocationTexture::promote() { // destroy the transfer framebuffer glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); - } - syncSampler(); - }); + + syncSampler(); + }); + } // destroy the old texture glDeleteTextures(1, &oldId); @@ -383,44 +445,86 @@ void GL41VariableAllocationTexture::demote() { auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; + uint16_t oldPopulatedMip = _populatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); if (_texelFormat.isCompressed()) { GLuint pbo { 0 }; glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + uint16_t numMips = _gpuObject.getNumMips(); + struct MipDesc { + GLint _faceSize; + GLint _size; + GLint _offset; + gpu::Vec3u _dims; + }; + std::vector sourceMips(numMips); glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); glBindTexture(_target, oldId); + const auto& faceTargets = getFaceTargets(_target); + GLint internalFormat { 0 }; - glGetTexLevelParameteriv(_target, sourceMip, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size); + // Collect the mip description from the source texture + GLint bufferOffset { 0 }; + for (uint16_t mip = oldPopulatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + sourceMip._dims = _gpuObject.evalMipDimensions(mip); - // copy pre-existing mips - uint16_t mips = _gpuObject.getNumMips(); - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - GLint size { 0 }; - GLint internalFormat; - glGetTexLevelParameteriv(_target, sourceMip, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size); + uint16_t sourceLevel = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); + // Grab internal format once + if (internalFormat != 0) { + glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); } + + // Collect the size of the first face, and then compute the total size offset needed for this mip level + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); + sourceMip._size = faceTargets.size() * sourceMip._faceSize; + sourceMip._offset = bufferOffset; + bufferOffset += sourceMip._size; + } + (void)CHECK_GL_ERROR(); + + // Allocate the PBO to accomodate for all the mips to copy + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); + (void)CHECK_GL_ERROR(); + + // Transfer from source texture to pbo + for (uint16_t mip = oldPopulatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - oldAllocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (sourceMip._offset + f * sourceMip._faceSize)); + } + (void)CHECK_GL_ERROR(); } + // Now populate the new texture from the pbo + glBindTexture(_target, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + + withPreservedTexture([&] { + // Transfer from pbo to new texture + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t destLevel = mip - _allocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._dims.x, sourceMip._dims.y, internalFormat, + sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + } + } + syncSampler(); + }); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glDeleteBuffers(1, &pbo); } else { @@ -429,9 +533,9 @@ void GL41VariableAllocationTexture::demote() { glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - uint16_t mips = _gpuObject.getNumMips(); + uint16_t numMips = _gpuObject.getNumMips(); // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { auto mipDimensions = _gpuObject.evalMipDimensions(mip); uint16_t targetMip = mip - _allocatedMip; uint16_t sourceMip = targetMip + 1; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 3e6ed166a7..353bcbc8f1 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -407,8 +407,12 @@ void Texture::setStoredMipFormat(const Element& format) { _storage->setFormat(format); } -const Element& Texture::getStoredMipFormat() const { - return _storage->getFormat(); +const Element Texture::getStoredMipFormat() const { + if (_storage) { + return _storage->getFormat(); + } else { + return Element(); + } } void Texture::assignStoredMip(uint16 level, Size size, const Byte* bytes) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9b23b4e695..21a1b0ec57 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -372,7 +372,7 @@ public: bool isColorRenderTarget() const; bool isDepthStencilRenderTarget() const; - const Element& getTexelFormat() const { return _texelFormat; } + const Element getTexelFormat() const { return _texelFormat; } Vec3u getDimensions() const { return Vec3u(_width, _height, _depth); } uint16 getWidth() const { return _width; } @@ -468,7 +468,7 @@ public: // Mip storage format is constant across all mips void setStoredMipFormat(const Element& format); - const Element& getStoredMipFormat() const; + const Element getStoredMipFormat() const; // Manually allocate the mips down until the specified maxMip // this is just allocating the sysmem version of it From d7dfab6405c4362b13a1b3bd743461ee4942b743 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 27 Apr 2017 17:31:46 -0700 Subject: [PATCH 066/292] Fixing the broken texture copy (during texture streaming) for compressed format in GL41Backend --- libraries/image/src/image/Image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index e60187d230..707a2e4496 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -28,9 +28,9 @@ using namespace gpu; #define CPU_MIPMAPS 1 -#define COMPRESS_COLOR_TEXTURES 1 +#define COMPRESS_COLOR_TEXTURES 0 #define COMPRESS_NORMALMAP_TEXTURES 0 // Disable Normalmap compression for now -#define COMPRESS_GRAYSCALE_TEXTURES 1 +#define COMPRESS_GRAYSCALE_TEXTURES 0 #define COMPRESS_CUBEMAP_TEXTURES 0 // Disable Cubemap compression for now static const glm::uvec2 SPARSE_PAGE_SIZE(128); From 764f6c69eaa4dbed7a06f0104b93864bb28c9367 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Apr 2017 17:39:25 -0700 Subject: [PATCH 067/292] Head input action will override the HMD for IK. --- interface/src/avatar/MyAvatar.cpp | 34 +++++----- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 37 +++++----- libraries/animation/src/Rig.cpp | 71 ++++---------------- libraries/animation/src/Rig.h | 13 ++-- scripts/developer/tests/viveMotionCapture.js | 44 ++++++++++++ 6 files changed, 97 insertions(+), 104 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9acbf14242..70d650e7a5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -83,21 +83,21 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -// 2 meter tall dude (in avatar coordinates) -static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.9f, 0.0f }; +// default values, used when avatar is missing joints... (avatar space) static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.8f, 0.0f }; +static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.7f, 0.0f }; +static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f }; +static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f }; static const glm::quat DEFAULT_AVATAR_NECK_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.5f, 0.0f }; +static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f }; static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180}; -static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.05f, 0.0f }; +static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f }; static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; -static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.1f, -0.9f, 0.0f }; // AJT: TODO: WRONG FIX ME -static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { Quaternions::IDENTITY }; // AJT: TODO: WRONG FIX ME -static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.1f, -0.9f, 0.0f }; // AJT: TODO: WRONG FIX ME -static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { Quaternions::IDENTITY }; // AJT: TODO: WRONG FIX ME +static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f}; +static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f }; +static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; +static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f }; MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), @@ -1433,14 +1433,12 @@ controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const { return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix); } -void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& headPose) { - bool inHmd = qApp->isHMDMode(); - Head* head = getHead(); - if (inHmd) { - _headControllerPoseInSensorFrameCache.set(headPose); - head->setDeltaPitch(headPose.rotation.x); - head->setDeltaYaw(headPose.rotation.y); - head->setDeltaRoll(headPose.rotation.z); +void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) { + if (controller::InputDevice::getLowVelocityFilter()) { + auto oldHeadPose = getHeadControllerPoseInSensorFrame(); + _headControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldHeadPose, head)); + } else { + _headControllerPoseInSensorFrameCache.set(head); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 01b6496e12..dc249e0411 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -460,7 +460,7 @@ public: controller::Pose getHipsControllerPoseInAvatarFrame() const; controller::Pose getSpine2ControllerPoseInAvatarFrame() const; - void setHeadControllerPoseInSensorFrame(const controller::Pose& headPose); + void setHeadControllerPoseInSensorFrame(const controller::Pose& head); controller::Pose getHeadControllerPoseInSensorFrame() const; controller::Pose getHeadControllerPoseInWorldFrame() const; controller::Pose getHeadControllerPoseInAvatarFrame() const; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ccee65d3e6..9fad9e09fb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -107,29 +107,27 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::HeadParameters headParams; - 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; - - headParams.rigHeadPosition = extractTranslation(rigHMDMat); - headParams.rigHeadOrientation = extractRotation(rigHMDMat); - headParams.worldHeadOrientation = extractRotation(worldHMDMat); + // 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 { - 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.worldHeadOrientation = myAvatar->getHeadControllerPoseInWorldFrame().getTranslation(); + 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. - headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame(); - headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); + // 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; } } @@ -152,7 +150,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.spine2Enabled = false; } - headParams.neckJointIndex = geometry.neckJointIndex; headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; _rig->updateFromHeadParameters(headParams, deltaTime); @@ -212,7 +209,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Model::updateRig(deltaTime, parentTransform); Rig::EyeParameters eyeParams; - eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; eyeParams.eyeLookAt = lookAt; eyeParams.eyeSaccade = head->getSaccade(); eyeParams.modelRotation = getRotation(); @@ -244,7 +240,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { head->setBaseRoll(glm::degrees(-eulers.z)); Rig::EyeParameters eyeParams; - eyeParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); eyeParams.eyeLookAt = lookAt; eyeParams.eyeSaccade = glm::vec3(0.0f); eyeParams.modelRotation = getRotation(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 53f76d82ff..700761b248 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -46,7 +46,6 @@ static bool isEqual(const glm::quat& p, const glm::quat& q) { const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f); const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f); const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f); -const glm::vec3 DEFAULT_NECK_POS(0.0f, 0.70f, 0.0f); void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { @@ -1020,7 +1019,7 @@ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { } void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { - updateNeckJoint(params.neckJointIndex, params); + updateHeadAnimVars(params); _animVars.set("isTalking", params.isTalking); _animVars.set("notIsTalking", !params.isTalking); @@ -1043,74 +1042,40 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { } void Rig::updateFromEyeParameters(const EyeParameters& params) { - updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, - params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); - updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, - params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade); } -void Rig::computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut, - glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const { +void Rig::computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const { // the input hmd values are in avatar/rig space const glm::vec3& hmdPosition = hmdPose.trans(); - const glm::quat& hmdOrientation = hmdPose.rot(); + + // the HMD looks down the negative z axis, but the head bone looks down the z axis, so apply a 180 degree rotation. + const glm::quat& hmdOrientation = hmdPose.rot() * Quaternions::Y_180; // TODO: cache jointIndices int rightEyeIndex = indexOfJoint("RightEye"); int leftEyeIndex = indexOfJoint("LeftEye"); int headIndex = indexOfJoint("Head"); - int neckIndex = indexOfJoint("Neck"); glm::vec3 absRightEyePos = rightEyeIndex != -1 ? getAbsoluteDefaultPose(rightEyeIndex).trans() : DEFAULT_RIGHT_EYE_POS; glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? getAbsoluteDefaultPose(leftEyeIndex).trans() : DEFAULT_LEFT_EYE_POS; glm::vec3 absHeadPos = headIndex != -1 ? getAbsoluteDefaultPose(headIndex).trans() : DEFAULT_HEAD_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? getAbsoluteDefaultPose(neckIndex).trans() : DEFAULT_NECK_POS; glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f; glm::vec3 eyeOffset = absCenterEyePos - absHeadPos; - glm::vec3 headOffset = absHeadPos - absNeckPos; - // apply simplistic head/neck model - - // head headPositionOut = hmdPosition - hmdOrientation * eyeOffset; + headOrientationOut = hmdOrientation; - - // neck - neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset); - - // slerp between default orientation and hmdOrientation - neckOrientationOut = safeMix(hmdOrientation, _animSkeleton->getRelativeDefaultPose(neckIndex).rot(), 0.5f); } -void Rig::updateNeckJoint(int index, const HeadParameters& params) { - if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { - glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); +void Rig::updateHeadAnimVars(const HeadParameters& params) { + if (_animSkeleton) { if (params.headEnabled) { - glm::vec3 headPos, neckPos; - glm::quat headRot, neckRot; - - AnimPose hmdPose(glm::vec3(1.0f), params.rigHeadOrientation * yFlip180, params.rigHeadPosition); - computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot); - - // debug rendering -#ifdef DEBUG_RENDERING - const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); - const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f); - - // transform from bone into avatar space - AnimPose headPose(glm::vec3(1), headRot, headPos); - DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red); - - // transform from bone into avatar space - AnimPose neckPose(glm::vec3(1), neckRot, neckPos); - DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green); -#endif - - _animVars.set("headPosition", headPos); - _animVars.set("headRotation", headRot); - + _animVars.set("headPosition", params.rigHeadPosition); + _animVars.set("headRotation", params.rigHeadOrientation); if (params.hipsEnabled) { // Since there is an explicit hips ik target, switch the head to use the more generic RotationAndPosition IK chain type. // this will allow the spine to bend more, ensuring that it can reach the head target position. @@ -1120,23 +1085,15 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { // but because the IK _hipsOffset is enabled, the hips will naturally follow underneath the head. _animVars.set("headType", (int)IKTarget::Type::HmdHead); } - _animVars.set("neckPosition", neckPos); - _animVars.set("neckRotation", neckRot); - _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target - } else { _animVars.unset("headPosition"); - _animVars.set("headRotation", params.rigHeadOrientation * yFlip180); - _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("headRotation", params.rigHeadOrientation); _animVars.set("headType", (int)IKTarget::Type::RotationOnly); - _animVars.unset("neckPosition"); - _animVars.unset("neckRotation"); - _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); } } } -void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { +void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { // TODO: does not properly handle avatar scale. diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index b66ca95042..2d024628f5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -44,18 +44,15 @@ public: struct HeadParameters { glm::mat4 hipsMatrix = glm::mat4(); // rig space glm::mat4 spine2Matrix = glm::mat4(); // rig space - glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward) glm::vec3 rigHeadPosition = glm::vec3(); // rig space bool hipsEnabled = false; bool headEnabled = false; bool spine2Enabled = false; - int neckJointIndex = -1; bool isTalking = false; }; struct EyeParameters { - glm::quat worldHeadOrientation = glm::quat(); glm::vec3 eyeLookAt = glm::vec3(); // world space glm::vec3 eyeSaccade = glm::vec3(); // world space glm::vec3 modelTranslation = glm::vec3(); @@ -232,6 +229,9 @@ public: void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; } + // input assumed to be in rig space + void computeHeadFromHMD(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut) const; + signals: void onLoadComplete(); @@ -241,10 +241,9 @@ protected: void applyOverridePoses(); void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut); - void updateNeckJoint(int index, const HeadParameters& params); - void computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut, - glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const; - void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + void updateHeadAnimVars(const HeadParameters& params); + + void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; AnimPose _modelOffset; // model to rig space diff --git a/scripts/developer/tests/viveMotionCapture.js b/scripts/developer/tests/viveMotionCapture.js index 4aa5cbf86f..e7fb8566dc 100644 --- a/scripts/developer/tests/viveMotionCapture.js +++ b/scripts/developer/tests/viveMotionCapture.js @@ -26,6 +26,7 @@ Controller.enableMapping(TRIGGER_MAPPING_NAME); var CONTROLLER_MAPPING_NAME = "com.highfidelity.viveMotionCapture.controller"; var controllerMapping; +var head; var leftFoot; var rightFoot; var hips; @@ -77,8 +78,29 @@ function computeDefaultToReferenceXform() { } } +function computeHeadOffsetXform() { + var leftEyeIndex = MyAvatar.getJointIndex("LeftEye"); + var rightEyeIndex = MyAvatar.getJointIndex("RightEye"); + var headIndex = MyAvatar.getJointIndex("Head"); + if (leftEyeIndex > 0 && rightEyeIndex > 0 && headIndex > 0) { + var defaultHeadXform = new Xform(MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(headIndex), + MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(headIndex)); + var defaultLeftEyeXform = new Xform(MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(leftEyeIndex), + MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(leftEyeIndex)); + var defaultRightEyeXform = new Xform(MyAvatar.getAbsoluteDefaultJointRotationInObjectFrame(rightEyeIndex), + MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(rightEyeIndex)); + var defaultCenterEyePos = Vec3.multiply(0.5, Vec3.sum(defaultLeftEyeXform.pos, defaultRightEyeXform.pos)); + var defaultCenterEyeXform = new Xform(defaultLeftEyeXform.rot, defaultCenterEyePos); + + return Xform.mul(defaultCenterEyeXform.inv(), defaultHeadXform); + } else { + return undefined; + } +} + function calibrate() { + head = undefined; leftFoot = undefined; rightFoot = undefined; hips = undefined; @@ -86,6 +108,13 @@ function calibrate() { var defaultToReferenceXform = computeDefaultToReferenceXform(); + var headOffsetXform = computeHeadOffsetXform(); + print("AJT: computed headOffsetXform " + (headOffsetXform ? JSON.stringify(headOffsetXform) : "undefined")); + + if (headOffsetXform) { + head = { offsetXform: headOffsetXform }; + } + var poses = []; if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { @@ -202,6 +231,7 @@ function update(dt) { // go back to normal, vive pucks will be ignored. print("AJT: UN-CALIBRATE!"); + head = undefined; leftFoot = undefined; rightFoot = undefined; hips = undefined; @@ -217,6 +247,20 @@ function update(dt) { controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + if (head) { + controllerMapping.from(function () { + var worldToAvatarXform = (new Xform(MyAvatar.orientation, MyAvatar.position)).inv(); + head.latestPose = { + valid: true, + translation: worldToAvatarXform.xformPoint(HMD.position), + rotation: Quat.multiply(worldToAvatarXform.rot, Quat.multiply(HMD.orientation, Y_180)), // postMult 180 rot flips head direction + velocity: {x: 0, y: 0, z: 0}, // TODO: currently this is unused anyway... + angularVelocity: {x: 0, y: 0, z: 0} + }; + return convertJointInfoToPose(head); + }).to(Controller.Standard.Head); + } + if (leftFoot) { controllerMapping.from(leftFoot.channel).to(function (pose) { leftFoot.latestPose = pose; From 038ac745c8af5fe8ced7d1bd4e3bc093d71fff50 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Apr 2017 17:47:18 -0700 Subject: [PATCH 068/292] clear domain octree details if ES killed --- interface/src/Application.cpp | 38 ++++++++--------------------------- interface/src/Application.h | 1 + 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 39a4b8ee7c..c8e61d87dc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -797,7 +797,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); - connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() { getOverlays().deleteOverlay(getTabletScreenID()); getOverlays().deleteOverlay(getTabletHomeButtonID()); @@ -5196,14 +5196,17 @@ void Application::clearDomainOctreeDetails() { skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); _recentlyClearedDomain = true; - - DependencyManager::get()->clearOtherAvatars(); + DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); } +void Application::clearDomainAvatars() { + DependencyManager::get()->clearOtherAvatars(); +} + void Application::domainChanged(const QString& domainHostname) { updateWindowTitle(); // disable physics until we have enough information about our new location to not cause craziness. @@ -5273,33 +5276,8 @@ void Application::nodeKilled(SharedNodePointer node) { if (node->getType() == NodeType::AudioMixer) { QMetaObject::invokeMethod(DependencyManager::get().data(), "audioMixerKilled"); } else if (node->getType() == NodeType::EntityServer) { - QUuid nodeUUID = node->getUUID(); - // see if this is the first we've heard of this node... - _entityServerJurisdictions.withReadLock([&] { - if (_entityServerJurisdictions.find(nodeUUID) == _entityServerJurisdictions.end()) { - return; - } - - auto rootCode = _entityServerJurisdictions[nodeUUID].getRootOctalCode(); - VoxelPositionSize rootDetails; - voxelDetailsForCode(rootCode.get(), rootDetails); - - qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]", - (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s); - - }); - - // If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server - _entityServerJurisdictions.withWriteLock([&] { - _entityServerJurisdictions.erase(_entityServerJurisdictions.find(nodeUUID)); - }); - - // also clean up scene stats for that server - _octreeServerSceneStats.withWriteLock([&] { - if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) { - _octreeServerSceneStats.erase(nodeUUID); - } - }); + // we lost an entity server, clear all of the domain octree details + clearDomainOctreeDetails(); } else if (node->getType() == NodeType::AvatarMixer) { // our avatar mixer has gone away - clear the hash of avatars DependencyManager::get()->clearOtherAvatars(); diff --git a/interface/src/Application.h b/interface/src/Application.h index dff1de2860..041f1f8930 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -409,6 +409,7 @@ public slots: private slots: void showDesktop(); void clearDomainOctreeDetails(); + void clearDomainAvatars(); void aboutToQuit(); void resettingDomain(); From 8240c3f76112d9c1b300eb50211886dda732ef3e Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 27 Apr 2017 18:05:52 -0700 Subject: [PATCH 069/292] Fixing the spam issue triggered by the external textures, removed 2 warnings --- libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h | 3 +++ libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 +- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 4 ++-- libraries/gpu/src/gpu/Texture.cpp | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h index 1edc10395c..8f37f6b604 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.h @@ -18,6 +18,9 @@ public: GLenum format; GLenum type; + GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {} + GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {} + bool isCompressed() const; static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 95815993c2..05c194405b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -103,7 +103,7 @@ GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& tex GLObject(backend, texture, id), _source(texture.source()), _target(getGLTextureType(texture)), - _texelFormat(GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), texture.getStoredMipFormat())) + _texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())) { Backend::setGPUObject(texture, this); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index f199d210b3..1f2cb20d04 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -358,7 +358,7 @@ void GL41VariableAllocationTexture::promote() { // Collect the size of the first face, and then compute the total size offset needed for this mip level glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); - sourceMip._size = faceTargets.size() * sourceMip._faceSize; + sourceMip._size = (GLint) faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; } @@ -482,7 +482,7 @@ void GL41VariableAllocationTexture::demote() { // Collect the size of the first face, and then compute the total size offset needed for this mip level glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); - sourceMip._size = faceTargets.size() * sourceMip._faceSize; + sourceMip._size = (GLint) faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; } diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 353bcbc8f1..b12ae5bc97 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -216,6 +216,7 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s TexturePointer Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { TexturePointer tex = std::make_shared(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; + tex->_texelFormat = Element::COLOR_RGBA_32; tex->_maxMipLevel = 0; tex->_sampler = sampler; tex->setExternalRecycler(recycler); From 9bc0609b1e2e6534d510354b234c330cbc72a1a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:17:48 -0700 Subject: [PATCH 070/292] address code review comments --- domain-server/resources/web/content/index.shtml | 11 +++++++---- domain-server/src/DomainServer.cpp | 4 ++-- interface/src/Application.cpp | 2 +- libraries/octree/src/OctreePersistThread.cpp | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/domain-server/resources/web/content/index.shtml b/domain-server/resources/web/content/index.shtml index 1d8d3fe1ab..c7eb765878 100644 --- a/domain-server/resources/web/content/index.shtml +++ b/domain-server/resources/web/content/index.shtml @@ -16,20 +16,23 @@

    - Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
    + Upload an entities file (e.g.: models.json.gz) to replace the content of this domain.
    Note: Your domain's content will be replaced by the content you upload, but the backup files of your domain's content will not immediately be changed.

    - If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored in C:\Users\[user_name]\AppData\Roaming\High Fidelity\assignment-client. + If your domain has any content that you would like to re-use at a later date, save a manual backup of your models.json.gz file, which is usually stored at the following paths:
    +

    C:\Users\[username]\AppData\Roaming\High Fidelity\assignment-client/entities/models.json.gz
    +
    /Users/[username]/Library/Application Support/High Fidelity/assignment-client/entities/models.json.gz
    +
    /home/[username]/.local/share/High Fidelity/assignment-client/entities/models.json.gz

    - +
    +
    - diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 233cb02dff..782c54419d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2593,9 +2593,9 @@ void DomainServer::handleOctreeFileReplacement(QByteArray octreeFile) { // enumerate the nodes and find any octree type servers with active sockets auto limitedNodeList = DependencyManager::get(); - limitedNodeList->eachMatchingNode([](const SharedNodePointer& node){ + limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) { return node->getType() == NodeType::EntityServer && node->getActiveSocket(); - }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode){ + }, [&octreeFile, limitedNodeList](const SharedNodePointer& octreeNode) { // setup a packet to send to this octree server with the new octree file data auto octreeFilePacketList = NLPacketList::create(PacketType::OctreeFileReplacement, QByteArray(), true, true); octreeFilePacketList->write(octreeFile); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c8e61d87dc..9fa66262cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5177,7 +5177,6 @@ void Application::clearDomainOctreeDetails() { qCDebug(interfaceapp) << "Clearing domain octree details..."; resetPhysicsReadyInformation(); - getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities // reset our node to stats and node to jurisdiction maps... since these must be changing... _entityServerJurisdictions.withWriteLock([&] { @@ -5204,6 +5203,7 @@ void Application::clearDomainOctreeDetails() { } void Application::clearDomainAvatars() { + getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities DependencyManager::get()->clearOtherAvatars(); } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index b79ce5537f..ea6bd28fc4 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -152,9 +152,10 @@ void OctreePersistThread::possiblyReplaceContent() { } else { qWarning() << "Could not backup previous models file to" << backupFileName << "- removing replacement models file"; - if (!QFile::remove(replacementFileName)) { + if (!replacementFile.remove()) { qWarning() << "Could not remove replacement models file from" << replacementFileName << "- replacement will be re-attempted on next server restart"; + return; } } } From 32c367b644181ecc3db953f5a8df5914df66fd0c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Apr 2017 10:18:15 -0700 Subject: [PATCH 071/292] use 'using' rather than 'typedef' --- libraries/physics/src/ObjectMotionState.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 1d258560c3..4230f636b3 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -170,7 +170,7 @@ protected: bool _hasInternalKinematicChanges { false }; }; -typedef QSet SetOfMotionStates; -typedef QVector VectorOfMotionStates; +using SetOfMotionStates = QSet; +using VectorOfMotionStates = QVector; #endif // hifi_ObjectMotionState_h From 994eed7b83a58b60a6a40f3e550b4366e9cd10aa Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Apr 2017 10:18:19 -0700 Subject: [PATCH 072/292] safer delete of AvatarMotionStates --- interface/src/avatar/AvatarManager.cpp | 52 ++++++++++---------------- interface/src/avatar/AvatarManager.h | 3 +- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ec22c3403a..04ab1531ba 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -289,16 +289,6 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { avatar->removeFromScene(*avatarItr, scene, transaction); scene->enqueueTransaction(transaction); } - - // delete the motionState - // TODO: use SharedPointer technology to make this happen automagically - assert(!avatar->isInPhysicsSimulation()); - AvatarMotionStateMap::iterator motionStateItr = _motionStates.find(avatar.get()); - if (motionStateItr != _motionStates.end()) { - delete *motionStateItr; - _motionStates.erase(motionStateItr); - } - avatarItr = _avatarsToFade.erase(avatarItr); } else { const bool inView = true; // HACK @@ -312,24 +302,19 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return std::make_shared(qApp->thread(), std::make_shared()); } -void AvatarManager::removeAvatarFromPhysicsSimulation(Avatar* avatar) { - assert(avatar); +void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { + AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); + + // remove from physics + auto avatar = std::static_pointer_cast(removedAvatar); avatar->setPhysicsCallback(nullptr); - AvatarMotionStateMap::iterator itr = _motionStates.find(avatar); + AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); if (itr != _motionStates.end()) { AvatarMotionState* motionState = *itr; _motionStatesToAddToPhysics.remove(motionState); _motionStatesToRemoveFromPhysics.push_back(motionState); + _motionStates.erase(itr); } -} - -void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { - AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); - - // removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar - // class in this context so we can call methods that don't exist at the base class. - auto avatar = std::static_pointer_cast(removedAvatar); - removeAvatarFromPhysicsSimulation(avatar.get()); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); @@ -370,14 +355,8 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - // delete motionStates - // TODO: use shared_ptr technology to make this work automagically - AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); - while (motionStateItr != _motionStates.end()) { - delete *motionStateItr; - ++motionStateItr; - } - _motionStates.clear(); + assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here + deleteMotionStates(); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); @@ -388,9 +367,18 @@ void AvatarManager::deleteAllAvatars() { } } +void AvatarManager::deleteMotionStates() { + // delete motionstates that were removed from physics last frame + for (auto state : _motionStatesToDelete) { + delete state; + } + _motionStatesToDelete.clear(); +} + void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { - result.clear(); - result.swap(_motionStatesToRemoveFromPhysics); + deleteMotionStates(); + result = _motionStatesToRemoveFromPhysics; + _motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics); } void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index e5b2117beb..c67088a4be 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -92,7 +92,7 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - void removeAvatarFromPhysicsSimulation(Avatar* avatar); + void deleteMotionStates(); void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; @@ -100,6 +100,7 @@ private: using AvatarMotionStateMap = QMap; AvatarMotionStateMap _motionStates; VectorOfMotionStates _motionStatesToRemoveFromPhysics; + VectorOfMotionStates _motionStatesToDelete; SetOfMotionStates _motionStatesToAddToPhysics; std::shared_ptr _myAvatar; From 4b0e7ceef5360827aae25c7a72eb97db70b34359 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Apr 2017 10:24:04 -0700 Subject: [PATCH 073/292] show hand lasers when edit is enabled, but not if there are not hand-controllers --- scripts/system/controllers/handControllerGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 6a7ed55417..ec70b0b1c8 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1801,15 +1801,15 @@ function MyController(hand) { } this.processStylus(); - - if (isInEditMode() && !this.isNearStylusTarget) { + + if (isInEditMode() && !this.isNearStylusTarget && HMD.isHandControllerAvailable()) { // Always showing lasers while in edit mode and hands/stylus is not active. var rayPickInfo = this.calcRayPickInfo(this.hand); this.intersectionDistance = (rayPickInfo.entityID || rayPickInfo.overlayID) ? rayPickInfo.distance : 0; this.searchIndicatorOn(rayPickInfo.searchRay); } else { this.searchIndicatorOff(); - } + } }; this.handleLaserOnHomeButton = function(rayPickInfo) { From b44a56db803889ac12ce93b01a70107434a60de3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:32:06 -0700 Subject: [PATCH 074/292] 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 f3cc1c2d86d7bcb5e568ac3dca7bedd6544ab906 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 28 Apr 2017 11:30:06 -0700 Subject: [PATCH 075/292] Added hipsContorllerTest.js for QA Testing --- scripts/developer/tests/hipsControllerTest.js | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 scripts/developer/tests/hipsControllerTest.js diff --git a/scripts/developer/tests/hipsControllerTest.js b/scripts/developer/tests/hipsControllerTest.js new file mode 100644 index 0000000000..5c6a4811e5 --- /dev/null +++ b/scripts/developer/tests/hipsControllerTest.js @@ -0,0 +1,105 @@ +// +// hipsControllerTest.js +// +// Created by Anthony Thibault on 4/24/17 +// Copyright 2017 High Fidelity, Inc. +// +// Test procedural manipulation of the Avatar hips via the controller system. +// Pull the left and right triggers on your hand controllers, you hips should begin to gyrate in an amusing mannor. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* global Xform */ +Script.include("/~/system/libraries/Xform.js"); + +var triggerPressHandled = false; +var rightTriggerPressed = false; +var leftTriggerPressed = false; + +var MAPPING_NAME = "com.highfidelity.hipsIkTest"; + +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from([Controller.Standard.RTClick]).peek().to(function (value) { + rightTriggerPressed = (value !== 0) ? true : false; +}); +mapping.from([Controller.Standard.LTClick]).peek().to(function (value) { + leftTriggerPressed = (value !== 0) ? true : false; +}); + +Controller.enableMapping(MAPPING_NAME); + +var CONTROLLER_MAPPING_NAME = "com.highfidelity.hipsIkTest.controller"; +var controllerMapping; + +var ZERO = {x: 0, y: 0, z: 0}; +var X_AXIS = {x: 1, y: 0, z: 0}; +var Y_AXIS = {x: 0, y: 1, z: 0}; +var Y_180 = {x: 0, y: 1, z: 0, w: 0}; +var Y_180_XFORM = new Xform(Y_180, {x: 0, y: 0, z: 0}); + +var hips = undefined; + +function computeCurrentXform(jointIndex) { + var currentXform = new Xform(MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex), + MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex)); + return currentXform; +} + +function calibrate() { + hips = computeCurrentXform(MyAvatar.getJointIndex("Hips")); +} + +function circleOffset(radius, theta, normal) { + var pos = {x: radius * Math.cos(theta), y: radius * Math.sin(theta), z: 0}; + var lookAtRot = Quat.lookAt(normal, ZERO, X_AXIS); + return Vec3.multiplyQbyV(lookAtRot, pos); +} + +var calibrationCount = 0; + +function update(dt) { + if (rightTriggerPressed && leftTriggerPressed) { + if (!triggerPressHandled) { + triggerPressHandled = true; + if (controllerMapping) { + hips = undefined; + Controller.disableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + controllerMapping = undefined; + } else { + calibrate(); + calibrationCount++; + controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + + var n = Y_AXIS; + var t = 0; + if (hips) { + controllerMapping.from(function () { + t += (1 / 60) * 4; + return { + valid: true, + translation: Vec3.sum(hips.pos, circleOffset(0.1, t, n)), + rotation: hips.rot, + velocity: ZERO, + angularVelocity: ZERO + }; + }).to(Controller.Standard.Hips); + } + Controller.enableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + } + } + } else { + triggerPressHandled = false; + } +} + +Script.update.connect(update); + +Script.scriptEnding.connect(function () { + Controller.disableMapping(MAPPING_NAME); + if (controllerMapping) { + Controller.disableMapping(CONTROLLER_MAPPING_NAME + calibrationCount); + } + Script.update.disconnect(update); +}); + From d0fa8fe88713ae4be7ad3e7662280cffca788758 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 28 Apr 2017 12:26:44 -0700 Subject: [PATCH 076/292] Debuging the bad transfers... --- .../gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 1f2cb20d04..fdc151e20b 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -386,7 +386,9 @@ void GL41VariableAllocationTexture::promote() { glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); - withPreservedTexture([&] { + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); + glBindTexture(_target, _texture); + // Transfer from pbo to new texture for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; @@ -394,12 +396,13 @@ void GL41VariableAllocationTexture::promote() { uint16_t destLevel = mip - _allocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._dims.x, sourceMip._dims.y, internalFormat, + glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._dims.x, sourceMip._dims.y, 0, sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); } } syncSampler(); - }); + + glBindTexture(_target, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); @@ -445,7 +448,6 @@ void GL41VariableAllocationTexture::demote() { auto oldSize = _size; const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; - uint16_t oldPopulatedMip = _populatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); @@ -469,7 +471,7 @@ void GL41VariableAllocationTexture::demote() { // Collect the mip description from the source texture GLint bufferOffset { 0 }; - for (uint16_t mip = oldPopulatedMip; mip < numMips; ++mip) { + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; sourceMip._dims = _gpuObject.evalMipDimensions(mip); @@ -493,7 +495,7 @@ void GL41VariableAllocationTexture::demote() { (void)CHECK_GL_ERROR(); // Transfer from source texture to pbo - for (uint16_t mip = oldPopulatedMip; mip < numMips; ++mip) { + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; uint16_t sourceLevel = mip - oldAllocatedMip; @@ -517,7 +519,7 @@ void GL41VariableAllocationTexture::demote() { uint16_t destLevel = mip - _allocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._dims.x, sourceMip._dims.y, internalFormat, + glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._dims.x, sourceMip._dims.y, 0, sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); } } From 58de67ba119268b45280b4a0fad83b91183e2d9b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 28 Apr 2017 14:12:00 -0700 Subject: [PATCH 077/292] Fix tests/controllers/src/main.cpp clang/gcc warnings --- tests/controllers/src/main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 2c8f361fac..e697bd501f 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -114,6 +114,12 @@ int main(int argc, char** argv) { last = now; InputCalibrationData calibrationData = { + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), glm::mat4(), glm::mat4(), glm::mat4() @@ -130,6 +136,12 @@ int main(int argc, char** argv) { { InputCalibrationData calibrationData = { + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), glm::mat4(), glm::mat4(), glm::mat4() From 086941356a842676ddb0ad05037df5f6f7edf6b5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:36:38 -0700 Subject: [PATCH 078/292] 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 bace47af7c8e7ace57ba2bd1e56c32bdcdfa0303 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 28 Apr 2017 14:46:27 -0700 Subject: [PATCH 079/292] 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 945952d51190dd300a62960940d68dd4e007393c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Apr 2017 10:32:06 -0700 Subject: [PATCH 080/292] 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 a99c2426468faef0af60213d2f37bbfd763d0cff Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 28 Apr 2017 15:52:32 -0700 Subject: [PATCH 081/292] increase minimum grip pressure to initiate handshake --- scripts/system/makeUserConnection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0ffea0c568..8d67e67700 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -13,7 +13,7 @@ var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m - var GRIP_MIN = 0.05; // goes from 0-1, so 5% pressed is pressed + var GRIP_MIN = 0.25; // goes from 0-1, so 25% pressed is pressed var MESSAGE_CHANNEL = "io.highfidelity.makeUserConnection"; var STATES = { INACTIVE: 0, From 765a06108621338507a0ea136b209e22f9029704 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:26:03 -0700 Subject: [PATCH 082/292] 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 89fb0a917b469592ed0d4587dbe70d0a738e5b3e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:27:44 -0700 Subject: [PATCH 083/292] 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 59f84a60c548233d4f7757a93fe2ae256816e93b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Sat, 29 Apr 2017 00:31:09 +0100 Subject: [PATCH 084/292] saving work --- .../qml/controls/TabletWebScreen.qml | 132 ++++++++++++++++++ .../resources/qml/controls/TabletWebView.qml | 12 ++ interface/resources/qml/controls/WebView.qml | 2 +- .../resources/qml/hifi/tablet/TabletRoot.qml | 21 +++ .../qml/hifi/tablet/TabletWebView.qml | 2 +- .../qml/hifi/tablet/WindowWebView.qml | 10 ++ 6 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 interface/resources/qml/controls/TabletWebScreen.qml create mode 100644 interface/resources/qml/hifi/tablet/WindowWebView.qml diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml new file mode 100644 index 0000000000..fec91046d8 --- /dev/null +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -0,0 +1,132 @@ +import QtQuick 2.5 +import QtWebEngine 1.1 +import QtWebChannel 1.0 +import "../controls-uit" as HiFiControls +import HFTabletWebEngineProfile 1.0 + +Item { + property alias url: root.url + property alias scriptURL: root.userScriptUrl + property alias eventBridge: eventBridgeWrapper.eventBridge + property alias canGoBack: root.canGoBack; + property var goBack: root.goBack; + property alias urlTag: root.urlTag + property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false + property bool keyboardRaised: false + property bool punctuationMode: false + + // FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface + // or provide HMDinfo object to QML in RenderableWebEntityItem and do the following. + /* + onKeyboardRaisedChanged: { + keyboardEnabled = HMDinfo.active; + } + */ + + QtObject { + id: eventBridgeWrapper + WebChannel.id: "eventBridgeWrapper" + property var eventBridge; + } + + property alias viewProfile: root.profile + + WebEngineView { + id: root + objectName: "webEngineView" + x: 0 + y: 0 + width: parent.width + height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height + + profile: HFTabletWebEngineProfile { + id: webviewProfile + storageName: "qmlTabletWebEngine" + } + + property string userScriptUrl: "" + + // creates a global EventBridge object. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + + // detects when to raise and lower virtual keyboard + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + // User script. + WebEngineScript { + id: userScript + sourceUrl: root.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + property string urlTag: "noDownload=false"; + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + property string newUrl: "" + + webChannel.registeredObjects: [eventBridgeWrapper] + + Component.onCompleted: { + // Ensure the JS from the web-engine makes it to our logging + root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); + }); + + root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)"; + } + + onFeaturePermissionRequested: { + grantFeaturePermission(securityOrigin, feature, true); + } + + onLoadingChanged: { + keyboardRaised = false; + punctuationMode = false; + keyboard.resetShiftMode(false); + + // Required to support clicking on "hifi://" links + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + var url = loadRequest.url.toString(); + url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + root.stop(); + } + } + } + } + + onNewViewRequested:{ + // desktop is not defined for web-entities or tablet + if (typeof desktop !== "undefined") { + desktop.openBrowserWindow(request, profile); + } else { + tabletRoot.openBrowserWindow(request, profile); + } + } + } + + HiFiControls.Keyboard { + id: keyboard + raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + +} diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 41cea625e2..e43441045f 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -25,6 +25,9 @@ Item { property bool isDesktop: false property bool removingPage: false property bool loadingPage: false + property alias webView: webview + property alias profile: webview.profile + property bool remove: false property int currentPage: -1 // used as a model for repeater @@ -110,6 +113,10 @@ Item { function closeWebEngine() { + if (remove) { + web.destroy(); + return; + } if (parentStackItem) { parentStackItem.pop(); } else { @@ -263,8 +270,13 @@ Item { } onNewViewRequested: { + console.log("--------------> new window opened <-------------"); request.openIn(webview); } + + onWindowCloseRequested: { + console.log("-------------> requested to cloes window <---------------"); + } } HiFiControls.Keyboard { diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 52f277520f..04ff731a25 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -113,7 +113,7 @@ Item { if (typeof desktop !== "undefined") { desktop.openBrowserWindow(request, profile); } else { - console.log("onNewViewRequested: desktop not defined"); + tabletRoot.openBrowserWindow(request, profile); } } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index 31e6174563..b19525d21a 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -1,7 +1,9 @@ import QtQuick 2.0 import Hifi 1.0 import QtQuick.Controls 1.4 +import HFTabletWebEngineProfile 1.0 import "../../dialogs" +import "../../controls" Item { id: tabletRoot @@ -11,6 +13,7 @@ Item { property var rootMenu; property var openModal: null; property var openMessage: null; + property var openBrowser: null; property string subMenu: "" signal showDesktop(); property bool shown: true @@ -21,6 +24,7 @@ Item { option = value; } + Component { id: profileCreator; HFTabletWebEngineProfile {} } Component { id: inputDialogBuilder; TabletQueryDialog { } } function inputDialog(properties) { openModal = inputDialogBuilder.createObject(tabletRoot, properties); @@ -101,8 +105,20 @@ Item { } } + function openBrowserWindow(request, profile) { + var component = Qt.createComponent("../../controls/TabletWebView.qml"); + var newWindow = component.createObject(tabletRoot); + newWindow.eventBridge = tabletRoot.eventBridge; + newWindow.remove = true; + newWindow.profile = profile; + request.openIn(newWindow.webView); + tabletRoot.openBrowser = newWindow; + } + function loadWebUrl(url, injectedJavaScriptUrl) { tabletApps.clear(); + var newProfile = profileCreator.createObject(); + loader.item.viewProfile = newProfile; loader.item.url = url; loader.item.scriptURL = injectedJavaScriptUrl; tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url}); @@ -180,6 +196,11 @@ Item { openModal.destroy(); openModal = null; } + + if (openBrowser) { + openBrowser.destroy(); + openBrowser = null; + } } } diff --git a/interface/resources/qml/hifi/tablet/TabletWebView.qml b/interface/resources/qml/hifi/tablet/TabletWebView.qml index 0f697d634e..ff6be0480f 100644 --- a/interface/resources/qml/hifi/tablet/TabletWebView.qml +++ b/interface/resources/qml/hifi/tablet/TabletWebView.qml @@ -3,7 +3,7 @@ import QtWebEngine 1.2 import "../../controls" as Controls -Controls.WebView { +Controls.TabletWebScreen { } diff --git a/interface/resources/qml/hifi/tablet/WindowWebView.qml b/interface/resources/qml/hifi/tablet/WindowWebView.qml new file mode 100644 index 0000000000..0f697d634e --- /dev/null +++ b/interface/resources/qml/hifi/tablet/WindowWebView.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import QtWebEngine 1.2 + +import "../../controls" as Controls + +Controls.WebView { + +} + + From 5e1bc0d908d63f81e852891c2efc27b1d6596fd7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Apr 2017 14:28:03 -0700 Subject: [PATCH 085/292] Remove faceshift --- cmake/externals/faceshift/CMakeLists.txt | 47 --- cmake/macros/TargetFaceshift.cmake | 14 - cmake/modules/FindFaceshift.cmake | 26 -- interface/CMakeLists.txt | 5 - .../resources/icons/tablet-icons/goto-msg.svg | 36 +- interface/src/Application.cpp | 25 +- interface/src/Menu.cpp | 12 +- interface/src/Menu.h | 1 - interface/src/avatar/Head.cpp | 17 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/devices/DeviceTracker.cpp | 3 - interface/src/devices/DeviceTracker.h | 3 - interface/src/devices/EyeTracker.cpp | 9 +- interface/src/devices/EyeTracker.h | 3 - interface/src/devices/FaceTracker.cpp | 40 ++- interface/src/devices/FaceTracker.h | 38 ++- interface/src/devices/Faceshift.cpp | 310 ------------------ interface/src/devices/Faceshift.h | 155 --------- interface/src/devices/Logging.cpp | 11 + interface/src/devices/Logging.h | 16 + interface/src/devices/MotionTracker.cpp | 5 - interface/src/devices/MotionTracker.h | 18 +- interface/src/ui/PreferencesDialog.cpp | 8 - libraries/avatars/src/AvatarData.cpp | 6 +- libraries/avatars/src/AvatarData.h | 4 +- libraries/ui/src/ui/Menu.cpp | 12 + libraries/ui/src/ui/Menu.h | 10 + 27 files changed, 150 insertions(+), 688 deletions(-) delete mode 100644 cmake/externals/faceshift/CMakeLists.txt delete mode 100644 cmake/macros/TargetFaceshift.cmake delete mode 100644 cmake/modules/FindFaceshift.cmake delete mode 100644 interface/src/devices/Faceshift.cpp delete mode 100644 interface/src/devices/Faceshift.h create mode 100644 interface/src/devices/Logging.cpp create mode 100644 interface/src/devices/Logging.h diff --git a/cmake/externals/faceshift/CMakeLists.txt b/cmake/externals/faceshift/CMakeLists.txt deleted file mode 100644 index c4f2055435..0000000000 --- a/cmake/externals/faceshift/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -set(EXTERNAL_NAME faceshift) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip - CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= - BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 -) - -# URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory") - -set(LIBRARY_DEBUG_PATH "lib/Debug") -set(LIBRARY_RELEASE_PATH "lib/Release") - -if (WIN32) - set(LIBRARY_PREFIX "") - set(LIBRARY_EXT "lib") - # use selected configuration in release path when building on Windows - set(LIBRARY_RELEASE_PATH "$<$:build/RelWithDebInfo>") - set(LIBRARY_RELEASE_PATH "${LIBRARY_RELEASE_PATH}$<$:build/MinSizeRel>") - set(LIBRARY_RELEASE_PATH "${LIBRARY_RELEASE_PATH}$<$,$>:lib/Release>") -elseif (APPLE) - set(LIBRARY_EXT "a") - set(LIBRARY_PREFIX "lib") - - if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") - set(LIBRARY_DEBUG_PATH "build") - set(LIBRARY_RELEASE_PATH "build") - endif () -endif() - -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG - ${INSTALL_DIR}/${LIBRARY_DEBUG_PATH}/${LIBRARY_PREFIX}faceshift.${LIBRARY_EXT} CACHE FILEPATH "Faceshift libraries") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE -${INSTALL_DIR}/${LIBRARY_RELEASE_PATH}/${LIBRARY_PREFIX}faceshift.${LIBRARY_EXT} CACHE FILEPATH "Faceshift libraries") diff --git a/cmake/macros/TargetFaceshift.cmake b/cmake/macros/TargetFaceshift.cmake deleted file mode 100644 index 99f65d942a..0000000000 --- a/cmake/macros/TargetFaceshift.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright 2015 High Fidelity, Inc. -# Created by Bradley Austin Davis on 2015/10/10 -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# -macro(TARGET_FACESHIFT) - add_dependency_external_projects(faceshift) - find_package(Faceshift REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) - add_definitions(-DHAVE_FACESHIFT) -endmacro() \ No newline at end of file diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake deleted file mode 100644 index bd77951273..0000000000 --- a/cmake/modules/FindFaceshift.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# -# FindFaceshift.cmake -# -# Try to find the Faceshift networking library -# -# You must provide a FACESHIFT_ROOT_DIR which contains lib and include directories -# -# Once done this will define -# -# FACESHIFT_FOUND - system found Faceshift -# FACESHIFT_INCLUDE_DIRS - the Faceshift include directory -# FACESHIFT_LIBRARIES - Link this to use Faceshift -# -# Created on 8/30/2013 by Andrzej Kapolka -# Copyright 2013 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(SelectLibraryConfigurations) -select_library_configurations(FACESHIFT) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) -mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4d58d70075..e3d01826b3 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -202,7 +202,6 @@ link_hifi_libraries( # include the binary directory of render-utils for shader includes target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils") -#fixme find a way to express faceshift as a plugin target_bullet() target_opengl() @@ -210,10 +209,6 @@ if (NOT ANDROID) target_glew() endif () -if (WIN32 OR APPLE) - target_faceshift() -endif() - # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/interface/resources/icons/tablet-icons/goto-msg.svg b/interface/resources/icons/tablet-icons/goto-msg.svg index 9b576ab1bf..ef905b6066 100644 --- a/interface/resources/icons/tablet-icons/goto-msg.svg +++ b/interface/resources/icons/tablet-icons/goto-msg.svg @@ -1,18 +1,18 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 39a4b8ee7c..306fa99852 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -137,7 +137,6 @@ #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" -#include "devices/Faceshift.h" #include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" @@ -480,7 +479,6 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1209,10 +1207,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo this->installEventFilter(this); - // initialize our face trackers after loading the menu settings - auto faceshiftTracker = DependencyManager::get(); - faceshiftTracker->init(); - connect(faceshiftTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #ifdef HAVE_DDE auto ddeTracker = DependencyManager::get(); ddeTracker->init(); @@ -3624,20 +3618,13 @@ ivec2 Application::getMouse() const { } FaceTracker* Application::getActiveFaceTracker() { - auto faceshift = DependencyManager::get(); auto dde = DependencyManager::get(); - return (dde->isActive() ? static_cast(dde.data()) : - (faceshift->isActive() ? static_cast(faceshift.data()) : nullptr)); + return dde->isActive() ? static_cast(dde.data()) : nullptr; } FaceTracker* Application::getSelectedFaceTracker() { FaceTracker* faceTracker = nullptr; -#ifdef HAVE_FACESHIFT - if (Menu::getInstance()->isOptionChecked(MenuOption::Faceshift)) { - faceTracker = DependencyManager::get().data(); - } -#endif #ifdef HAVE_DDE if (Menu::getInstance()->isOptionChecked(MenuOption::UseCamera)) { faceTracker = DependencyManager::get().data(); @@ -3647,15 +3634,8 @@ FaceTracker* Application::getSelectedFaceTracker() { } void Application::setActiveFaceTracker() const { -#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE) - bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking); -#endif -#ifdef HAVE_FACESHIFT - auto faceshiftTracker = DependencyManager::get(); - faceshiftTracker->setIsMuted(isMuted); - faceshiftTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && !isMuted); -#endif #ifdef HAVE_DDE + bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking); bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera); Menu::getInstance()->getActionForOption(MenuOption::BinaryEyelidControl)->setVisible(isUsingDDE); Menu::getInstance()->getActionForOption(MenuOption::CoupleEyelids)->setVisible(isUsingDDE); @@ -5131,7 +5111,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } void Application::resetSensors(bool andReload) { - DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); getActiveDisplayPlugin()->resetSensors(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8754951317..fcd539ca7d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -34,7 +34,6 @@ #include "avatar/AvatarManager.h" #include "AvatarBookmarks.h" #include "devices/DdeFaceTracker.h" -#include "devices/Faceshift.h" #include "MainWindow.h" #include "render/DrawStatus.h" #include "scripting/MenuScriptingInterface.h" @@ -451,12 +450,6 @@ Menu::Menu() { qApp, SLOT(setActiveFaceTracker())); faceTrackerGroup->addAction(noFaceTracker); -#ifdef HAVE_FACESHIFT - QAction* faceshiftFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Faceshift, - 0, false, - qApp, SLOT(setActiveFaceTracker())); - faceTrackerGroup->addAction(faceshiftFaceTracker); -#endif #ifdef HAVE_DDE QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, 0, true, @@ -477,11 +470,10 @@ Menu::Menu() { QAction* ddeCalibrate = addActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::CalibrateCamera, 0, DependencyManager::get().data(), SLOT(calibrate())); ddeCalibrate->setVisible(true); // DDE face tracking is on by default -#endif -#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE) faceTrackingMenu->addSeparator(); addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking, - Qt::CTRL | Qt::SHIFT | Qt::Key_F, true); // DDE face tracking is on by default + [](bool mute) { FaceTracker::setIsMuted(mute); }, + Qt::CTRL | Qt::SHIFT | Qt::Key_F, FaceTracker::isMuted()); addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false); #endif diff --git a/interface/src/Menu.h b/interface/src/Menu.h index eeffcac7ca..479a78e7c2 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -105,7 +105,6 @@ namespace MenuOption { const QString ExpandPaintGLTiming = "Expand /paintGL"; const QString ExpandPhysicsSimulationTiming = "Expand /physics"; const QString ExpandUpdateTiming = "Expand /update"; - const QString Faceshift = "Faceshift"; const QString FirstPerson = "First Person"; const QString FivePointCalibration = "5 Point Calibration"; const QString FixGaze = "Fix Gaze (no saccade)"; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 282acf6bf5..f2eeba9d60 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -23,7 +23,6 @@ #include "Util.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" -#include "devices/Faceshift.h" #include using namespace std; @@ -209,14 +208,14 @@ void Head::simulate(float deltaTime, bool isMine) { // use data to update fake Faceshift blendshape coefficients calculateMouthShapes(deltaTime); - DependencyManager::get()->updateFakeCoefficients(_leftEyeBlink, - _rightEyeBlink, - _browAudioLift, - _audioJawOpen, - _mouth2, - _mouth3, - _mouth4, - _blendshapeCoefficients); + FaceTracker::updateFakeCoefficients(_leftEyeBlink, + _rightEyeBlink, + _browAudioLift, + _audioJawOpen, + _mouth2, + _mouth3, + _mouth4, + _blendshapeCoefficients); applyEyelidOffset(getOrientation()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7bc961c654..b8c7656839 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -41,9 +41,9 @@ #include #include #include +#include #include "Application.h" -#include "devices/Faceshift.h" #include "AvatarManager.h" #include "AvatarActionHold.h" #include "Menu.h" @@ -650,7 +650,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } FaceTracker* tracker = qApp->getActiveFaceTracker(); - bool inFacetracker = tracker && !tracker->isMuted(); + bool inFacetracker = tracker && !FaceTracker::isMuted(); if (inHmd) { estimatedPosition = extractTranslation(getHMDSensorMatrix()); diff --git a/interface/src/devices/DeviceTracker.cpp b/interface/src/devices/DeviceTracker.cpp index 2cd4950064..93aeb607bc 100644 --- a/interface/src/devices/DeviceTracker.cpp +++ b/interface/src/devices/DeviceTracker.cpp @@ -1,7 +1,4 @@ // -// DeviceTracker.cpp -// interface/src/devices -// // Created by Sam Cake on 6/20/14. // Copyright 2014 High Fidelity, Inc. // diff --git a/interface/src/devices/DeviceTracker.h b/interface/src/devices/DeviceTracker.h index 543e9bab1a..8a7f509cb3 100644 --- a/interface/src/devices/DeviceTracker.h +++ b/interface/src/devices/DeviceTracker.h @@ -1,7 +1,4 @@ // -// DeviceTracker.h -// interface/src/devices -// // Created by Sam Cake on 6/20/14. // Copyright 2014 High Fidelity, Inc. // diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 367aa52aae..8733461dbb 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -1,7 +1,4 @@ // -// EyeTracker.cpp -// interface/src/devices -// // Created by David Rowe on 27 Jul 2015. // Copyright 2015 High Fidelity, Inc. // @@ -17,8 +14,8 @@ #include -#include "InterfaceLogging.h" -#include "OctreeConstants.h" +#include "Logging.h" +#include #ifdef HAVE_IVIEWHMD char* HIGH_FIDELITY_EYE_TRACKER_CALIBRATION = "HighFidelityEyeTrackerCalibration"; @@ -115,7 +112,7 @@ void EyeTracker::processData(smi_CallbackDataStruct* data) { void EyeTracker::init() { if (_isInitialized) { - qCWarning(interfaceapp) << "Eye Tracker: Already initialized"; + qCWarning(trackers) << "Eye Tracker: Already initialized"; return; } } diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 0e760d9454..9cf35d0f2a 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -1,7 +1,4 @@ // -// EyeTracker.h -// interface/src/devices -// // Created by David Rowe on 27 Jul 2015. // Copyright 2015 High Fidelity, Inc. // diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp index 76a4534952..034787f19a 100644 --- a/interface/src/devices/FaceTracker.cpp +++ b/interface/src/devices/FaceTracker.cpp @@ -1,7 +1,4 @@ // -// FaceTracker.cpp -// interface/src/devices -// // Created by Andrzej Kapolka on 4/9/14. // Copyright 2014 High Fidelity, Inc. // @@ -9,22 +6,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include - #include "FaceTracker.h" -#include "InterfaceLogging.h" -#include "Menu.h" + +#include +#include +#include "Logging.h" +//#include "Menu.h" const int FPS_TIMER_DELAY = 2000; // ms const int FPS_TIMER_DURATION = 2000; // ms const float DEFAULT_EYE_DEFLECTION = 0.25f; Setting::Handle FaceTracker::_eyeDeflection("faceshiftEyeDeflection", DEFAULT_EYE_DEFLECTION); +bool FaceTracker::_isMuted { true }; void FaceTracker::init() { - _isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking); _isInitialized = true; // FaceTracker can be used now } @@ -101,7 +97,7 @@ void FaceTracker::countFrame() { } void FaceTracker::finishFPSTimer() { - qCDebug(interfaceapp) << "Face tracker FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f); + qCDebug(trackers) << "Face tracker FPS =" << (float)_frameCount / ((float)FPS_TIMER_DURATION / 1000.0f); _isCalculatingFPS = false; } @@ -113,3 +109,25 @@ void FaceTracker::toggleMute() { void FaceTracker::setEyeDeflection(float eyeDeflection) { _eyeDeflection.set(eyeDeflection); } + +void FaceTracker::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, + float jawOpen, float mouth2, float mouth3, float mouth4, QVector& coefficients) { + const int MMMM_BLENDSHAPE = 34; + const int FUNNEL_BLENDSHAPE = 40; + const int SMILE_LEFT_BLENDSHAPE = 28; + const int SMILE_RIGHT_BLENDSHAPE = 29; + const int MAX_FAKE_BLENDSHAPE = 40; // Largest modified blendshape from above and below + + coefficients.resize(std::max((int)coefficients.size(), MAX_FAKE_BLENDSHAPE + 1)); + qFill(coefficients.begin(), coefficients.end(), 0.0f); + coefficients[_leftBlinkIndex] = leftBlink; + coefficients[_rightBlinkIndex] = rightBlink; + coefficients[_browUpCenterIndex] = browUp; + coefficients[_browUpLeftIndex] = browUp; + coefficients[_browUpRightIndex] = browUp; + coefficients[_jawOpenIndex] = jawOpen; + coefficients[SMILE_LEFT_BLENDSHAPE] = coefficients[SMILE_RIGHT_BLENDSHAPE] = mouth4; + coefficients[MMMM_BLENDSHAPE] = mouth2; + coefficients[FUNNEL_BLENDSHAPE] = mouth3; +} + diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h index 7126d19ca8..58d5c5e574 100644 --- a/interface/src/devices/FaceTracker.h +++ b/interface/src/devices/FaceTracker.h @@ -1,7 +1,4 @@ // -// FaceTracker.h -// interface/src/devices -// // Created by Andrzej Kapolka on 4/9/14. // Copyright 2014 High Fidelity, Inc. // @@ -20,7 +17,7 @@ #include -/// Base class for face trackers (Faceshift, DDE). +/// Base class for face trackers (DDE, BinaryVR). class FaceTracker : public QObject { Q_OBJECT @@ -45,12 +42,21 @@ public: const QVector& getBlendshapeCoefficients() const; float getBlendshapeCoefficient(int index) const; - bool isMuted() const { return _isMuted; } - void setIsMuted(bool isMuted) { _isMuted = isMuted; } + static bool isMuted() { return _isMuted; } + static void setIsMuted(bool isMuted) { _isMuted = isMuted; } static float getEyeDeflection() { return _eyeDeflection.get(); } static void setEyeDeflection(float eyeDeflection); + static void updateFakeCoefficients(float leftBlink, + float rightBlink, + float browUp, + float jawOpen, + float mouth2, + float mouth3, + float mouth4, + QVector& coefficients); + signals: void muteToggled(); @@ -63,7 +69,7 @@ protected: virtual ~FaceTracker() {}; bool _isInitialized = false; - bool _isMuted = true; + static bool _isMuted; glm::vec3 _headTranslation = glm::vec3(0.0f); glm::quat _headRotation = glm::quat(); @@ -84,6 +90,24 @@ private: bool _isCalculatingFPS = false; int _frameCount = 0; + // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes + static const int _leftBlinkIndex = 0; + static const int _rightBlinkIndex = 1; + static const int _leftEyeOpenIndex = 8; + static const int _rightEyeOpenIndex = 9; + + // Brows + static const int _browDownLeftIndex = 14; + static const int _browDownRightIndex = 15; + static const int _browUpCenterIndex = 16; + static const int _browUpLeftIndex = 17; + static const int _browUpRightIndex = 18; + + static const int _mouthSmileLeftIndex = 28; + static const int _mouthSmileRightIndex = 29; + + static const int _jawOpenIndex = 21; + static Setting::Handle _eyeDeflection; }; diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp deleted file mode 100644 index 81c099c740..0000000000 --- a/interface/src/devices/Faceshift.cpp +++ /dev/null @@ -1,310 +0,0 @@ -// -// Faceshift.cpp -// interface/src/devices -// -// Created by Andrzej Kapolka on 9/3/13. -// Copyright 2013 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 - -#include -#include -#include - -#include "Faceshift.h" -#include "Menu.h" -#include "Util.h" -#include "InterfaceLogging.h" - -#ifdef HAVE_FACESHIFT -using namespace fs; -#endif - -using namespace std; - -const QString DEFAULT_FACESHIFT_HOSTNAME = "localhost"; -const quint16 FACESHIFT_PORT = 33433; - -Faceshift::Faceshift() : - _hostname("faceshiftHostname", DEFAULT_FACESHIFT_HOSTNAME) -{ -#ifdef HAVE_FACESHIFT - connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected())); - connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError))); - connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket())); - connect(&_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(connectionStateChanged())); - connect(&_tcpSocket, SIGNAL(disconnected()), SLOT(noteDisconnected())); - - connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); - - _udpSocket.bind(FACESHIFT_PORT); -#endif -} - -#ifdef HAVE_FACESHIFT -void Faceshift::init() { - FaceTracker::init(); - setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && !_isMuted); -} - -void Faceshift::update(float deltaTime) { - if (!isActive()) { - return; - } - FaceTracker::update(deltaTime); - - // get the euler angles relative to the window - glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3( - (_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))))); - - // compute and subtract the long term average - const float LONG_TERM_AVERAGE_SMOOTHING = 0.999f; - if (!_longTermAverageInitialized) { - _longTermAverageEyePitch = eulers.x; - _longTermAverageEyeYaw = eulers.y; - _longTermAverageInitialized = true; - - } else { - _longTermAverageEyePitch = glm::mix(eulers.x, _longTermAverageEyePitch, LONG_TERM_AVERAGE_SMOOTHING); - _longTermAverageEyeYaw = glm::mix(eulers.y, _longTermAverageEyeYaw, LONG_TERM_AVERAGE_SMOOTHING); - } - _estimatedEyePitch = eulers.x - _longTermAverageEyePitch; - _estimatedEyeYaw = eulers.y - _longTermAverageEyeYaw; -} - -void Faceshift::reset() { - if (_tcpSocket.state() == QAbstractSocket::ConnectedState) { - qCDebug(interfaceapp, "Faceshift: Reset"); - - FaceTracker::reset(); - - string message; - fsBinaryStream::encode_message(message, fsMsgCalibrateNeutral()); - send(message); - } - _longTermAverageInitialized = false; -} - -bool Faceshift::isActive() const { - const quint64 ACTIVE_TIMEOUT_USECS = 1000000; - return (usecTimestampNow() - _lastReceiveTimestamp) < ACTIVE_TIMEOUT_USECS; -} - -bool Faceshift::isTracking() const { - return isActive() && _tracking; -} -#endif - - -bool Faceshift::isConnectedOrConnecting() const { - return _tcpSocket.state() == QAbstractSocket::ConnectedState || - (_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState); -} - -void Faceshift::updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, - float jawOpen, float mouth2, float mouth3, float mouth4, QVector& coefficients) const { - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - const int MAX_FAKE_BLENDSHAPE = 40; // Largest modified blendshape from above and below - - coefficients.resize(max((int)coefficients.size(), MAX_FAKE_BLENDSHAPE + 1)); - qFill(coefficients.begin(), coefficients.end(), 0.0f); - coefficients[_leftBlinkIndex] = leftBlink; - coefficients[_rightBlinkIndex] = rightBlink; - coefficients[_browUpCenterIndex] = browUp; - coefficients[_browUpLeftIndex] = browUp; - coefficients[_browUpRightIndex] = browUp; - coefficients[_jawOpenIndex] = jawOpen; - coefficients[SMILE_LEFT_BLENDSHAPE] = coefficients[SMILE_RIGHT_BLENDSHAPE] = mouth4; - coefficients[MMMM_BLENDSHAPE] = mouth2; - coefficients[FUNNEL_BLENDSHAPE] = mouth3; -} - -void Faceshift::setEnabled(bool enabled) { - // Don't enable until have explicitly initialized - if (!_isInitialized) { - return; - } -#ifdef HAVE_FACESHIFT - if ((_tcpEnabled = enabled)) { - connectSocket(); - } else { - qCDebug(interfaceapp, "Faceshift: Disconnecting..."); - _tcpSocket.disconnectFromHost(); - } -#endif -} - -void Faceshift::connectSocket() { - if (_tcpEnabled) { - if (!_tcpRetryCount) { - qCDebug(interfaceapp, "Faceshift: Connecting..."); - } - - _tcpSocket.connectToHost(_hostname.get(), FACESHIFT_PORT); - _tracking = false; - } -} - -void Faceshift::noteConnected() { -#ifdef HAVE_FACESHIFT - qCDebug(interfaceapp, "Faceshift: Connected"); - // request the list of blendshape names - string message; - fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames()); - send(message); -#endif -} - -void Faceshift::noteDisconnected() { -#ifdef HAVE_FACESHIFT - qCDebug(interfaceapp, "Faceshift: Disconnected"); -#endif -} - -void Faceshift::noteError(QAbstractSocket::SocketError error) { - if (!_tcpRetryCount) { - // Only spam log with fail to connect the first time, so that we can keep waiting for server - qCWarning(interfaceapp) << "Faceshift: " << _tcpSocket.errorString(); - } - // retry connection after a 2 second delay - if (_tcpEnabled) { - _tcpRetryCount++; - QTimer::singleShot(2000, this, SLOT(connectSocket())); - } -} - -void Faceshift::readPendingDatagrams() { - QByteArray buffer; - while (_udpSocket.hasPendingDatagrams()) { - buffer.resize(_udpSocket.pendingDatagramSize()); - _udpSocket.readDatagram(buffer.data(), buffer.size()); - receive(buffer); - } -} - -void Faceshift::readFromSocket() { - receive(_tcpSocket.readAll()); -} - -void Faceshift::send(const std::string& message) { - _tcpSocket.write(message.data(), message.size()); -} - -void Faceshift::receive(const QByteArray& buffer) { -#ifdef HAVE_FACESHIFT - _lastReceiveTimestamp = usecTimestampNow(); - - _stream.received(buffer.size(), buffer.constData()); - fsMsgPtr msg; - for (fsMsgPtr msg; (msg = _stream.get_message()); ) { - switch (msg->id()) { - case fsMsg::MSG_OUT_TRACKING_STATE: { - const fsTrackingData& data = static_pointer_cast(msg)->tracking_data(); - if ((_tracking = data.m_trackingSuccessful)) { - glm::quat newRotation = glm::quat(data.m_headRotation.w, -data.m_headRotation.x, - data.m_headRotation.y, -data.m_headRotation.z); - // Compute angular velocity of the head - glm::quat r = glm::normalize(newRotation * glm::inverse(_headRotation)); - float theta = 2 * acos(r.w); - if (theta > EPSILON) { - float rMag = glm::length(glm::vec3(r.x, r.y, r.z)); - _headAngularVelocity = theta / _averageFrameTime * glm::vec3(r.x, r.y, r.z) / rMag; - } else { - _headAngularVelocity = glm::vec3(0,0,0); - } - const float ANGULAR_VELOCITY_FILTER_STRENGTH = 0.3f; - _headRotation = safeMix(_headRotation, newRotation, glm::clamp(glm::length(_headAngularVelocity) * - ANGULAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f)); - - const float TRANSLATION_SCALE = 0.02f; - glm::vec3 newHeadTranslation = glm::vec3(data.m_headTranslation.x, data.m_headTranslation.y, - -data.m_headTranslation.z) * TRANSLATION_SCALE; - - _headLinearVelocity = (newHeadTranslation - _lastHeadTranslation) / _averageFrameTime; - - const float LINEAR_VELOCITY_FILTER_STRENGTH = 0.3f; - float velocityFilter = glm::clamp(1.0f - glm::length(_headLinearVelocity) * - LINEAR_VELOCITY_FILTER_STRENGTH, 0.0f, 1.0f); - _filteredHeadTranslation = velocityFilter * _filteredHeadTranslation + (1.0f - velocityFilter) * newHeadTranslation; - - _lastHeadTranslation = newHeadTranslation; - _headTranslation = _filteredHeadTranslation; - - _eyeGazeLeftPitch = -data.m_eyeGazeLeftPitch; - _eyeGazeLeftYaw = data.m_eyeGazeLeftYaw; - _eyeGazeRightPitch = -data.m_eyeGazeRightPitch; - _eyeGazeRightYaw = data.m_eyeGazeRightYaw; - _blendshapeCoefficients = QVector::fromStdVector(data.m_coeffs); - - const float FRAME_AVERAGING_FACTOR = 0.99f; - quint64 usecsNow = usecTimestampNow(); - if (_lastMessageReceived != 0) { - _averageFrameTime = FRAME_AVERAGING_FACTOR * _averageFrameTime + - (1.0f - FRAME_AVERAGING_FACTOR) * (float)(usecsNow - _lastMessageReceived) / 1000000.0f; - } - _lastMessageReceived = usecsNow; - } - break; - } - case fsMsg::MSG_OUT_BLENDSHAPE_NAMES: { - const vector& names = static_pointer_cast(msg)->blendshape_names(); - for (int i = 0; i < (int)names.size(); i++) { - if (names[i] == "EyeBlink_L") { - _leftBlinkIndex = i; - - } else if (names[i] == "EyeBlink_R") { - _rightBlinkIndex = i; - - } else if (names[i] == "EyeOpen_L") { - _leftEyeOpenIndex = i; - - } else if (names[i] == "EyeOpen_R") { - _rightEyeOpenIndex = i; - - } else if (names[i] == "BrowsD_L") { - _browDownLeftIndex = i; - - } else if (names[i] == "BrowsD_R") { - _browDownRightIndex = i; - - } else if (names[i] == "BrowsU_C") { - _browUpCenterIndex = i; - - } else if (names[i] == "BrowsU_L") { - _browUpLeftIndex = i; - - } else if (names[i] == "BrowsU_R") { - _browUpRightIndex = i; - - } else if (names[i] == "JawOpen") { - _jawOpenIndex = i; - - } else if (names[i] == "MouthSmile_L") { - _mouthSmileLeftIndex = i; - - } else if (names[i] == "MouthSmile_R") { - _mouthSmileRightIndex = i; - } - } - break; - } - default: - break; - } - } -#endif - - FaceTracker::countFrame(); -} - -void Faceshift::setHostname(const QString& hostname) { - _hostname.set(hostname); -} - diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h deleted file mode 100644 index 2c5889857c..0000000000 --- a/interface/src/devices/Faceshift.h +++ /dev/null @@ -1,155 +0,0 @@ -// -// Faceshift.h -// interface/src/devices -// -// Created by Andrzej Kapolka on 9/3/13. -// Copyright 2013 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_Faceshift_h -#define hifi_Faceshift_h - -#include -#include - -#ifdef HAVE_FACESHIFT -#include -#endif - -#include -#include - -#include "FaceTracker.h" - -const float STARTING_FACESHIFT_FRAME_TIME = 0.033f; - -/// Handles interaction with the Faceshift software, which provides head position/orientation and facial features. -class Faceshift : public FaceTracker, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: -#ifdef HAVE_FACESHIFT - // If we don't have faceshift, use the base class' methods - virtual void init() override; - virtual void update(float deltaTime) override; - virtual void reset() override; - - virtual bool isActive() const override; - virtual bool isTracking() const override; -#endif - - bool isConnectedOrConnecting() const; - - const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } - - // these pitch/yaw angles are in degrees - float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } - float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; } - - float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; } - float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; } - - float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } - float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } - float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } - float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); } - - float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); } - float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); } - float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); } - float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); } - float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); } - - float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); } - float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); } - float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); } - - QString getHostname() { return _hostname.get(); } - void setHostname(const QString& hostname); - - void updateFakeCoefficients(float leftBlink, - float rightBlink, - float browUp, - float jawOpen, - float mouth2, - float mouth3, - float mouth4, - QVector& coefficients) const; - -signals: - void connectionStateChanged(); - -public slots: - void setEnabled(bool enabled) override; - -private slots: - void connectSocket(); - void noteConnected(); - void noteError(QAbstractSocket::SocketError error); - void readPendingDatagrams(); - void readFromSocket(); - void noteDisconnected(); - -private: - Faceshift(); - virtual ~Faceshift() {} - - void send(const std::string& message); - void receive(const QByteArray& buffer); - - QTcpSocket _tcpSocket; - QUdpSocket _udpSocket; - -#ifdef HAVE_FACESHIFT - fs::fsBinaryStream _stream; -#endif - - bool _tcpEnabled = true; - int _tcpRetryCount = 0; - bool _tracking = false; - quint64 _lastReceiveTimestamp = 0; - quint64 _lastMessageReceived = 0; - float _averageFrameTime = STARTING_FACESHIFT_FRAME_TIME; - - glm::vec3 _headAngularVelocity = glm::vec3(0.0f); - glm::vec3 _headLinearVelocity = glm::vec3(0.0f); - glm::vec3 _lastHeadTranslation = glm::vec3(0.0f); - glm::vec3 _filteredHeadTranslation = glm::vec3(0.0f); - - // degrees - float _eyeGazeLeftPitch = 0.0f; - float _eyeGazeLeftYaw = 0.0f; - float _eyeGazeRightPitch = 0.0f; - float _eyeGazeRightYaw = 0.0f; - - // degrees - float _longTermAverageEyePitch = 0.0f; - float _longTermAverageEyeYaw = 0.0f; - bool _longTermAverageInitialized = false; - - Setting::Handle _hostname; - - // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes - int _leftBlinkIndex = 0; - int _rightBlinkIndex = 1; - int _leftEyeOpenIndex = 8; - int _rightEyeOpenIndex = 9; - - // Brows - int _browDownLeftIndex = 14; - int _browDownRightIndex = 15; - int _browUpCenterIndex = 16; - int _browUpLeftIndex = 17; - int _browUpRightIndex = 18; - - int _mouthSmileLeftIndex = 28; - int _mouthSmileRightIndex = 29; - - int _jawOpenIndex = 21; -}; - -#endif // hifi_Faceshift_h diff --git a/interface/src/devices/Logging.cpp b/interface/src/devices/Logging.cpp new file mode 100644 index 0000000000..a4dcf1b711 --- /dev/null +++ b/interface/src/devices/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis on 2017/04/25 +// 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 "Logging.h" + +Q_LOGGING_CATEGORY(trackers, "hifi.trackers") diff --git a/interface/src/devices/Logging.h b/interface/src/devices/Logging.h new file mode 100644 index 0000000000..554429b61d --- /dev/null +++ b/interface/src/devices/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis on 2017/04/25 +// 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_TrackersLogging_h +#define hifi_TrackersLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(trackers) + +#endif // hifi_TrackersLogging_h diff --git a/interface/src/devices/MotionTracker.cpp b/interface/src/devices/MotionTracker.cpp index 234a8d0c0c..c6012c0422 100644 --- a/interface/src/devices/MotionTracker.cpp +++ b/interface/src/devices/MotionTracker.cpp @@ -1,7 +1,4 @@ // -// MotionTracker.cpp -// interface/src/devices -// // Created by Sam Cake on 6/20/14. // Copyright 2014 High Fidelity, Inc. // @@ -10,8 +7,6 @@ // #include "MotionTracker.h" -#include "GLMHelpers.h" - // glm::mult(mat43, mat43) just the composition of the 2 matrices assuming they are in fact mat44 with the last raw = { 0, 0, 0, 1 } namespace glm { diff --git a/interface/src/devices/MotionTracker.h b/interface/src/devices/MotionTracker.h index a4b5e6735e..26c8dcea2c 100644 --- a/interface/src/devices/MotionTracker.h +++ b/interface/src/devices/MotionTracker.h @@ -1,7 +1,4 @@ // -// MotionTracker.h -// interface/src/devices -// // Created by Sam Cake on 6/20/14. // Copyright 2014 High Fidelity, Inc. // @@ -14,20 +11,7 @@ #include "DeviceTracker.h" -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-compare" -#endif - -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - - -#include -#include +#include /// Base class for device trackers. class MotionTracker : public DeviceTracker { diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index a12d9020ae..89fffbab22 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -207,13 +206,6 @@ void setupPreferences() { auto setter = [](float value) { FaceTracker::setEyeDeflection(value); }; preferences->addPreference(new SliderPreference(AVATAR_TUNING, "Face tracker eye deflection", getter, setter)); } - { - auto getter = []()->QString { return DependencyManager::get()->getHostname(); }; - auto setter = [](const QString& value) { DependencyManager::get()->setHostname(value); }; - auto preference = new EditPreference(AVATAR_TUNING, "Faceshift hostname", getter, setter); - preference->setPlaceholderText("localhost"); - preferences->addPreference(preference); - } { auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); }; auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); }; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1427ce6359..6c265ef1b6 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -393,9 +393,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (isFingerPointing) { setAtBit(flags, HAND_STATE_FINGER_POINTING_BIT); } - // faceshift state + // face tracker state if (_headData->_isFaceTrackerConnected) { - setAtBit(flags, IS_FACESHIFT_CONNECTED); + setAtBit(flags, IS_FACE_TRACKER_CONNECTED); } // eye tracker state if (_headData->_isEyeTrackerConnected) { @@ -883,7 +883,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newHandState = getSemiNibbleAt(bitItems, HAND_STATE_START_BIT) + (oneAtBit(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); - auto newFaceTrackerConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); + auto newFaceTrackerConnected = oneAtBit(bitItems, IS_FACE_TRACKER_CONNECTED); auto newEyeTrackerConnected = oneAtBit(bitItems, IS_EYE_TRACKER_CONNECTED); bool keyStateChanged = (_keyState != newKeyState); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 545a5f1f8c..b2cc912007 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -99,7 +99,7 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = // Referential Data - R is found in the 7th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits -const int IS_FACESHIFT_CONNECTED = 4; // 5th bit +const int IS_FACE_TRACKER_CONNECTED = 4; // 5th bit const int IS_EYE_TRACKER_CONNECTED = 5; // 6th bit (was CHAT_CIRCLING) const int HAS_REFERENTIAL = 6; // 7th bit const int HAND_STATE_FINGER_POINTING_BIT = 7; // 8th bit @@ -218,7 +218,7 @@ namespace AvatarDataPacket { } PACKED_END; const size_t AVATAR_LOCAL_POSITION_SIZE = 12; - // only present if IS_FACESHIFT_CONNECTED flag is set in AvatarInfo.flags + // only present if IS_FACE_TRACKER_CONNECTED flag is set in AvatarInfo.flags PACKED_BEGIN struct FaceTrackerInfo { float leftEyeBlink; float rightEyeBlink; diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp index 50833e90fc..7511448c38 100644 --- a/libraries/ui/src/ui/Menu.cpp +++ b/libraries/ui/src/ui/Menu.cpp @@ -223,6 +223,18 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMe return action; } +QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const std::function& handler, + const QKeySequence& shortcut, + const bool checked, + int menuItemLocation, + const QString& grouping) { + auto action = addCheckableActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, checked, nullptr, nullptr, menuItemLocation, grouping); + connect(action, &QAction::triggered, handler); + return action; +} + void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { auto action = _actionHash.value(actionName); menu->removeAction(action); diff --git a/libraries/ui/src/ui/Menu.h b/libraries/ui/src/ui/Menu.h index 9839bd1eb6..25f8f74063 100644 --- a/libraries/ui/src/ui/Menu.h +++ b/libraries/ui/src/ui/Menu.h @@ -9,6 +9,8 @@ #ifndef hifi_ui_Menu_h #define hifi_ui_Menu_h +#include + #include #include #include @@ -90,6 +92,14 @@ public: int menuItemLocation = UNSPECIFIED_POSITION, const QString& grouping = QString()); + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const std::function& handler, + const QKeySequence& shortcut = 0, + const bool checked = false, + int menuItemLocation = UNSPECIFIED_POSITION, + const QString& grouping = QString()); + void removeAction(MenuWrapper* menu, const QString& actionName); public slots: From 168cfa9b5d68a3f085e4c57a96c643c866d82672 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 28 Apr 2017 16:39:58 -0700 Subject: [PATCH 086/292] upping polling time plus min grip --- scripts/system/makeUserConnection.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 8d67e67700..7a3215ca89 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -13,7 +13,7 @@ var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m - var GRIP_MIN = 0.25; // goes from 0-1, so 25% pressed is pressed + var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed var MESSAGE_CHANNEL = "io.highfidelity.makeUserConnection"; var STATES = { INACTIVE: 0, @@ -592,7 +592,11 @@ Window.makeConnection(false, result.connection); UserActivityLogger.makeUserConnection(connectingId, false, result.connection); } - var POLL_INTERVAL_MS = 200, POLL_LIMIT = 5; + // This is a bit fragile - but to account for skew in when people actually create the + // connection request, I've upped this to 2 seconds (plus the round-trip times) + // TODO: keep track of when the person we are connecting with is done, and don't stop + // until say 1 second after that. + var POLL_INTERVAL_MS = 200, POLL_LIMIT = 10; function handleConnectionResponseAndMaybeRepeat(error, response) { // If response is 'pending', set a short timeout to try again. // If we fail other than pending, set result and immediately call connectionRequestCompleted. From bc89af28423cb55d11e4f9d7a6f60082e59dcbb8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Apr 2017 16:26:03 -0700 Subject: [PATCH 087/292] 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 088/292] 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 bfe8bc7593b0710864a041333aecc8757de1550c Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 28 Apr 2017 16:52:43 -0700 Subject: [PATCH 089/292] Adding the 45 codepath for debug --- .../src/gpu/gl41/GL41BackendTexture.cpp | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index fdc151e20b..06fc5ca7c6 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -325,6 +325,27 @@ void GL41VariableAllocationTexture::promote() { allocateStorage(targetAllocatedMip); if (_texelFormat.isCompressed()) { +#if defined(USE_45IMAGECOPY_PROMOTE) + withPreservedTexture([&] { + uint16_t numMips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + syncSampler(); + }); +#else GLuint pbo { 0 }; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); @@ -406,6 +427,7 @@ void GL41VariableAllocationTexture::promote() { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); +#endif } else { withPreservedTexture([&] { GLuint fbo { 0 }; @@ -452,6 +474,27 @@ void GL41VariableAllocationTexture::demote() { _populatedMip = std::max(_populatedMip, _allocatedMip); if (_texelFormat.isCompressed()) { +#if defined(USE_45IMAGECOPY_DEMOTE) + withPreservedTexture([&] { + uint16_t numMips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = targetMip + 1; + auto faces = getFaceCount(_target); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + oldId, _target, sourceMip, 0, 0, face, + _id, _target, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } + syncSampler(); + }); +#else GLuint pbo { 0 }; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); @@ -528,7 +571,7 @@ void GL41VariableAllocationTexture::demote() { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); - +#endif } else { withPreservedTexture([&] { GLuint fbo { 0 }; From f9debf13886f451eea64173f36b3c2dc17741971 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 28 Apr 2017 16:53:08 -0700 Subject: [PATCH 090/292] checkpoint --- interface/resources/qml/hifi/Feed.qml | 46 ++++++-- .../qml/hifi/tablet/TabletAddressDialog.qml | 73 +++++-------- scripts/system/pal.js | 16 ++- scripts/system/tablet-goto.js | 103 ++++++++++++------ 4 files changed, 150 insertions(+), 88 deletions(-) diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index b03144644a..ee0db29cc7 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -34,12 +34,12 @@ Column { property string metaverseServerUrl: ''; property string actions: 'snapshot'; - onActionsChanged: fillDestinations(); Component.onCompleted: fillDestinations(); property string labelText: actions; property string filter: ''; onFilterChanged: filterChoicesByText(); property var goFunction: null; + property var rpc: null; HifiConstants { id: hifi } ListModel { id: suggestions; } @@ -81,10 +81,24 @@ Column { property var allStories: []; property var placeMap: ({}); // Used for making stacks. property int requestId: 0; + function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey + if (!error && (data.status === 'success')) { + return; + } + if (!error) { // Create a message from the data + error = data.status + ': ' + data.error; + } + if (typeof(error) === 'string') { // Make a proper Error object + error = new Error(error); + } + error.message += ' in ' + url; // Include the url. + cb(error); + return true; + } function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model // If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness. var options = [ - 'now=' + new Date().toISOString(), + //'now=' + new Date().toISOString(), 'include_actions=' + actions, 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), 'require_online=true', @@ -93,8 +107,17 @@ Column { ]; var url = metaverseBase + 'user_stories?' + options.join('&'); var thisRequestId = ++requestId; - getRequest(url, function (error, data) { - if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) { + rpc('request', { + uri: url + }, function (error, data) { + console.log('fixme response', url, JSON.stringify(error), JSON.stringify(data)); + data.total_pages = 1; // fixme remove after testing + if (thisRequestId !== requestId) { + error = 'stale'; + } + //console.log('fixme', actions, pageNumber, thisRequestId, requestId, url, error) + //console.log('fixme data', actions, pageNumber, JSON.stringify(data)); + if (handleError(url, error, data, cb)) { return; // abandon stale requests } allStories = allStories.concat(data.user_stories.map(makeModelData)); @@ -108,21 +131,28 @@ Column { }); } function fillDestinations() { // Public + function report(label, error) { + console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count); + } var filter = makeFilteredStoryProcessor(), counter = 0; allStories = []; suggestions.clear(); placeMap = {}; getUserStoryPage(1, function (error) { allStories.slice(counter).forEach(filter); - console.log('user stories query', actions, error || 'ok', allStories.length, 'filtered to', suggestions.count); + report('user stories update', error); root.visible = !!suggestions.count; - }, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed. + }/*, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed. allStories.forEach(function (story) { counter++; filter(story); root.visible = !!suggestions.count; }); - }); + report('user stories'); + }*/); + } + function identity(x) { + return x; } function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches var words = filter.toUpperCase().split(/\s+/).filter(identity); @@ -130,7 +160,7 @@ Column { if (story.action === 'snapshot') { return true; } - return (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain. + return true; // fixme restore (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain. } function matches(story) { if (!words.length) { diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 9689583649..0893c51517 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -33,9 +33,33 @@ StackView { property int cardWidth: 212; property int cardHeight: 152; property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/"; - property var tablet: null; + // This version only implements rpc(method, parameters, callback(error, result)) calls initiated from here, not initiated from .js, nor "notifications". + property var rpcCalls: ({}); + property var rpcCounter: 0; + signal sendToScript(var message); + function rpc(method, parameters, callback) { + console.log('fixme rpc', method); + sendToScript('foo'); + console.log('fixme sent to script'); + /*rpcCalls[rpcCounter] = callback; + var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"}; + console.log('fixme sending rpc', JSON.stringify(message)); + sendToScript(message); + console.log('fixme sent rpc', message.id);*/ + } + function fromScript(message) { + console.log('fixme got message from script:', JSON.stringify(message)); + var callback = rpcCalls[message.id]; + if (!callback) { + console.log('No callback for message fromScript', JSON.stringify(message)); + return; + } + delete rpcCalls[message.id]; + callback(message.error, message.result); + } + Component { id: tabletWebView; TabletWebView {} } Component.onCompleted: { updateLocationText(false); @@ -266,6 +290,7 @@ StackView { actions: 'announcement'; filter: addressLine.text; goFunction: goCard; + rpc: root.rpc; } Feed { id: places; @@ -278,6 +303,7 @@ StackView { actions: 'concurrency'; filter: addressLine.text; goFunction: goCard; + rpc: root.rpc; } Feed { id: snapshots; @@ -291,6 +317,7 @@ StackView { actions: 'snapshot'; filter: addressLine.text; goFunction: goCard; + rpc: root.rpc; } } } @@ -330,50 +357,6 @@ StackView { } - function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. - // TODO: make available to other .qml. - var request = new XMLHttpRequest(); - // QT bug: apparently doesn't handle onload. Workaround using readyState. - request.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (request.readyState >= READY_STATE_DONE) { - var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, - response = !error && request.responseText, - contentType = !error && request.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - cb(error, response); - } - }; - request.open("GET", url, true); - request.send(); - } - - function identity(x) { - return x; - } - - function handleError(url, error, data, cb) { // cb(error) and answer truthy if needed, else falsey - if (!error && (data.status === 'success')) { - return; - } - if (!error) { // Create a message from the data - error = data.status + ': ' + data.error; - } - if (typeof(error) === 'string') { // Make a proper Error object - error = new Error(error); - } - error.message += ' in ' + url; // Include the url. - cb(error); - return true; - } - function updateLocationText(enteringAddress) { if (enteringAddress) { notice.text = "Go To a place, @user, path, or network address:"; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ae64065216..4a6b8d4142 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -723,7 +723,6 @@ function startup() { activeIcon: "icons/tablet-icons/people-a.svg", sortOrder: 7 }); - tablet.fromQml.connect(fromQml); button.clicked.connect(onTabletButtonClicked); tablet.screenChanged.connect(onTabletScreenChanged); Users.usernameFromIDReply.connect(usernameFromIDReply); @@ -789,8 +788,23 @@ function onTabletButtonClicked() { audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); } } +var hasEventBridge = false; +function wireEventBridge(on) { + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } + } +} function onTabletScreenChanged(type, url) { + wireEventBridge(shouldActivateButton); // for toolbar mode: change button to active when window is first openend, false otherwise. button.editProperties({isActive: shouldActivateButton}); shouldActivateButton = false; diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 8ba19d18a8..c32f5d9e50 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -30,40 +30,6 @@ text: buttonName, sortOrder: 8 }); - function messagesWaiting(isWaiting) { - button.editProperties({ - icon: isWaiting ? WAITING_ICON : NORMAL_ICON - // No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway. - }); - } - - function onClicked() { - if (onGotoScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - shouldActivateButton = true; - tablet.loadQMLSource(gotoQmlSource); - onGotoScreen = true; - } - } - - function onScreenChanged(type, url) { - ignore(type); - if (url === gotoQmlSource) { - onGotoScreen = true; - shouldActivateButton = true; - button.editProperties({isActive: shouldActivateButton}); - messagesWaiting(false); - } else { - shouldActivateButton = false; - onGotoScreen = false; - button.editProperties({isActive: shouldActivateButton}); - } - } - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); - 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. @@ -112,6 +78,74 @@ httpRequest.open(options.method, options.uri, true); httpRequest.send(options.body); } + function fromQmlXX(message) { + print('fixme got fromQml', JSON.stringify(message)); + /*var response = {id: message.id, jsonrpc: "2.0"}; + switch (message.method) { + case 'request': + request(message.params, function (error, data) { + response.error = error; + response.result = data; + tablet.sendToQml(response); + }); + return; + default: + response.error = {message: 'Unrecognized message', data: message}; + } + tablet.sendToQml(response);*/ + } + function messagesWaiting(isWaiting) { + button.editProperties({ + icon: isWaiting ? WAITING_ICON : NORMAL_ICON + // No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway. + }); + } + var hasEventBridge = false; + function wireEventBridge(on) { + print('fixme wireEventBridge', on, hasEventBridge); + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQmlXX); + print('fixme wired', tablet); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQmlXX); + hasEventBridge = false; + } + } + } + wireEventBridge(true); + + function onClicked() { + if (onGotoScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + shouldActivateButton = true; + tablet.loadQMLSource(gotoQmlSource); + onGotoScreen = true; + } + } + + function onScreenChanged(type, url) { + ignore(type); + if (url === gotoQmlSource) { + onGotoScreen = true; + shouldActivateButton = true; + button.editProperties({isActive: shouldActivateButton}); + wireEventBridge(true); + messagesWaiting(false); + } else { + shouldActivateButton = false; + onGotoScreen = false; + button.editProperties({isActive: shouldActivateButton}); + wireEventBridge(false); + } + } + button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); var stories = {}; var DEBUG = false; @@ -135,6 +169,7 @@ return; } var didNotify = false; + print('fixme poll', url, JSON.stringify(data.user_stories)); data.user_stories.forEach(function (story) { if (stories[story.id]) { // already seen return; From 04895864b5f9c3fc543ac71584fe3f393b2605a4 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 28 Apr 2017 17:34:15 -0700 Subject: [PATCH 091/292] Factorizing the code... --- .../src/gpu/gl41/GL41BackendTexture.cpp | 237 +++++++----------- 1 file changed, 85 insertions(+), 152 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 06fc5ca7c6..8c249fc652 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -308,6 +308,87 @@ void GL41VariableAllocationTexture::syncSampler() const { }); } + +void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + struct MipDesc { + GLint _faceSize; + GLint _size; + GLint _offset; + GLint _width; + GLint _height; + }; + std::vector sourceMips(numMips); + + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + glBindTexture(texTarget, srcId); + const auto& faceTargets = GLTexture::getFaceTargets(texTarget); + GLint internalFormat { 0 }; + + // Collect the mip description from the source texture + GLint bufferOffset { 0 }; + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcAllocatedMip; + + // Grab internal format once + if (internalFormat != 0) { + glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + } + + // Collect the size of the first face, and then compute the total size offset needed for this mip level + + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); + sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; + sourceMip._offset = bufferOffset; + bufferOffset += sourceMip._size; + } + (void)CHECK_GL_ERROR(); + + // Allocate the PBO to accomodate for all the mips to copy + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); + (void)CHECK_GL_ERROR(); + + // Transfer from source texture to pbo + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcAllocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + } + (void)CHECK_GL_ERROR(); + } + + // Now populate the new texture from the pbo + glBindTexture(texTarget, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); + + // Transfer from pbo to new texture + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t destLevel = mip - destAllocatedMip; + + for (GLint f = 0; f < faceTargets.size(); f++) { + glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._width, sourceMip._height, 0, + sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + } + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pbo); +} + void GL41VariableAllocationTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); @@ -346,87 +427,11 @@ void GL41VariableAllocationTexture::promote() { syncSampler(); }); #else - GLuint pbo { 0 }; - glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); uint16_t numMips = _gpuObject.getNumMips(); - struct MipDesc { - GLint _faceSize; - GLint _size; - GLint _offset; - gpu::Vec3u _dims; - }; - std::vector sourceMips(numMips); - - glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); - glBindTexture(_target, oldId); - const auto& faceTargets = getFaceTargets(_target); - GLint internalFormat { 0 }; - (void)CHECK_GL_ERROR(); - - // Collect the mip description from the source texture - GLint bufferOffset { 0 }; - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - sourceMip._dims = _gpuObject.evalMipDimensions(mip); - - uint16_t sourceLevel = mip - oldAllocatedMip; - - // Grab internal format once - if (internalFormat == 0) { - glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); - } - - // Collect the size of the first face, and then compute the total size offset needed for this mip level - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); - sourceMip._size = (GLint) faceTargets.size() * sourceMip._faceSize; - sourceMip._offset = bufferOffset; - bufferOffset += sourceMip._size; - } - (void)CHECK_GL_ERROR(); - - // Allocate the PBO to accomodate for all the mips to copy - glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); - (void)CHECK_GL_ERROR(); - - // Transfer from source texture to pbo - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t sourceLevel = mip - oldAllocatedMip; - - for (GLint f = 0; f < faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*)(sourceMip._offset + f * sourceMip._faceSize)); - } - (void)CHECK_GL_ERROR(); - - } - - // Now populate the new texture from the pbo - glBindTexture(_target, 0); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); - - glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); - glBindTexture(_target, _texture); - - // Transfer from pbo to new texture - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t destLevel = mip - _allocatedMip; - - for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._dims.x, sourceMip._dims.y, 0, - sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); - } - } + withPreservedTexture([&] { + copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); - - glBindTexture(_target, 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glDeleteBuffers(1, &pbo); + }); #endif } else { withPreservedTexture([&] { @@ -495,82 +500,11 @@ void GL41VariableAllocationTexture::demote() { syncSampler(); }); #else - GLuint pbo { 0 }; - glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); uint16_t numMips = _gpuObject.getNumMips(); - struct MipDesc { - GLint _faceSize; - GLint _size; - GLint _offset; - gpu::Vec3u _dims; - }; - std::vector sourceMips(numMips); - - glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); - glBindTexture(_target, oldId); - const auto& faceTargets = getFaceTargets(_target); - GLint internalFormat { 0 }; - - // Collect the mip description from the source texture - GLint bufferOffset { 0 }; - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - sourceMip._dims = _gpuObject.evalMipDimensions(mip); - - uint16_t sourceLevel = mip - oldAllocatedMip; - - // Grab internal format once - if (internalFormat != 0) { - glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); - } - - // Collect the size of the first face, and then compute the total size offset needed for this mip level - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); - sourceMip._size = (GLint) faceTargets.size() * sourceMip._faceSize; - sourceMip._offset = bufferOffset; - bufferOffset += sourceMip._size; - } - (void)CHECK_GL_ERROR(); - - // Allocate the PBO to accomodate for all the mips to copy - glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); - (void)CHECK_GL_ERROR(); - - // Transfer from source texture to pbo - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t sourceLevel = mip - oldAllocatedMip; - - for (GLint f = 0; f < faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (sourceMip._offset + f * sourceMip._faceSize)); - } - (void)CHECK_GL_ERROR(); - } - - // Now populate the new texture from the pbo - glBindTexture(_target, 0); - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); - withPreservedTexture([&] { - // Transfer from pbo to new texture - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto& sourceMip = sourceMips[mip]; - - uint16_t destLevel = mip - _allocatedMip; - - for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._dims.x, sourceMip._dims.y, 0, - sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); - } - } + copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); }); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glDeleteBuffers(1, &pbo); #endif } else { withPreservedTexture([&] { @@ -668,4 +602,3 @@ GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend GL41ResourceTexture::~GL41ResourceTexture() { } - From b6ae0a5bde0705ab6e99ee8fbe696d7be4e5fc54 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 28 Apr 2017 20:06:13 -0700 Subject: [PATCH 092/292] add nextTick, and cleanup --- interface/resources/qml/hifi/Feed.qml | 23 +++++++++---------- .../qml/hifi/tablet/TabletAddressDialog.qml | 10 ++------ scripts/system/tablet-goto.js | 15 ++++-------- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index ee0db29cc7..fd3472b7be 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -34,7 +34,8 @@ Column { property string metaverseServerUrl: ''; property string actions: 'snapshot'; - Component.onCompleted: fillDestinations(); + // sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick. + Component.onCompleted: delay.start(); property string labelText: actions; property string filter: ''; onFilterChanged: filterChoicesByText(); @@ -98,7 +99,7 @@ Column { function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model // If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness. var options = [ - //'now=' + new Date().toISOString(), + 'now=' + new Date().toISOString(), 'include_actions=' + actions, 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), 'require_online=true', @@ -107,16 +108,10 @@ Column { ]; var url = metaverseBase + 'user_stories?' + options.join('&'); var thisRequestId = ++requestId; - rpc('request', { - uri: url - }, function (error, data) { - console.log('fixme response', url, JSON.stringify(error), JSON.stringify(data)); - data.total_pages = 1; // fixme remove after testing + rpc('request', url, function (error, data) { if (thisRequestId !== requestId) { error = 'stale'; } - //console.log('fixme', actions, pageNumber, thisRequestId, requestId, url, error) - //console.log('fixme data', actions, pageNumber, JSON.stringify(data)); if (handleError(url, error, data, cb)) { return; // abandon stale requests } @@ -130,6 +125,10 @@ Column { cb(); }); } + property var delay: Timer { // No setTimeout or nextTick in QML. + interval: 0; + onTriggered: fillDestinations(); + } function fillDestinations() { // Public function report(label, error) { console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count); @@ -142,14 +141,14 @@ Column { allStories.slice(counter).forEach(filter); report('user stories update', error); root.visible = !!suggestions.count; - }/*, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed. + }, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed. allStories.forEach(function (story) { counter++; filter(story); root.visible = !!suggestions.count; }); report('user stories'); - }*/); + }); } function identity(x) { return x; @@ -160,7 +159,7 @@ Column { if (story.action === 'snapshot') { return true; } - return true; // fixme restore (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain. + return story.place_name !== AddressManager.placename; // Not our entry, but do show other entry points to current domain. } function matches(story) { if (!words.length) { diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 0893c51517..b7c0d24b24 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -40,17 +40,11 @@ StackView { property var rpcCounter: 0; signal sendToScript(var message); function rpc(method, parameters, callback) { - console.log('fixme rpc', method); - sendToScript('foo'); - console.log('fixme sent to script'); - /*rpcCalls[rpcCounter] = callback; + rpcCalls[rpcCounter] = callback; var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"}; - console.log('fixme sending rpc', JSON.stringify(message)); sendToScript(message); - console.log('fixme sent rpc', message.id);*/ } function fromScript(message) { - console.log('fixme got message from script:', JSON.stringify(message)); var callback = rpcCalls[message.id]; if (!callback) { console.log('No callback for message fromScript', JSON.stringify(message)); @@ -78,7 +72,7 @@ StackView { } - function resetAfterTeleport() { + function resetAfterTeleport() { //storyCardFrame.shown = root.shown = false; } function goCard(targetString) { diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index c32f5d9e50..fec7a6de90 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -78,9 +78,8 @@ httpRequest.open(options.method, options.uri, true); httpRequest.send(options.body); } - function fromQmlXX(message) { - print('fixme got fromQml', JSON.stringify(message)); - /*var response = {id: message.id, jsonrpc: "2.0"}; + function fromQml(message) { + var response = {id: message.id, jsonrpc: "2.0"}; switch (message.method) { case 'request': request(message.params, function (error, data) { @@ -92,7 +91,7 @@ default: response.error = {message: 'Unrecognized message', data: message}; } - tablet.sendToQml(response);*/ + tablet.sendToQml(response); } function messagesWaiting(isWaiting) { button.editProperties({ @@ -102,21 +101,18 @@ } var hasEventBridge = false; function wireEventBridge(on) { - print('fixme wireEventBridge', on, hasEventBridge); if (on) { if (!hasEventBridge) { - tablet.fromQml.connect(fromQmlXX); - print('fixme wired', tablet); + tablet.fromQml.connect(fromQml); hasEventBridge = true; } } else { if (hasEventBridge) { - tablet.fromQml.disconnect(fromQmlXX); + tablet.fromQml.disconnect(fromQml); hasEventBridge = false; } } } - wireEventBridge(true); function onClicked() { if (onGotoScreen) { @@ -169,7 +165,6 @@ return; } var didNotify = false; - print('fixme poll', url, JSON.stringify(data.user_stories)); data.user_stories.forEach(function (story) { if (stories[story.id]) { // already seen return; From 953a3f7a6f07140396cbeb08d8ab5936e35188f5 Mon Sep 17 00:00:00 2001 From: 1st-BrainStormer <1st-BrainStormer@users.noreply.github.com> Date: Fri, 28 Apr 2017 20:14:01 -0700 Subject: [PATCH 093/292] Update EntityDynamicInterface.cpp Grammar correction. --- libraries/entities/src/EntityDynamicInterface.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index bed3185b8f..2ab9a60397 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -49,20 +49,20 @@ -An dynamic is a callback which is registered with bullet. An dynamic is called-back every physics +A dynamic is a callback which is registered with bullet. A dynamic is called-back every physics simulation step and can do whatever it wants with the various datastructures it has available. An dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that point by a spring. -In this system, an dynamic is a property of an EntityItem (rather, an EntityItem has a property which +In this system, a dynamic is a property of an EntityItem (rather, an EntityItem has a property which encodes a list of dynamics). Each dynamic has a type and some arguments. Dynamics can be created by a script or when receiving information via an EntityTree data-stream (either over the network or from an svo file). In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic -subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects an dynamic-object +subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects a dynamic-object via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass -instance is created, it is registered as an dynamic with bullet. Bullet will call into code in this +instance is created, it is registered as a dynamic with bullet. Bullet will call into code in this instance with the btDynamicInterface every physics-simulation step. Because the dynamic can exist next to the interface's EntityTree or the entity-server's EntityTree, From 2b43a1989f629662bb09f83b1644b7b88c543eab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Apr 2017 16:40:48 -0700 Subject: [PATCH 094/292] Moving face and eye trackers out of interface --- interface/CMakeLists.txt | 2 +- .../resources/icons/tablet-icons/goto-msg.svg | 36 +++++++++---------- interface/src/Application.cpp | 2 +- interface/src/avatar/Head.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/devices/DdeFaceTracker.h | 2 +- interface/src/devices/Leapmotion.h | 2 +- .../ControllerScriptingInterface.cpp | 2 +- interface/src/ui/AvatarInputs.cpp | 2 +- libraries/trackers/CMakeLists.txt | 6 ++++ .../trackers/src/trackers}/DeviceTracker.cpp | 0 .../trackers/src/trackers}/DeviceTracker.h | 0 .../trackers/src/trackers}/EyeTracker.cpp | 0 .../trackers/src/trackers}/EyeTracker.h | 0 .../trackers/src/trackers}/FaceTracker.cpp | 0 .../trackers/src/trackers}/FaceTracker.h | 0 .../trackers/src/trackers}/Logging.cpp | 0 .../trackers/src/trackers}/Logging.h | 0 .../trackers/src/trackers}/MotionTracker.cpp | 0 .../trackers/src/trackers}/MotionTracker.h | 0 20 files changed, 32 insertions(+), 26 deletions(-) create mode 100644 libraries/trackers/CMakeLists.txt rename {interface/src/devices => libraries/trackers/src/trackers}/DeviceTracker.cpp (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/DeviceTracker.h (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/EyeTracker.cpp (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/EyeTracker.h (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/FaceTracker.cpp (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/FaceTracker.h (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/Logging.cpp (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/Logging.h (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/MotionTracker.cpp (100%) rename {interface/src/devices => libraries/trackers/src/trackers}/MotionTracker.h (100%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index e3d01826b3..d7e4b1ae7c 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -194,7 +194,7 @@ link_hifi_libraries( recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui auto-updater - controllers plugins image + controllers plugins image trackers ui-plugins display-plugins input-plugins ${NON_ANDROID_LIBRARIES} ) diff --git a/interface/resources/icons/tablet-icons/goto-msg.svg b/interface/resources/icons/tablet-icons/goto-msg.svg index ef905b6066..9b576ab1bf 100644 --- a/interface/resources/icons/tablet-icons/goto-msg.svg +++ b/interface/resources/icons/tablet-icons/goto-msg.svg @@ -1,18 +1,18 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 306fa99852..1d90c523b5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -128,6 +128,7 @@ #include #include #include +#include #include "AudioClient.h" @@ -136,7 +137,6 @@ #include "avatar/ScriptAvatar.h" #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" -#include "devices/EyeTracker.h" #include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index f2eeba9d60..16e5776d87 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Application.h" #include "Avatar.h" @@ -22,7 +23,6 @@ #include "Menu.h" #include "Util.h" #include "devices/DdeFaceTracker.h" -#include "devices/EyeTracker.h" #include using namespace std; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b8c7656839..f2726930b5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include "Application.h" #include "AvatarManager.h" diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 973c3b224e..f125dfc3cf 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -22,7 +22,7 @@ #include #include -#include "FaceTracker.h" +#include class DdeFaceTracker : public FaceTracker, public Dependency { Q_OBJECT diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h index d7981a65e8..6ecec8ccf9 100644 --- a/interface/src/devices/Leapmotion.h +++ b/interface/src/devices/Leapmotion.h @@ -14,7 +14,7 @@ #include -#include "MotionTracker.h" +#include #ifdef HAVE_LEAPMOTION #include diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 0d0c2ef668..f3ec3cd79d 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -17,7 +17,7 @@ #include #include "Application.h" -#include "devices/MotionTracker.h" +#include void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 341915e57f..2b715eac9d 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -11,9 +11,9 @@ #include #include +#include #include "Application.h" -#include "devices/FaceTracker.h" #include "Menu.h" HIFI_QML_DEF(AvatarInputs) diff --git a/libraries/trackers/CMakeLists.txt b/libraries/trackers/CMakeLists.txt new file mode 100644 index 0000000000..0999a45b59 --- /dev/null +++ b/libraries/trackers/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME trackers) +setup_hifi_library() +GroupSources("src") +link_hifi_libraries(shared) + +target_bullet() diff --git a/interface/src/devices/DeviceTracker.cpp b/libraries/trackers/src/trackers/DeviceTracker.cpp similarity index 100% rename from interface/src/devices/DeviceTracker.cpp rename to libraries/trackers/src/trackers/DeviceTracker.cpp diff --git a/interface/src/devices/DeviceTracker.h b/libraries/trackers/src/trackers/DeviceTracker.h similarity index 100% rename from interface/src/devices/DeviceTracker.h rename to libraries/trackers/src/trackers/DeviceTracker.h diff --git a/interface/src/devices/EyeTracker.cpp b/libraries/trackers/src/trackers/EyeTracker.cpp similarity index 100% rename from interface/src/devices/EyeTracker.cpp rename to libraries/trackers/src/trackers/EyeTracker.cpp diff --git a/interface/src/devices/EyeTracker.h b/libraries/trackers/src/trackers/EyeTracker.h similarity index 100% rename from interface/src/devices/EyeTracker.h rename to libraries/trackers/src/trackers/EyeTracker.h diff --git a/interface/src/devices/FaceTracker.cpp b/libraries/trackers/src/trackers/FaceTracker.cpp similarity index 100% rename from interface/src/devices/FaceTracker.cpp rename to libraries/trackers/src/trackers/FaceTracker.cpp diff --git a/interface/src/devices/FaceTracker.h b/libraries/trackers/src/trackers/FaceTracker.h similarity index 100% rename from interface/src/devices/FaceTracker.h rename to libraries/trackers/src/trackers/FaceTracker.h diff --git a/interface/src/devices/Logging.cpp b/libraries/trackers/src/trackers/Logging.cpp similarity index 100% rename from interface/src/devices/Logging.cpp rename to libraries/trackers/src/trackers/Logging.cpp diff --git a/interface/src/devices/Logging.h b/libraries/trackers/src/trackers/Logging.h similarity index 100% rename from interface/src/devices/Logging.h rename to libraries/trackers/src/trackers/Logging.h diff --git a/interface/src/devices/MotionTracker.cpp b/libraries/trackers/src/trackers/MotionTracker.cpp similarity index 100% rename from interface/src/devices/MotionTracker.cpp rename to libraries/trackers/src/trackers/MotionTracker.cpp diff --git a/interface/src/devices/MotionTracker.h b/libraries/trackers/src/trackers/MotionTracker.h similarity index 100% rename from interface/src/devices/MotionTracker.h rename to libraries/trackers/src/trackers/MotionTracker.h From 6623028d71df16b5f45e27d48bcca3039c11368e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 29 Apr 2017 08:35:55 -0700 Subject: [PATCH 095/292] ball-and-socket constraint --- interface/src/InterfaceDynamicFactory.cpp | 3 + .../entities/src/EntityDynamicInterface.cpp | 5 + .../entities/src/EntityDynamicInterface.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + .../src/ObjectConstraintBallSocket.cpp | 240 ++++++++++++++++++ .../physics/src/ObjectConstraintBallSocket.h | 46 ++++ .../physics/src/ObjectConstraintSlider.cpp | 1 - 8 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 libraries/physics/src/ObjectConstraintBallSocket.cpp create mode 100644 libraries/physics/src/ObjectConstraintBallSocket.h diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index 71f6b572e0..5fe938e861 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "InterfaceDynamicFactory.h" @@ -41,6 +42,8 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid return std::make_shared(id, ownerEntity); case DYNAMIC_TYPE_SLIDER: return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_BALL_SOCKET: + return std::make_shared(id, ownerEntity); } qDebug() << "Unknown entity dynamic type"; diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index da5ab7d8c4..89bba4379f 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -120,6 +120,9 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT if (normalizedDynamicTypeString == "slider") { return DYNAMIC_TYPE_SLIDER; } + if (normalizedDynamicTypeString == "ballsocket") { + return DYNAMIC_TYPE_BALL_SOCKET; + } qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name" << dynamicTypeString; @@ -144,6 +147,8 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp return "far-grab"; case DYNAMIC_TYPE_SLIDER: return "slider"; + case DYNAMIC_TYPE_BALL_SOCKET: + return "ball-socket"; } assert(false); return "none"; diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index cfb82dbdf8..28b1d89b99 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -32,7 +32,8 @@ enum EntityDynamicType { DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000, DYNAMIC_TYPE_HINGE = 5000, DYNAMIC_TYPE_FAR_GRAB = 6000, - DYNAMIC_TYPE_SLIDER = 7000 + DYNAMIC_TYPE_SLIDER = 7000, + DYNAMIC_TYPE_BALL_SOCKET = 8000 }; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 082f7ade1e..ce51ccd019 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_SLIDER_CONSTRAINT; + return VERSION_ENTITIES_MORE_CONSTRAINTS; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 80646251a9..f84b16818a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -209,6 +209,7 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; const PacketVersion VERSION_ENTITIES_SLIDER_CONSTRAINT = 70; +const PacketVersion VERSION_ENTITIES_MORE_CONSTRAINTS = 71; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/libraries/physics/src/ObjectConstraintBallSocket.cpp b/libraries/physics/src/ObjectConstraintBallSocket.cpp new file mode 100644 index 0000000000..35f138e840 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintBallSocket.cpp @@ -0,0 +1,240 @@ +// +// ObjectConstraintBallSocket.cpp +// libraries/physics/src +// +// Created by Seth Alves 2017-4-29 +// Copyright 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 "QVariantGLM.h" + +#include "EntityTree.h" +#include "ObjectConstraintBallSocket.h" +#include "PhysicsLogging.h" + + +const uint16_t ObjectConstraintBallSocket::constraintVersion = 1; + + +ObjectConstraintBallSocket::ObjectConstraintBallSocket(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectConstraint(DYNAMIC_TYPE_BALL_SOCKET, id, ownerEntity), + _pivotInA(glm::vec3(0.0f)), + _pivotInB(glm::vec3(0.0f)) +{ + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintBallSocket::ObjectConstraintBallSocket"; + #endif +} + +ObjectConstraintBallSocket::~ObjectConstraintBallSocket() { + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintBallSocket::~ObjectConstraintBallSocket"; + #endif +} + +QList ObjectConstraintBallSocket::getRigidBodies() { + QList result; + result += getRigidBody(); + QUuid otherEntityID; + withReadLock([&]{ + otherEntityID = _otherEntityID; + }); + if (!otherEntityID.isNull()) { + result += getOtherRigidBody(otherEntityID); + } + return result; +} + +void ObjectConstraintBallSocket::prepareForPhysicsSimulation() { +} + +void ObjectConstraintBallSocket::updateBallSocket() { + btPoint2PointConstraint* constraint { nullptr }; + + withReadLock([&]{ + constraint = static_cast(_constraint); + }); + + if (!constraint) { + return; + } + + constraint->setPivotA(glmToBullet(_pivotInA)); + constraint->setPivotB(glmToBullet(_pivotInB)); +} + + +btTypedConstraint* ObjectConstraintBallSocket::getConstraint() { + btPoint2PointConstraint* constraint { nullptr }; + QUuid otherEntityID; + glm::vec3 pivotInA; + glm::vec3 pivotInB; + + withReadLock([&]{ + constraint = static_cast(_constraint); + pivotInA = _pivotInA; + otherEntityID = _otherEntityID; + pivotInB = _pivotInB; + }); + if (constraint) { + return constraint; + } + + btRigidBody* rigidBodyA = getRigidBody(); + if (!rigidBodyA) { + qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA"; + return nullptr; + } + + if (!otherEntityID.isNull()) { + // This constraint is between two entities... find the other rigid body. + + btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); + if (!rigidBodyB) { + return nullptr; + } + + constraint = new btPoint2PointConstraint(*rigidBodyA, *rigidBodyB, glmToBullet(pivotInA), glmToBullet(pivotInB)); + } else { + // This constraint is between an entity and the world-frame. + + constraint = new btPoint2PointConstraint(*rigidBodyA, glmToBullet(pivotInA)); + } + + withWriteLock([&]{ + _constraint = constraint; + }); + + // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network + forceBodyNonStatic(); + activateBody(); + + updateBallSocket(); + + return constraint; +} + + +bool ObjectConstraintBallSocket::updateArguments(QVariantMap arguments) { + glm::vec3 pivotInA; + QUuid otherEntityID; + glm::vec3 pivotInB; + + bool needUpdate = false; + bool somethingChanged = ObjectDynamic::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + pivotInA = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "pivot", ok, false); + if (!ok) { + pivotInA = _pivotInA; + } + + ok = true; + otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("ball-socket constraint", + arguments, "otherEntityID", ok, false)); + if (!ok) { + otherEntityID = _otherEntityID; + } + + ok = true; + pivotInB = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "otherPivot", ok, false); + if (!ok) { + pivotInB = _pivotInB; + } + + if (somethingChanged || + pivotInA != _pivotInA || + otherEntityID != _otherEntityID || + pivotInB != _pivotInB) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { + withWriteLock([&] { + _pivotInA = pivotInA; + _otherEntityID = otherEntityID; + _pivotInB = pivotInB; + + _active = true; + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); + } + }); + + updateBallSocket(); + } + + return true; +} + +QVariantMap ObjectConstraintBallSocket::getArguments() { + QVariantMap arguments = ObjectDynamic::getArguments(); + withReadLock([&] { + if (_constraint) { + arguments["pivot"] = glmToQMap(_pivotInA); + arguments["otherEntityID"] = _otherEntityID; + arguments["otherPivot"] = glmToQMap(_pivotInB); + } + }); + return arguments; +} + +QByteArray ObjectConstraintBallSocket::serialize() const { + QByteArray serializedConstraintArguments; + QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_BALL_SOCKET; + dataStream << getID(); + dataStream << ObjectConstraintBallSocket::constraintVersion; + + withReadLock([&] { + dataStream << localTimeToServerTime(_expires); + dataStream << _tag; + + dataStream << _pivotInA; + dataStream << _otherEntityID; + dataStream << _pivotInB; + }); + + return serializedConstraintArguments; +} + +void ObjectConstraintBallSocket::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectConstraintBallSocket::constraintVersion) { + assert(false); + return; + } + + withWriteLock([&] { + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; + + dataStream >> _pivotInA; + dataStream >> _otherEntityID; + dataStream >> _pivotInB; + + _active = true; + }); +} diff --git a/libraries/physics/src/ObjectConstraintBallSocket.h b/libraries/physics/src/ObjectConstraintBallSocket.h new file mode 100644 index 0000000000..9e0b942a6f --- /dev/null +++ b/libraries/physics/src/ObjectConstraintBallSocket.h @@ -0,0 +1,46 @@ +// +// ObjectConstraintBallSocket.h +// libraries/physics/src +// +// Created by Seth Alves 2017-4-29 +// Copyright 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_ObjectConstraintBallSocket_h +#define hifi_ObjectConstraintBallSocket_h + +#include "ObjectConstraint.h" + +// http://bulletphysics.org/Bullet/BulletFull/classbtBallSocketConstraint.html + +class ObjectConstraintBallSocket : public ObjectConstraint { +public: + ObjectConstraintBallSocket(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraintBallSocket(); + + virtual void prepareForPhysicsSimulation() override; + + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; + + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; + + virtual QList getRigidBodies() override; + virtual btTypedConstraint* getConstraint() override; + +protected: + static const uint16_t constraintVersion; + + void updateBallSocket(); + + glm::vec3 _pivotInA; + + EntityItemID _otherEntityID; + glm::vec3 _pivotInB; +}; + +#endif // hifi_ObjectConstraintBallSocket_h diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index fb8d8b9f68..8a722d1744 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -55,7 +55,6 @@ void ObjectConstraintSlider::updateSlider() { btSliderConstraint* constraint { nullptr }; withReadLock([&]{ - // TODO -- write this constraint = static_cast(_constraint); }); From 4c4e0dffcc652308ecd59e7ddadf8aaed40294e6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 29 Apr 2017 12:30:12 -0700 Subject: [PATCH 096/292] bullet cone-twist constraint --- interface/src/InterfaceDynamicFactory.cpp | 3 + .../entities/src/EntityDynamicInterface.cpp | 5 + .../entities/src/EntityDynamicInterface.h | 3 +- .../physics/src/ObjectConstraintConeTwist.cpp | 371 ++++++++++++++++++ .../physics/src/ObjectConstraintConeTwist.h | 55 +++ 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 libraries/physics/src/ObjectConstraintConeTwist.cpp create mode 100644 libraries/physics/src/ObjectConstraintConeTwist.h diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index 5fe938e861..5acc0700c0 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "InterfaceDynamicFactory.h" @@ -44,6 +45,8 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid return std::make_shared(id, ownerEntity); case DYNAMIC_TYPE_BALL_SOCKET: return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_CONE_TWIST: + return std::make_shared(id, ownerEntity); } qDebug() << "Unknown entity dynamic type"; diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index 89bba4379f..57f86105b2 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -123,6 +123,9 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT if (normalizedDynamicTypeString == "ballsocket") { return DYNAMIC_TYPE_BALL_SOCKET; } + if (normalizedDynamicTypeString == "conetwist") { + return DYNAMIC_TYPE_CONE_TWIST; + } qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name" << dynamicTypeString; @@ -149,6 +152,8 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp return "slider"; case DYNAMIC_TYPE_BALL_SOCKET: return "ball-socket"; + case DYNAMIC_TYPE_CONE_TWIST: + return "cone-twist"; } assert(false); return "none"; diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index 28b1d89b99..c04aaf67b2 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -33,7 +33,8 @@ enum EntityDynamicType { DYNAMIC_TYPE_HINGE = 5000, DYNAMIC_TYPE_FAR_GRAB = 6000, DYNAMIC_TYPE_SLIDER = 7000, - DYNAMIC_TYPE_BALL_SOCKET = 8000 + DYNAMIC_TYPE_BALL_SOCKET = 8000, + DYNAMIC_TYPE_CONE_TWIST = 9000 }; diff --git a/libraries/physics/src/ObjectConstraintConeTwist.cpp b/libraries/physics/src/ObjectConstraintConeTwist.cpp new file mode 100644 index 0000000000..7a7120cd09 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintConeTwist.cpp @@ -0,0 +1,371 @@ +// +// ObjectConstraintConeTwist.cpp +// libraries/physics/src +// +// Created by Seth Alves 2017-4-29 +// Copyright 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 "QVariantGLM.h" + +#include "EntityTree.h" +#include "ObjectConstraintConeTwist.h" +#include "PhysicsLogging.h" + + +const uint16_t ObjectConstraintConeTwist::constraintVersion = 1; + + +ObjectConstraintConeTwist::ObjectConstraintConeTwist(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectConstraint(DYNAMIC_TYPE_CONE_TWIST, id, ownerEntity), + _pivotInA(glm::vec3(0.0f)), + _axisInA(glm::vec3(0.0f)) +{ + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintConeTwist::ObjectConstraintConeTwist"; + #endif +} + +ObjectConstraintConeTwist::~ObjectConstraintConeTwist() { + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintConeTwist::~ObjectConstraintConeTwist"; + #endif +} + +QList ObjectConstraintConeTwist::getRigidBodies() { + QList result; + result += getRigidBody(); + QUuid otherEntityID; + withReadLock([&]{ + otherEntityID = _otherEntityID; + }); + if (!otherEntityID.isNull()) { + result += getOtherRigidBody(otherEntityID); + } + return result; +} + +void ObjectConstraintConeTwist::prepareForPhysicsSimulation() { +} + +void ObjectConstraintConeTwist::updateConeTwist() { + btConeTwistConstraint* constraint { nullptr }; + float swingSpan1; + float swingSpan2; + float twistSpan; + float softness; + float biasFactor; + float relaxationFactor; + + withReadLock([&]{ + constraint = static_cast(_constraint); + swingSpan1 = _swingSpan1; + swingSpan2 = _swingSpan2; + twistSpan = _twistSpan; + softness = _softness; + biasFactor = _biasFactor; + relaxationFactor = _relaxationFactor; + }); + + if (!constraint) { + return; + } + + constraint->setLimit(swingSpan1, + swingSpan2, + twistSpan, + softness, + biasFactor, + relaxationFactor); +} + + +btTypedConstraint* ObjectConstraintConeTwist::getConstraint() { + btConeTwistConstraint* constraint { nullptr }; + QUuid otherEntityID; + glm::vec3 pivotInA; + glm::vec3 axisInA; + glm::vec3 pivotInB; + glm::vec3 axisInB; + + withReadLock([&]{ + constraint = static_cast(_constraint); + pivotInA = _pivotInA; + axisInA = _axisInA; + otherEntityID = _otherEntityID; + pivotInB = _pivotInB; + axisInB = _axisInB; + }); + if (constraint) { + return constraint; + } + + btRigidBody* rigidBodyA = getRigidBody(); + if (!rigidBodyA) { + qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyA"; + return nullptr; + } + + if (!otherEntityID.isNull()) { + // This coneTwist is between two entities... find the other rigid body. + + glm::quat rotA = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + glm::quat rotB = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInB)); + + btTransform frameInA(glmToBullet(rotA), glmToBullet(pivotInA)); + btTransform frameInB(glmToBullet(rotB), glmToBullet(pivotInB)); + + btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); + if (!rigidBodyB) { + return nullptr; + } + + constraint = new btConeTwistConstraint(*rigidBodyA, *rigidBodyB, frameInA, frameInB); + } else { + // This coneTwist is between an entity and the world-frame. + + glm::quat rot = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + + btTransform frameInA(glmToBullet(rot), glmToBullet(pivotInA)); + + constraint = new btConeTwistConstraint(*rigidBodyA, frameInA); + } + + withWriteLock([&]{ + _constraint = constraint; + }); + + // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network + forceBodyNonStatic(); + activateBody(); + + updateConeTwist(); + + return constraint; +} + + +bool ObjectConstraintConeTwist::updateArguments(QVariantMap arguments) { + glm::vec3 pivotInA; + glm::vec3 axisInA; + QUuid otherEntityID; + glm::vec3 pivotInB; + glm::vec3 axisInB; + float swingSpan1; + float swingSpan2; + float twistSpan; + float softness; + float biasFactor; + float relaxationFactor; + + bool needUpdate = false; + bool somethingChanged = ObjectDynamic::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + pivotInA = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "pivot", ok, false); + if (!ok) { + pivotInA = _pivotInA; + } + + ok = true; + axisInA = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "axis", ok, false); + if (!ok) { + axisInA = _axisInA; + } + + ok = true; + otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("coneTwist constraint", + arguments, "otherEntityID", ok, false)); + if (!ok) { + otherEntityID = _otherEntityID; + } + + ok = true; + pivotInB = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "otherPivot", ok, false); + if (!ok) { + pivotInB = _pivotInB; + } + + ok = true; + axisInB = EntityDynamicInterface::extractVec3Argument("coneTwist constraint", arguments, "otherAxis", ok, false); + if (!ok) { + axisInB = _axisInB; + } + + ok = true; + swingSpan1 = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "swingSpan1", ok, false); + if (!ok) { + swingSpan1 = _swingSpan1; + } + + ok = true; + swingSpan2 = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "swingSpan2", ok, false); + if (!ok) { + swingSpan2 = _swingSpan2; + } + + ok = true; + twistSpan = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "twistSpan", ok, false); + if (!ok) { + twistSpan = _twistSpan; + } + + ok = true; + softness = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "softness", ok, false); + if (!ok) { + softness = _softness; + } + + ok = true; + biasFactor = EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "biasFactor", ok, false); + if (!ok) { + biasFactor = _biasFactor; + } + + ok = true; + relaxationFactor = + EntityDynamicInterface::extractFloatArgument("coneTwist constraint", arguments, "relaxationFactor", ok, false); + if (!ok) { + relaxationFactor = _relaxationFactor; + } + + if (somethingChanged || + pivotInA != _pivotInA || + axisInA != _axisInA || + otherEntityID != _otherEntityID || + pivotInB != _pivotInB || + axisInB != _axisInB || + swingSpan1 != _swingSpan1 || + swingSpan2 != _swingSpan2 || + twistSpan != _twistSpan || + softness != _softness || + biasFactor != _biasFactor || + relaxationFactor != _relaxationFactor) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { + withWriteLock([&] { + _pivotInA = pivotInA; + _axisInA = axisInA; + _otherEntityID = otherEntityID; + _pivotInB = pivotInB; + _axisInB = axisInB; + _swingSpan1 = swingSpan1; + _swingSpan2 = swingSpan2; + _twistSpan = twistSpan; + _softness = softness; + _biasFactor = biasFactor; + _relaxationFactor = relaxationFactor; + + _active = true; + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); + } + }); + + updateConeTwist(); + } + + return true; +} + +QVariantMap ObjectConstraintConeTwist::getArguments() { + QVariantMap arguments = ObjectDynamic::getArguments(); + withReadLock([&] { + if (_constraint) { + arguments["pivot"] = glmToQMap(_pivotInA); + arguments["axis"] = glmToQMap(_axisInA); + arguments["otherEntityID"] = _otherEntityID; + arguments["otherPivot"] = glmToQMap(_pivotInB); + arguments["otherAxis"] = glmToQMap(_axisInB); + arguments["swingSpan1"] = _swingSpan1; + arguments["swingSpan2"] = _swingSpan2; + arguments["twistSpan"] = _twistSpan; + arguments["softness"] = _softness; + arguments["biasFactor"] = _biasFactor; + arguments["relaxationFactor"] = _relaxationFactor; + + + // arguments["linearPosition"] = static_cast(_constraint)->getLinearPos(); + // arguments["angularPosition"] = static_cast(_constraint)->getAngularPos(); + } + }); + return arguments; +} + +QByteArray ObjectConstraintConeTwist::serialize() const { + QByteArray serializedConstraintArguments; + QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_CONE_TWIST; + dataStream << getID(); + dataStream << ObjectConstraintConeTwist::constraintVersion; + + withReadLock([&] { + dataStream << localTimeToServerTime(_expires); + dataStream << _tag; + + dataStream << _pivotInA; + dataStream << _axisInA; + dataStream << _otherEntityID; + dataStream << _pivotInB; + dataStream << _axisInB; + dataStream << _swingSpan1; + dataStream << _swingSpan2; + dataStream << _twistSpan; + dataStream << _softness; + dataStream << _biasFactor; + dataStream << _relaxationFactor; + }); + + return serializedConstraintArguments; +} + +void ObjectConstraintConeTwist::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectConstraintConeTwist::constraintVersion) { + assert(false); + return; + } + + withWriteLock([&] { + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; + + dataStream >> _pivotInA; + dataStream >> _axisInA; + dataStream >> _otherEntityID; + dataStream >> _pivotInB; + dataStream >> _axisInB; + dataStream >> _swingSpan1; + dataStream >> _swingSpan2; + dataStream >> _twistSpan; + dataStream >> _softness; + dataStream >> _biasFactor; + dataStream >> _relaxationFactor; + + _active = true; + }); +} diff --git a/libraries/physics/src/ObjectConstraintConeTwist.h b/libraries/physics/src/ObjectConstraintConeTwist.h new file mode 100644 index 0000000000..02297e2b91 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintConeTwist.h @@ -0,0 +1,55 @@ +// +// ObjectConstraintConeTwist.h +// libraries/physics/src +// +// Created by Seth Alves 2017-4-23 +// Copyright 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_ObjectConstraintConeTwist_h +#define hifi_ObjectConstraintConeTwist_h + +#include "ObjectConstraint.h" + +// http://bulletphysics.org/Bullet/BulletFull/classbtConeTwistConstraint.html + +class ObjectConstraintConeTwist : public ObjectConstraint { +public: + ObjectConstraintConeTwist(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraintConeTwist(); + + virtual void prepareForPhysicsSimulation() override; + + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; + + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; + + virtual QList getRigidBodies() override; + virtual btTypedConstraint* getConstraint() override; + +protected: + static const uint16_t constraintVersion; + + void updateConeTwist(); + + glm::vec3 _pivotInA; + glm::vec3 _axisInA; + + EntityItemID _otherEntityID; + glm::vec3 _pivotInB; + glm::vec3 _axisInB; + + float _swingSpan1 { TWO_PI }; + float _swingSpan2 { TWO_PI };; + float _twistSpan { TWO_PI };; + float _softness { 1.0f }; + float _biasFactor {0.3f }; + float _relaxationFactor { 1.0f }; +}; + +#endif // hifi_ObjectConstraintConeTwist_h From 84ac863a327e28342b4cae01e257643295f710d5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 13:58:20 -0700 Subject: [PATCH 097/292] 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 e52c94895e5076c946cfb0a51dfd268712745409 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 29 Apr 2017 14:18:14 -0700 Subject: [PATCH 098/292] remove motor from hinge. fix hinge limits --- .../physics/src/ObjectConstraintHinge.cpp | 113 +----------------- libraries/physics/src/ObjectConstraintHinge.h | 12 -- 2 files changed, 3 insertions(+), 122 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 9f591da18b..94052c7625 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -49,50 +49,6 @@ QList ObjectConstraintHinge::getRigidBodies() { } void ObjectConstraintHinge::prepareForPhysicsSimulation() { - // setting the motor velocity doesn't appear to work for anyone. constantly adjusting the - // target angle seems to work. - // https://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7020 - - if (!isMine()) { - // TODO - // don't activate motor for someone else's action? - // maybe don't if this interface isn't the sim owner? - return; - } - uint64_t now = usecTimestampNow(); - withWriteLock([&]{ - btHingeConstraint* constraint = static_cast(_constraint); - if (!constraint) { - return; - } - if (_previousMotorTime == 0) { - _previousMotorTime = now; - return; - } - if (_motorVelocity != 0.0f) { - if (_startMotorTime == 0) { - _startMotorTime = now; - } - float dt = (float)(now - _previousMotorTime) / (float)USECS_PER_SECOND; - float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; - float motorTarget = _motorVelocity * t; - - if (!_motorEnabled) { - constraint->enableMotor(true); - _motorEnabled = true; - } - constraint->setMaxMotorImpulse(_maxImpulse); - constraint->setMotorTarget(motorTarget, dt); - - } else if (_motorTargetTimeScale > 0.0f) { - // TODO -- we probably want a spring-like action here - } else if (_motorEnabled) { - constraint->enableMotor(false); - _motorEnabled = false; - _startMotorTime = 0; - } - _previousMotorTime = now; - }); } void ObjectConstraintHinge::updateHinge() { @@ -103,9 +59,6 @@ void ObjectConstraintHinge::updateHinge() { float softness; float biasFactor; float relaxationFactor; - float motorVelocity; - float motorTarget; - float motorTargetTimeScale; float maxImpulse; withReadLock([&]{ @@ -116,13 +69,6 @@ void ObjectConstraintHinge::updateHinge() { maxImpulse = _maxImpulse; biasFactor = _biasFactor; relaxationFactor = _relaxationFactor; - - // under the hood, motorTarget sets a veloctiy and must be called repeatedly to maintain - // a velocity. _motorTargetTimeScale of 0.0f means the target isn't set. motorVelocity is - // only considered when motorTarget isn't set. - motorVelocity = _motorVelocity; - motorTarget = _motorTarget; - motorTargetTimeScale = _motorTargetTimeScale; softness = _softness; }); @@ -132,6 +78,7 @@ void ObjectConstraintHinge::updateHinge() { auto bulletAxisInA = glmToBullet(axisInA); constraint->setAxis(bulletAxisInA); + constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); } @@ -187,7 +134,7 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { forceBodyNonStatic(); activateBody(); - // updateHinge(); + updateHinge(); return constraint; } @@ -204,10 +151,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { float softness; float biasFactor; float relaxationFactor; - float motorVelocity; - float motorTarget; - float motorTargetTimeScale; - float maxImpulse; bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); @@ -274,34 +217,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { relaxationFactor = _relaxationFactor; } - ok = true; - motorVelocity = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, - "motorVelocity", ok, false); - if (!ok) { - motorVelocity = _motorVelocity; - } - - ok = true; - motorTarget = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, - "motorTarget", ok, false); - if (!ok) { - motorTarget = _motorTarget; - } - - ok = true; - motorTargetTimeScale = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, - "motorTargetTimeScale", ok, false); - if (!ok) { - motorTargetTimeScale = _motorTargetTimeScale; - } - - ok = true; - maxImpulse = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "maxImpulse", ok, false); - if (!ok) { - maxImpulse = _maxImpulse; - } - - if (somethingChanged || pivotInA != _pivotInA || axisInA != _axisInA || @@ -312,11 +227,7 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { high != _high || softness != _softness || biasFactor != _biasFactor || - relaxationFactor != _relaxationFactor || - motorVelocity != _motorVelocity || - motorTarget != _motorTarget || - motorTargetTimeScale != _motorTargetTimeScale || - maxImpulse != _maxImpulse) { + relaxationFactor != _relaxationFactor) { // something changed needUpdate = true; } @@ -334,10 +245,6 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { _softness = softness; _biasFactor = biasFactor; _relaxationFactor = relaxationFactor; - _motorVelocity = motorVelocity; - _motorTarget = motorTarget; - _motorTargetTimeScale = motorTargetTimeScale; - _maxImpulse = maxImpulse; _active = true; @@ -368,10 +275,6 @@ QVariantMap ObjectConstraintHinge::getArguments() { arguments["softness"] = _softness; arguments["biasFactor"] = _biasFactor; arguments["relaxationFactor"] = _relaxationFactor; - arguments["motorVelocity"] = _motorVelocity; - arguments["motorTarget"] = _motorTarget; - arguments["motorTargetTimeScale"] = _motorTargetTimeScale; - arguments["maxImpulse"] = _maxImpulse; arguments["angle"] = static_cast(_constraint)->getHingeAngle(); // [-PI,PI] } }); @@ -400,11 +303,6 @@ QByteArray ObjectConstraintHinge::serialize() const { dataStream << localTimeToServerTime(_expires); dataStream << _tag; - - dataStream << _motorVelocity; - dataStream << _motorTarget; - dataStream << _motorTargetTimeScale; - dataStream << _maxImpulse; }); return serializedConstraintArguments; @@ -446,11 +344,6 @@ void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) { dataStream >> _tag; - dataStream >> _motorVelocity; - dataStream >> _motorTarget; - dataStream >> _motorTargetTimeScale; - dataStream >> _maxImpulse; - _active = true; }); } diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h index cd5f6ac7c2..5e9975ccf8 100644 --- a/libraries/physics/src/ObjectConstraintHinge.h +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -68,21 +68,9 @@ protected: float _maxImpulse { 1.0f }; - float _softness { 0.9f }; - float _biasFactor { 0.3f }; - float _relaxationFactor { 1.0f }; - - float _motorVelocity { 0.0f }; - float _motorTarget { 0.0f }; - float _motorTargetTimeScale { 0.0f }; - - uint64_t _startMotorTime { 0 }; - uint64_t _previousMotorTime { 0 }; - - bool _motorEnabled { false }; }; #endif // hifi_ObjectConstraintHinge_h From 41f699ec9b58cb9a60355e9ef47a18d85cac79df Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 29 Apr 2017 14:28:56 -0700 Subject: [PATCH 099/292] fix hinge limits --- libraries/physics/src/ObjectConstraintHinge.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 94052c7625..14a666f09e 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -59,14 +59,12 @@ void ObjectConstraintHinge::updateHinge() { float softness; float biasFactor; float relaxationFactor; - float maxImpulse; withReadLock([&]{ axisInA = _axisInA; constraint = static_cast(_constraint); low = _low; high = _high; - maxImpulse = _maxImpulse; biasFactor = _biasFactor; relaxationFactor = _relaxationFactor; softness = _softness; @@ -76,8 +74,8 @@ void ObjectConstraintHinge::updateHinge() { return; } - auto bulletAxisInA = glmToBullet(axisInA); - constraint->setAxis(bulletAxisInA); + // auto bulletAxisInA = glmToBullet(axisInA); + // constraint->setAxis(bulletAxisInA); constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); } From 274cc5b5558e9c03a6fe17f9106b8674ef517249 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:29:43 -0700 Subject: [PATCH 100/292] 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 d7a2e571a72a18066bfd000e10ab7648c7c994e2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 14:39:35 -0700 Subject: [PATCH 101/292] 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 8f2fc24885f813a762ed36597b81a1f1fc0f651e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 29 Apr 2017 15:46:04 -0700 Subject: [PATCH 102/292] remove motors from constraints --- libraries/physics/src/ObjectConstraintHinge.h | 1 - .../physics/src/ObjectConstraintSlider.cpp | 77 +------------------ .../physics/src/ObjectConstraintSlider.h | 9 --- 3 files changed, 1 insertion(+), 86 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h index 5e9975ccf8..07ce8eb8a3 100644 --- a/libraries/physics/src/ObjectConstraintHinge.h +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -67,7 +67,6 @@ protected: // limits. A low value will make the the limits more spongy. - float _maxImpulse { 1.0f }; float _softness { 0.9f }; float _biasFactor { 0.3f }; float _relaxationFactor { 1.0f }; diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index 8a722d1744..6a59199356 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -146,13 +146,6 @@ bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { float linearHigh; float angularLow; float angularHigh; - float linearTarget; - float linearTimeScale; - bool linearTargetSet; - float angularTarget; - float angularTimeScale; - bool angularTargetSet; - bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); @@ -212,44 +205,6 @@ bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { angularHigh = _angularHigh; } - - ok = true; - linearTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, - "linearTarget", ok, false); - if (!ok) { - linearTarget = _linearTarget; - linearTargetSet = _linearTargetSet; - } else { - linearTargetSet = true; - } - - - ok = true; - linearTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, - "linearTimeScale", ok, false); - if (!ok) { - linearTimeScale = _linearTimeScale; - } - - ok = true; - angularTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, - "angularTarget", ok, false); - if (!ok) { - angularTarget = _angularTarget; - angularTargetSet = _angularTargetSet; - } else { - angularTargetSet = true; - } - - - ok = true; - angularTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, - "angularTimeScale", ok, false); - if (!ok) { - angularTimeScale = _angularTimeScale; - } - - if (somethingChanged || pointInA != _pointInA || axisInA != _axisInA || @@ -259,13 +214,7 @@ bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { linearLow != _linearLow || linearHigh != _linearHigh || angularLow != _angularLow || - angularHigh != _angularHigh || - linearTarget != _linearTarget || - linearTimeScale != _linearTimeScale || - linearTargetSet != _linearTargetSet || - angularTarget != _angularTarget || - angularTimeScale != _angularTimeScale || - angularTargetSet != _angularTargetSet) { + angularHigh != _angularHigh) { // something changed needUpdate = true; } @@ -282,12 +231,6 @@ bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { _linearHigh = linearHigh; _angularLow = angularLow; _angularHigh = angularHigh; - _linearTarget = linearTarget; - _linearTimeScale = linearTimeScale; - _linearTargetSet = linearTargetSet; - _angularTarget = angularTarget; - _angularTimeScale = angularTimeScale; - _angularTargetSet = angularTargetSet; _active = true; @@ -317,12 +260,6 @@ QVariantMap ObjectConstraintSlider::getArguments() { arguments["linearHigh"] = _linearHigh; arguments["angularLow"] = _angularLow; arguments["angularHigh"] = _angularHigh; - arguments["linearTarget"] = _linearTarget; - arguments["linearTimeScale"] = _linearTimeScale; - arguments["linearTargetSet"] = _linearTargetSet; - arguments["angularTarget"] = _angularTarget; - arguments["angularTimeScale"] = _angularTimeScale; - arguments["angularTargetSet"] = _angularTargetSet; arguments["linearPosition"] = static_cast(_constraint)->getLinearPos(); arguments["angularPosition"] = static_cast(_constraint)->getAngularPos(); } @@ -351,12 +288,6 @@ QByteArray ObjectConstraintSlider::serialize() const { dataStream << _linearHigh; dataStream << _angularLow; dataStream << _angularHigh; - dataStream << _linearTarget; - dataStream << _linearTimeScale; - dataStream << _linearTargetSet; - dataStream << _angularTarget; - dataStream << _angularTimeScale; - dataStream << _angularTargetSet; }); return serializedConstraintArguments; @@ -395,12 +326,6 @@ void ObjectConstraintSlider::deserialize(QByteArray serializedArguments) { dataStream >> _linearHigh; dataStream >> _angularLow; dataStream >> _angularHigh; - dataStream >> _linearTarget; - dataStream >> _linearTimeScale; - dataStream >> _linearTargetSet; - dataStream >> _angularTarget; - dataStream >> _angularTimeScale; - dataStream >> _angularTargetSet; _active = true; }); diff --git a/libraries/physics/src/ObjectConstraintSlider.h b/libraries/physics/src/ObjectConstraintSlider.h index d0746b0aba..d616b9954c 100644 --- a/libraries/physics/src/ObjectConstraintSlider.h +++ b/libraries/physics/src/ObjectConstraintSlider.h @@ -49,15 +49,6 @@ protected: float _angularLow { -TWO_PI }; float _angularHigh { TWO_PI }; - - float _linearTarget { 0.0f }; - float _linearTimeScale { 0.0f }; - bool _linearTargetSet { false }; - - float _angularTarget { 0.0f }; - float _angularTimeScale { 0.0f }; - bool _angularTargetSet { false }; - }; #endif // hifi_ObjectConstraintSlider_h From 9c83e21e1780d0dc0f02cbe047b6acc642492ce0 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 15:50:04 -0700 Subject: [PATCH 103/292] 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 819f02a988a025c7ae33cf18d8f265c4352c0f11 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 30 Apr 2017 11:40:28 +1200 Subject: [PATCH 104/292] Fix keyboard not raising for Facebook username field --- interface/resources/html/raiseAndLowerKeyboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index 63e016c5d4..27ead23124 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -19,7 +19,7 @@ function shouldRaiseKeyboard() { var nodeName = document.activeElement.nodeName; var nodeType = document.activeElement.type; - if (nodeName === "INPUT" && (nodeType === "text" || nodeType === "number" || nodeType === "password") + if (nodeName === "INPUT" && ["email", "number", "password", "tel", "text", "url"].indexOf(nodeType) !== -1 || document.activeElement.nodeName === "TEXTAREA") { return true; } else { From 18dbe3568c8012f649c7ba8ed01d3ff0a3a47222 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 18:25:33 -0700 Subject: [PATCH 105/292] 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 d41a911fd2866aeda640d54a7f6347f94d4d6670 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 29 Apr 2017 19:39:50 -0700 Subject: [PATCH 106/292] 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 92481bf475d7b6af005eeecb2e9bae05ec387baa Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 08:04:46 -0700 Subject: [PATCH 107/292] 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 eca72926514d7aad764d704d862fa20f4b589128 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 09:44:34 -0700 Subject: [PATCH 108/292] 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 7ce0ef2ec456cf95b3f6f96c7a795f6fae7bba72 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 10:04:07 -0700 Subject: [PATCH 109/292] fix spring action so that it can be linear or rotational or both --- interface/src/avatar/AvatarActionHold.cpp | 6 +- interface/src/avatar/AvatarActionHold.h | 3 +- libraries/physics/src/ObjectActionSpring.cpp | 91 ++++++++++++-------- libraries/physics/src/ObjectActionSpring.h | 3 +- 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 98ff687eb3..627dc2ba02 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -113,7 +113,8 @@ void AvatarActionHold::prepareForPhysicsSimulation() { } bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, - glm::vec3& linearVelocity, glm::vec3& angularVelocity) { + glm::vec3& linearVelocity, glm::vec3& angularVelocity, + float& linearTimeScale, float& angularTimeScale) { auto avatarManager = DependencyManager::get(); auto holdingAvatar = std::static_pointer_cast(avatarManager->getAvatarBySessionID(_holderID)); @@ -213,6 +214,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: // update linearVelocity based on offset via _relativePosition; linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); + + linearTimeScale = _linearTimeScale; + angularTimeScale = _angularTimeScale; }); return true; diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index f0b42111ed..7eeda53e06 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -38,7 +38,8 @@ public: bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, - glm::vec3& linearVelocity, glm::vec3& angularVelocity) override; + glm::vec3& linearVelocity, glm::vec3& angularVelocity, + float& linearTimeScale, float& angularTimeScale) override; virtual void prepareForPhysicsSimulation() override; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index df7e5f87a3..7c35806bf0 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -16,6 +16,7 @@ #include "PhysicsLogging.h" const float SPRING_MAX_SPEED = 10.0f; +const float MAX_SPRING_TIMESCALE = 600.0f; // 10 min is a long time const uint16_t ObjectActionSpring::springVersion = 1; @@ -42,11 +43,16 @@ ObjectActionSpring::~ObjectActionSpring() { } bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, - glm::vec3& linearVelocity, glm::vec3& angularVelocity) { - rotation = _desiredRotationalTarget; - position = _desiredPositionalTarget; - linearVelocity = glm::vec3(); - angularVelocity = glm::vec3(); + glm::vec3& linearVelocity, glm::vec3& angularVelocity, + float& linearTimeScale, float& angularTimeScale) { + withReadLock([&]{ + rotation = _desiredRotationalTarget; + position = _desiredPositionalTarget; + linearVelocity = glm::vec3(); + angularVelocity = glm::vec3(); + linearTimeScale = _linearTimeScale; + angularTimeScale = _angularTimeScale; + }); return true; } @@ -61,8 +67,10 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) { glm::vec3 linearVelocity; glm::vec3 angularVelocity; - bool valid = false; - int springCount = 0; + bool linearValid = false; + int linearSpringCount = 0; + bool angularValid = false; + int angularSpringCount = 0; QList springDerivedActions; springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING)); @@ -73,41 +81,55 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) { std::shared_ptr springAction = std::static_pointer_cast(action); glm::quat rotationForAction; glm::vec3 positionForAction; - glm::vec3 linearVelocityForAction, angularVelocityForAction; - bool success = springAction->getTarget(deltaTimeStep, rotationForAction, - positionForAction, linearVelocityForAction, - angularVelocityForAction); + glm::vec3 linearVelocityForAction; + glm::vec3 angularVelocityForAction; + float linearTimeScale; + float angularTimeScale; + bool success = springAction->getTarget(deltaTimeStep, + rotationForAction, positionForAction, + linearVelocityForAction, angularVelocityForAction, + linearTimeScale, angularTimeScale); if (success) { - springCount ++; - if (springAction.get() == this) { - // only use the rotation for this action - valid = true; - rotation = rotationForAction; + if (angularTimeScale < MAX_SPRING_TIMESCALE) { + angularValid = true; + angularSpringCount++; + angularVelocity += angularVelocityForAction; + if (springAction.get() == this) { + // only use the rotation for this action + rotation = rotationForAction; + } } - position += positionForAction; - linearVelocity += linearVelocityForAction; - angularVelocity += angularVelocityForAction; + if (linearTimeScale < MAX_SPRING_TIMESCALE) { + linearValid = true; + linearSpringCount++; + position += positionForAction; + linearVelocity += linearVelocityForAction; + } } } - if (valid && springCount > 0) { - position /= springCount; - linearVelocity /= springCount; - angularVelocity /= springCount; - + if ((angularValid && angularSpringCount > 0) || (linearValid && linearSpringCount > 0)) { withWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - _linearVelocityTarget = linearVelocity; - _angularVelocityTarget = angularVelocity; - _positionalTargetSet = true; - _rotationalTargetSet = true; - _active = true; + if (linearValid && linearSpringCount > 0) { + position /= linearSpringCount; + linearVelocity /= linearSpringCount; + _positionalTarget = position; + _linearVelocityTarget = linearVelocity; + _positionalTargetSet = true; + _active = true; + } + if (angularValid && angularSpringCount > 0) { + angularVelocity /= angularSpringCount; + _rotationalTarget = rotation; + _angularVelocityTarget = angularVelocity; + _rotationalTargetSet = true; + _active = true; + } }); } - return valid; + return linearValid || angularValid; } @@ -133,8 +155,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { return; } - const float MAX_TIMESCALE = 600.0f; // 10 min is a long time - if (_linearTimeScale < MAX_TIMESCALE) { + if (_linearTimeScale < MAX_SPRING_TIMESCALE) { btVector3 targetVelocity(0.0f, 0.0f, 0.0f); btVector3 offset = rigidBody->getCenterOfMassPosition() - glmToBullet(_positionalTarget); float offsetLength = offset.length(); @@ -150,7 +171,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { rigidBody->setLinearVelocity(targetVelocity); } - if (_angularTimeScale < MAX_TIMESCALE) { + if (_angularTimeScale < MAX_SPRING_TIMESCALE) { btVector3 targetVelocity(0.0f, 0.0f, 0.0f); btQuaternion bodyRotation = rigidBody->getOrientation(); diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index de9562d3fa..83a65b36b4 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -28,7 +28,8 @@ public: virtual void deserialize(QByteArray serializedArguments) override; virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, - glm::vec3& linearVelocity, glm::vec3& angularVelocity); + glm::vec3& linearVelocity, glm::vec3& angularVelocity, + float& linearTimeScale, float& angularTimeScale); protected: static const uint16_t springVersion; From c9dfebb713340e9015327e527898ae07ad644898 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 10:58:58 -0700 Subject: [PATCH 110/292] allow spring action to have targets relative to another entity --- libraries/physics/src/ObjectActionSpring.cpp | 63 +++++++++++++++++++- libraries/physics/src/ObjectActionSpring.h | 4 ++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 7c35806bf0..03ab1a845e 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -42,12 +42,53 @@ ObjectActionSpring::~ObjectActionSpring() { #endif } +SpatiallyNestablePointer ObjectActionSpring::getOther() { + SpatiallyNestablePointer other; + withWriteLock([&]{ + if (_otherID == QUuid()) { + // no other + return; + } + other = _other.lock(); + if (other && other->getID() == _otherID) { + // other is already up-to-date + return; + } + if (other) { + // we have a pointer to other, but it's wrong + other.reset(); + _other.reset(); + } + // we have an other-id but no pointer to other cached + QSharedPointer parentFinder = DependencyManager::get(); + if (!parentFinder) { + return; + } + EntityItemPointer ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + bool success; + _other = parentFinder->find(_otherID, success, ownerEntity->getParentTree()); + if (success) { + other = _other.lock(); + } + }); + return other; +} + bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, glm::vec3& linearVelocity, glm::vec3& angularVelocity, float& linearTimeScale, float& angularTimeScale) { + SpatiallyNestablePointer other = getOther(); withReadLock([&]{ - rotation = _desiredRotationalTarget; - position = _desiredPositionalTarget; + if (other) { + rotation = _desiredRotationalTarget * other->getRotation(); + position = _desiredPositionalTarget + other->getPosition(); + } else { + rotation = _desiredRotationalTarget; + position = _desiredPositionalTarget; + } linearVelocity = glm::vec3(); angularVelocity = glm::vec3(); linearTimeScale = _linearTimeScale; @@ -210,6 +251,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { float linearTimeScale; glm::quat rotationalTarget; float angularTimeScale; + QUuid otherID; + bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); @@ -239,11 +282,19 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { angularTimeScale = _angularTimeScale; } + ok = true; + otherID = QUuid(EntityDynamicInterface::extractStringArgument("spring action", + arguments, "otherID", ok, false)); + if (!ok) { + otherID = _otherID; + } + if (somethingChanged || positionalTarget != _desiredPositionalTarget || linearTimeScale != _linearTimeScale || rotationalTarget != _desiredRotationalTarget || - angularTimeScale != _angularTimeScale) { + angularTimeScale != _angularTimeScale || + otherID != _otherID) { // something changed needUpdate = true; } @@ -255,6 +306,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { _linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale)); _desiredRotationalTarget = rotationalTarget; _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale)); + _otherID = otherID; _active = true; auto ownerEntity = _ownerEntity.lock(); @@ -277,6 +329,8 @@ QVariantMap ObjectActionSpring::getArguments() { arguments["targetRotation"] = glmToQMap(_desiredRotationalTarget); arguments["angularTimeScale"] = _angularTimeScale; + + arguments["otherID"] = _otherID; }); return arguments; } @@ -291,6 +345,7 @@ void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const { dataStream << _rotationalTargetSet; dataStream << localTimeToServerTime(_expires); dataStream << _tag; + dataStream << _otherID; }); } @@ -323,6 +378,8 @@ void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, Q dataStream >> _tag; + dataStream >> _otherID; + _active = true; }); } diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index 83a65b36b4..8f810d7956 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -47,6 +47,10 @@ protected: glm::vec3 _linearVelocityTarget; glm::vec3 _angularVelocityTarget; + EntityItemID _otherID; + SpatiallyNestableWeakPointer _other; + SpatiallyNestablePointer getOther(); + virtual bool prepareForSpringUpdate(btScalar deltaTimeStep); void serializeParameters(QDataStream& dataStream) const; From dabec586ec71a1d5cb25d13a7a066e5833e36e3a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 11:01:34 -0700 Subject: [PATCH 111/292] if spring is linked to another entity which we can't find, disable the spring --- libraries/physics/src/ObjectActionSpring.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 03ab1a845e..3cd3926073 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -82,17 +82,24 @@ bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm float& linearTimeScale, float& angularTimeScale) { SpatiallyNestablePointer other = getOther(); withReadLock([&]{ - if (other) { - rotation = _desiredRotationalTarget * other->getRotation(); - position = _desiredPositionalTarget + other->getPosition(); + linearTimeScale = _linearTimeScale; + angularTimeScale = _angularTimeScale; + + if (_otherID != QUuid()) { + if (other) { + rotation = _desiredRotationalTarget * other->getRotation(); + position = _desiredPositionalTarget + other->getPosition(); + } else { + // we should have an "other" but can't find it, so disable the spring. + linearTimeScale = FLT_MAX; + angularTimeScale = FLT_MAX; + } } else { rotation = _desiredRotationalTarget; position = _desiredPositionalTarget; } linearVelocity = glm::vec3(); angularVelocity = glm::vec3(); - linearTimeScale = _linearTimeScale; - angularTimeScale = _angularTimeScale; }); return true; } From 41663c58b8c547ad32da2b4813b5598c8b782c6d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 12:09:37 -0700 Subject: [PATCH 112/292] test app for bullet dynamics --- .../dynamics/dynamics-tests-interface.js | 64 ++ .../tests/dynamics/dynamics-tests.html | 37 + .../developer/tests/dynamics/dynamicsTests.js | 749 ++++++++++++++++++ .../tests/dynamics/dynamicsTests.svg | 69 ++ 4 files changed, 919 insertions(+) create mode 100644 scripts/developer/tests/dynamics/dynamics-tests-interface.js create mode 100644 scripts/developer/tests/dynamics/dynamics-tests.html create mode 100644 scripts/developer/tests/dynamics/dynamicsTests.js create mode 100644 scripts/developer/tests/dynamics/dynamicsTests.svg diff --git a/scripts/developer/tests/dynamics/dynamics-tests-interface.js b/scripts/developer/tests/dynamics/dynamics-tests-interface.js new file mode 100644 index 0000000000..53517fab24 --- /dev/null +++ b/scripts/developer/tests/dynamics/dynamics-tests-interface.js @@ -0,0 +1,64 @@ +"use strict"; +/* globals $, EventBridge */ + +var parameters = { + "lifetime":"integer" +}; + + +function getQueryArgByName(name, url) { + if (!url) { + url = window.location.href; + } + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); +} + + +function addCommandParameters(params) { + // copy from html elements into an associative-array which will get passed (as JSON) through the EventBridge + for (var parameterName in parameters) { + if (parameters.hasOwnProperty(parameterName)) { + var parameterType = parameters[parameterName]; + var strVal = $("#" + parameterName).val(); + if (parameterType == "integer") { + params[parameterName] = parseInt(strVal); + } else if (parameterType == "float") { + params[parameterName] = parseFloat(strVal); + } else { + params[parameterName] = strVal; + } + } + } + return params; +} + + +$(document).ready(function() { + // hook all buttons to EventBridge + $(":button").each(function(index) { + $(this).click(function() { + EventBridge.emitWebEvent(JSON.stringify(addCommandParameters({ "dynamics-tests-command": this.id }))); + }); + }); + + // copy parameters from query-args into elements + for (var parameterName in parameters) { + if (parameters.hasOwnProperty(parameterName)) { + var val = getQueryArgByName(parameterName); + if (val) { + var parameterType = parameters[parameterName]; + if (parameterType == "integer") { + val = parseInt(val); + } else if (parameterType == "float") { + val = parseFloat(val); + } + $("#" + parameterName).val(val.toString()); + } + } + } +}); diff --git a/scripts/developer/tests/dynamics/dynamics-tests.html b/scripts/developer/tests/dynamics/dynamics-tests.html new file mode 100644 index 0000000000..0f324e121c --- /dev/null +++ b/scripts/developer/tests/dynamics/dynamics-tests.html @@ -0,0 +1,37 @@ + + + Dynamics Tests + + + + + + + lifetime: +
    +
    + A platform with a lever. The lever can be moved in a cone and rotated. A spring brings it back to its neutral position. +
    +
    + A grabbable door with a hinge between it and world-space. +
    +
    + A chain of blocks connected by hinges. +
    +
    + The block can only move up and down over a range of 1/2 meter. +
    +
    + A chain of blocks connected by slider constraints. +
    +
    + A chain of spheres connected by ball-and-socket joints between the spheres. +
    +
    + A chain of spheres connected by ball-and-socket joints coincident-with the spheres. +
    +
    + A self-righting ragdoll. The head is on a weak spring vs the body. + + + diff --git a/scripts/developer/tests/dynamics/dynamicsTests.js b/scripts/developer/tests/dynamics/dynamicsTests.js new file mode 100644 index 0000000000..18585ef152 --- /dev/null +++ b/scripts/developer/tests/dynamics/dynamicsTests.js @@ -0,0 +1,749 @@ + +"use strict"; + +/* global Entities, Script, Tablet, MyAvatar, Vec3 */ + +(function() { // BEGIN LOCAL_SCOPE + + var DYNAMICS_TESTS_URL = Script.resolvePath("dynamics-tests.html"); + var DEFAULT_LIFETIME = 120; // seconds + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + + var button = tablet.addButton({ + icon: Script.resolvePath("dynamicsTests.svg"), + text: "Dynamics", + sortOrder: 15 + }); + + + + function coneTwistAndSpringLeverTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: -0.5, z: -2})); + var lifetime = params.lifetime; + + var baseID = Entities.addEntity({ + name: "cone-twist test -- base", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: 0.5, y: 0.2, z: 0.5 }, + position: Vec3.sum(pos, { x: 0, y: 0, z:0 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + var leverID = Entities.addEntity({ + name: "cone-twist test -- lever", + type: "Box", + color: { blue: 128, green: 100, red: 200 }, + dimensions: { x: 0.05, y: 1, z: 0.05 }, + position: Vec3.sum(pos, { x: 0, y: 0.7, z:0 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addEntity({ + name: "cone-twist test -- handle", + type: "Box", + color: { blue: 30, green: 100, red: 200 }, + dimensions: { x: 0.1, y: 0.08, z: 0.08 }, + position: Vec3.sum(pos, { x: 0, y: 0.7 + 0.5, z:0 }), + dynamic: false, + collisionless: true, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + parentID: leverID, + userData: "{ \"grabbableKey\": { \"grabbable\": false } }" + }); + + Entities.addAction("cone-twist", baseID, { + pivot: { x: 0, y: 0.2, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: leverID, + otherPivot: { x: 0, y: -0.55, z: 0 }, + otherAxis: { x: 0, y: 1, z: 0 }, + swingSpan1: Math.PI / 4, + swingSpan2: Math.PI / 4, + twistSpan: Math.PI / 2, + tag: "cone-twist test" + }); + + Entities.addAction("spring", leverID, { + targetRotation: { x: 0, y: 0, z: 0, w: 1 }, + angularTimeScale: 0.2, + tag: "cone-twist test spring" + }); + + + Entities.editEntity(baseID, { gravity: { x: 0, y: -5, z: 0 } }); + } + + function doorVSWorldTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var doorID = Entities.addEntity({ + name: "door test", + type: "Box", + color: { blue: 128, green: 20, red: 20 }, + dimensions: { x: 1.0, y: 2, z: 0.1 }, + position: pos, + dynamic: true, + collisionless: false, + lifetime: lifetime, + gravity: { x: 0, y: 0, z: 0 }, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", doorID, { + pivot: { x: -0.5, y: 0, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + low: 0, + high: Math.PI, + tag: "door hinge test" + }); + } + + function hingeChainTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var offset = 0.28; + var prevEntityID = null; + for (var i = 0; i < 5; i++) { + var newID = Entities.addEntity({ + name: "hinge test " + i, + type: "Box", + color: { blue: 128, green: 40 * i, red: 20 }, + dimensions: { x: 0.2, y: 0.2, z: 0.1 }, + position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}), + dynamic: true, + collisionless: false, + lifetime: lifetime, + gravity: { x: 0, y: 0, z: 0 }, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + if (prevEntityID) { + Entities.addAction("hinge", prevEntityID, { + pivot: { x: 0, y: offset / 2, z: 0 }, + axis: { x: 1, y: 0, z: 0 }, + otherEntityID: newID, + otherPivot: { x: 0, y: -offset / 2, z: 0 }, + otherAxis: { x: 1, y: 0, z: 0 }, + tag: "A/B hinge test " + i + }); + } + prevEntityID = newID; + } + } + + function sliderVSWorldTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var sliderEntityID = Entities.addEntity({ + name: "slider test", + type: "Box", + color: { blue: 128, green: 20, red: 20 }, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + position: pos, + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("slider", sliderEntityID, { + point: { x: -0.5, y: 0, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + linearLow: 0, + linearHigh: 0.6, + tag: "slider test" + }); + } + + function sliderChainTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var offset = 0.28; + var prevEntityID = null; + for (var i = 0; i < 7; i++) { + var newID = Entities.addEntity({ + name: "hinge test " + i, + type: "Box", + color: { blue: 128, green: 40 * i, red: 20 }, + dimensions: { x: 0.2, y: 0.1, z: 0.2 }, + position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + if (prevEntityID) { + Entities.addAction("slider", prevEntityID, { + point: { x: 0, y: 0, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: newID, + otherPoint: { x: 0, y: -offset / 2, z: 0 }, + otherAxis: { x: 0, y: 1, z: 0 }, + linearLow: 0, + linearHigh: 0.6, + tag: "A/B slider test " + i + }); + } + prevEntityID = newID; + } + } + + function ballSocketBetweenTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var offset = 0.2; + var diameter = offset - 0.01; + var prevEntityID = null; + for (var i = 0; i < 7; i++) { + var newID = Entities.addEntity({ + name: "ball and socket test " + i, + type: "Sphere", + color: { blue: 128, green: 40 * i, red: 20 }, + dimensions: { x: diameter, y: diameter, z: diameter }, + position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}), + dynamic: true, + collisionless: false, + lifetime: lifetime, + gravity: { x: 0, y: 0, z: 0 }, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + if (prevEntityID) { + Entities.addAction("ball-socket", prevEntityID, { + pivot: { x: 0, y: offset / 2, z: 0 }, + otherEntityID: newID, + otherPivot: { x: 0, y: -offset / 2, z: 0 }, + tag: "A/B ball-and-socket test " + i + }); + } + prevEntityID = newID; + } + } + + function ballSocketCoincidentTest(params) { + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})); + var lifetime = params.lifetime; + + var offset = 0.2; + var diameter = offset - 0.01; + var prevEntityID = null; + for (var i = 0; i < 7; i++) { + var newID = Entities.addEntity({ + name: "ball and socket test " + i, + type: "Sphere", + color: { blue: 128, green: 40 * i, red: 20 }, + dimensions: { x: diameter, y: diameter, z: diameter }, + position: Vec3.sum(pos, {x: 0, y: offset * i, z:0}), + dynamic: true, + collisionless: false, + lifetime: lifetime, + gravity: { x: 0, y: 0, z: 0 }, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + if (prevEntityID) { + Entities.addAction("ball-socket", prevEntityID, { + pivot: { x: 0, y: 0, z: 0 }, + otherEntityID: newID, + otherPivot: { x: 0, y: offset, z: 0 }, + tag: "A/B ball-and-socket test " + i + }); + } + prevEntityID = newID; + } + } + + function ragdollTest(params) { + var scale = 1.6; + var lifetime = 120; + var pos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 1.0, z: -2})); + + var neckLength = scale * 0.05; + var shoulderGap = scale * 0.1; + var elbowGap = scale * 0.06; + var hipGap = scale * 0.07; + var kneeGap = scale * 0.08; + var ankleGap = scale * 0.06; + var ankleMin = 0; + var ankleMax = Math.PI / 4; + + var headSize = scale * 0.2; + + var bodyHeight = scale * 0.4; + var bodyWidth = scale * 0.3; + var bodyDepth = scale * 0.2; + + var upperArmThickness = scale * 0.05; + var upperArmLength = scale * 0.2; + + var lowerArmThickness = scale * 0.05; + var lowerArmLength = scale * 0.2; + + var legLength = scale * 0.3; + var legThickness = scale * 0.08; + + var shinLength = scale * 0.2; + var shinThickness = scale * 0.06; + + var footLength = scale * 0.2; + var footThickness = scale * 0.03; + var footWidth = scale * 0.08; + + + // + // body + // + + var bodyID = Entities.addEntity({ + name: "ragdoll body", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: bodyDepth, y: bodyHeight, z: bodyWidth }, + position: Vec3.sum(pos, { x: 0, y: scale * 0.0, z:0 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + // + // head + // + + var headID = Entities.addEntity({ + name: "ragdoll head", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: headSize, y: headSize, z: headSize }, + position: Vec3.sum(pos, { x: 0, y: bodyHeight / 2 + headSize / 2 + neckLength, z:0 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0.5, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("spring", headID, { + targetRotation: { x: 0, y: 0, z: 0, w: 1 }, + angularTimeScale: 2.0, + otherID: bodyID, + tag: "cone-twist test spring" + }); + + + var noseID = Entities.addEntity({ + name: "ragdoll nose", + type: "Box", + color: { blue: 128, green: 100, red: 100 }, + dimensions: { x: headSize / 5, y: headSize / 5, z: headSize / 5 }, + localPosition: { x: headSize / 2 + headSize / 10, y: 0, z: 0 }, + dynamic: false, + collisionless: true, + lifetime: lifetime, + parentID: headID, + userData: "{ \"grabbableKey\": { \"grabbable\": false } }" + }); + + Entities.addAction("cone-twist", headID, { + pivot: { x: 0, y: -headSize / 2 - neckLength / 2, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: bodyID, + otherPivot: { x: 0, y: bodyHeight / 2 + neckLength / 2, z: 0 }, + otherAxis: { x: 0, y: 1, z: 0 }, + swingSpan1: Math.PI / 4, + swingSpan2: Math.PI / 4, + twistSpan: Math.PI / 2, + tag: "ragdoll neck joint" + }); + + // + // right upper arm + // + + var rightUpperArmID = Entities.addEntity({ + name: "ragdoll right arm", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: upperArmThickness, y: upperArmThickness, z: upperArmLength }, + position: Vec3.sum(pos, { x: 0, + y: bodyHeight / 2 + upperArmThickness / 2, + z: bodyWidth / 2 + shoulderGap + upperArmLength / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("cone-twist", bodyID, { + pivot: { x: 0, y: bodyHeight / 2 + upperArmThickness / 2, z: bodyWidth / 2 + shoulderGap / 2 }, + axis: { x: 0, y: 0, z: 1 }, + otherEntityID: rightUpperArmID, + otherPivot: { x: 0, y: 0, z: -upperArmLength / 2 - shoulderGap / 2 }, + otherAxis: { x: 0, y: 0, z: 1 }, + swingSpan1: Math.PI / 2, + swingSpan2: Math.PI / 2, + twistSpan: 0, + tag: "ragdoll right shoulder joint" + }); + + // + // left upper arm + // + + var leftUpperArmID = Entities.addEntity({ + name: "ragdoll left arm", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: upperArmThickness, y: upperArmThickness, z: upperArmLength }, + position: Vec3.sum(pos, { x: 0, + y: bodyHeight / 2 + upperArmThickness / 2, + z: -bodyWidth / 2 - shoulderGap - upperArmLength / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("cone-twist", bodyID, { + pivot: { x: 0, y: bodyHeight / 2 + upperArmThickness / 2, z: -bodyWidth / 2 - shoulderGap / 2 }, + axis: { x: 0, y: 0, z: -1 }, + otherEntityID: leftUpperArmID, + otherPivot: { x: 0, y: 0, z: upperArmLength / 2 + shoulderGap / 2 }, + otherAxis: { x: 0, y: 0, z: -1 }, + swingSpan1: Math.PI / 2, + swingSpan2: Math.PI / 2, + twistSpan: 0, + tag: "ragdoll left shoulder joint" + }); + + // + // right lower arm + // + + var rightLowerArmID = Entities.addEntity({ + name: "ragdoll right lower arm", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: lowerArmThickness, y: lowerArmThickness, z: lowerArmLength }, + position: Vec3.sum(pos, { x: 0, + y: bodyHeight / 2 - upperArmThickness / 2, + z: bodyWidth / 2 + shoulderGap + upperArmLength + elbowGap + lowerArmLength / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -1, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", rightLowerArmID, { + pivot: { x: 0, y: 0, z: -lowerArmLength / 2 - elbowGap / 2 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: rightUpperArmID, + otherPivot: { x: 0, y: 0, z: upperArmLength / 2 + elbowGap / 2 }, + otherAxis: { x: 0, y: 1, z: 0 }, + low: Math.PI / -2, + high: 0, + tag: "ragdoll right elbow joint" + }); + + // + // left lower arm + // + + var leftLowerArmID = Entities.addEntity({ + name: "ragdoll left lower arm", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: lowerArmThickness, y: lowerArmThickness, z: lowerArmLength }, + position: Vec3.sum(pos, { x: 0, + y: bodyHeight / 2 - upperArmThickness / 2, + z: -bodyWidth / 2 - shoulderGap - upperArmLength - elbowGap - lowerArmLength / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -1, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", leftLowerArmID, { + pivot: { x: 0, y: 0, z: lowerArmLength / 2 + elbowGap / 2 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: leftUpperArmID, + otherPivot: { x: 0, y: 0, z: -upperArmLength / 2 - elbowGap / 2 }, + otherAxis: { x: 0, y: 1, z: 0 }, + low: 0, + high: Math.PI / 2, + tag: "ragdoll left elbow joint" + }); + + // + // right leg + // + + var rightLegID = Entities.addEntity({ + name: "ragdoll right arm", + type: "Box", + color: { blue: 20, green: 200, red: 20 }, + dimensions: { x: legThickness, y: legLength, z: legThickness }, + position: Vec3.sum(pos, { x: 0, y: -bodyHeight / 2 - hipGap - legLength / 2, z: bodyWidth / 2 - legThickness / 2 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("cone-twist", rightLegID, { + pivot: { x: 0, y: legLength / 2 + hipGap / 2, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: bodyID, + otherPivot: { x: 0, y: -bodyHeight / 2 - hipGap / 2, z: bodyWidth / 2 - legThickness / 2 }, + otherAxis: Vec3.normalize({ x: -1, y: 1, z: 0 }), + swingSpan1: Math.PI / 4, + swingSpan2: Math.PI / 4, + twistSpan: 0, + tag: "ragdoll right hip joint" + }); + + // + // left leg + // + + var leftLegID = Entities.addEntity({ + name: "ragdoll left arm", + type: "Box", + color: { blue: 20, green: 200, red: 20 }, + dimensions: { x: legThickness, y: legLength, z: legThickness }, + position: Vec3.sum(pos, { x: 0, y: -bodyHeight / 2 - hipGap - legLength / 2, z: -bodyWidth / 2 + legThickness / 2 }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("cone-twist", leftLegID, { + pivot: { x: 0, y: legLength / 2 + hipGap / 2, z: 0 }, + axis: { x: 0, y: 1, z: 0 }, + otherEntityID: bodyID, + otherPivot: { x: 0, y: -bodyHeight / 2 - hipGap / 2, z: -bodyWidth / 2 + legThickness / 2 }, + otherAxis: Vec3.normalize({ x: -1, y: 1, z: 0 }), + swingSpan1: Math.PI / 4, + swingSpan2: Math.PI / 4, + twistSpan: 0, + tag: "ragdoll left hip joint" + }); + + // + // right shin + // + + var rightShinID = Entities.addEntity({ + name: "ragdoll right shin", + type: "Box", + color: { blue: 20, green: 200, red: 20 }, + dimensions: { x: shinThickness, y: shinLength, z: shinThickness }, + position: Vec3.sum(pos, { x: 0, + y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength / 2, + z: bodyWidth / 2 - legThickness / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -2, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", rightShinID, { + pivot: { x: 0, y: shinLength / 2 + kneeGap / 2, z: 0 }, + axis: { x: 0, y: 0, z: 1 }, + otherEntityID: rightLegID, + otherPivot: { x: 0, y: -legLength / 2 - kneeGap / 2, z: 0 }, + otherAxis: { x: 0, y: 0, z: 1 }, + low: 0, + high: Math.PI / 2, + tag: "ragdoll right knee joint" + }); + + + // + // left shin + // + + var leftShinID = Entities.addEntity({ + name: "ragdoll left shin", + type: "Box", + color: { blue: 20, green: 200, red: 20 }, + dimensions: { x: shinThickness, y: shinLength, z: shinThickness }, + position: Vec3.sum(pos, { x: 0, + y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength / 2, + z: -bodyWidth / 2 + legThickness / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -2, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", leftShinID, { + pivot: { x: 0, y: shinLength / 2 + kneeGap / 2, z: 0 }, + axis: { x: 0, y: 0, z: 1 }, + otherEntityID: leftLegID, + otherPivot: { x: 0, y: -legLength / 2 - kneeGap / 2, z: 0 }, + otherAxis: { x: 0, y: 0, z: 1 }, + low: 0, + high: Math.PI / 2, + tag: "ragdoll left knee joint" + }); + + // + // right foot + // + + var rightFootID = Entities.addEntity({ + name: "ragdoll right foot", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: footLength, y: footThickness, z: footWidth }, + position: Vec3.sum(pos, { x: -shinThickness / 2 + footLength / 2, + y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength - ankleGap - footThickness / 2, + z: bodyWidth / 2 - legThickness / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -5, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", rightFootID, { + pivot: { x: -footLength / 2 + shinThickness / 2, y: ankleGap / 2, z: 0 }, + axis: { x: 0, y: 0, z: 1 }, + otherEntityID: rightShinID, + otherPivot: { x: 0, y: -shinLength / 2 - ankleGap / 2, z: 0 }, + otherAxis: { x: 0, y: 0, z: 1 }, + low: ankleMin, + high: ankleMax, + tag: "ragdoll right ankle joint" + }); + + // + // left foot + // + + var leftFootID = Entities.addEntity({ + name: "ragdoll left foot", + type: "Box", + color: { blue: 128, green: 100, red: 20 }, + dimensions: { x: footLength, y: footThickness, z: footWidth }, + position: Vec3.sum(pos, { x: -shinThickness / 2 + footLength / 2, + y: -bodyHeight / 2 - hipGap - legLength - kneeGap - shinLength - ankleGap - footThickness / 2, + z: bodyWidth / 2 - legThickness / 2 + }), + dynamic: true, + collisionless: false, + gravity: { x: 0, y: -5, z: 0 }, + lifetime: lifetime, + userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }); + + Entities.addAction("hinge", leftFootID, { + pivot: { x: -footLength / 2 + shinThickness / 2, y: ankleGap / 2, z: 0 }, + axis: { x: 0, y: 0, z: 1 }, + otherEntityID: leftShinID, + otherPivot: { x: 0, y: -shinLength / 2 - ankleGap / 2, z: 0 }, + otherAxis: { x: 0, y: 0, z: 1 }, + low: ankleMin, + high: ankleMax, + tag: "ragdoll left ankle joint" + }); + + } + + function onWebEventReceived(eventString) { + print("received web event: " + JSON.stringify(eventString)); + if (typeof eventString === "string") { + var event; + try { + event = JSON.parse(eventString); + } catch(e) { + return; + } + + if (event["dynamics-tests-command"]) { + var commandToFunctionMap = { + "cone-twist-and-spring-lever-test": coneTwistAndSpringLeverTest, + "door-vs-world-test": doorVSWorldTest, + "hinge-chain-test": hingeChainTest, + "slider-vs-world-test": sliderVSWorldTest, + "slider-chain-test": sliderChainTest, + "ball-socket-between-test": ballSocketBetweenTest, + "ball-socket-coincident-test": ballSocketCoincidentTest, + "ragdoll-test": ragdollTest + }; + + var cmd = event["dynamics-tests-command"]; + if (commandToFunctionMap.hasOwnProperty(cmd)) { + var func = commandToFunctionMap[cmd]; + func(event); + } + } + } + } + + + var onDynamicsTestsScreen = false; + var shouldActivateButton = false; + + function onClicked() { + if (onDynamicsTestsScreen) { + tablet.gotoHomeScreen(); + } else { + shouldActivateButton = true; + tablet.gotoWebScreen(DYNAMICS_TESTS_URL + + "?lifetime=" + DEFAULT_LIFETIME.toString() + ); + onDynamicsTestsScreen = true; + } + } + + function onScreenChanged() { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onDynamicsTestsScreen = shouldActivateButton; + } + + function cleanup() { + button.clicked.disconnect(onClicked); + tablet.removeButton(button); + } + + button.clicked.connect(onClicked); + tablet.webEventReceived.connect(onWebEventReceived); + tablet.screenChanged.connect(onScreenChanged); + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE diff --git a/scripts/developer/tests/dynamics/dynamicsTests.svg b/scripts/developer/tests/dynamics/dynamicsTests.svg new file mode 100644 index 0000000000..a1407e87d4 --- /dev/null +++ b/scripts/developer/tests/dynamics/dynamicsTests.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + From 1b67a8b251730bb7797ac6bbededc42960004dfc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 12:23:34 -0700 Subject: [PATCH 113/292] cleanups --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +-- libraries/physics/src/ObjectConstraintConeTwist.cpp | 4 ---- .../tests/dynamics/dynamics-tests-interface.js | 12 ++++++++++++ scripts/developer/tests/dynamics/dynamicsTests.js | 10 ++++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ce51ccd019..82b4bf703d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_MORE_CONSTRAINTS; + return VERSION_ENTITIES_BULLET_DYNAMICS; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index f84b16818a..746ae80361 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -208,8 +208,7 @@ const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66; const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; -const PacketVersion VERSION_ENTITIES_SLIDER_CONSTRAINT = 70; -const PacketVersion VERSION_ENTITIES_MORE_CONSTRAINTS = 71; +const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/libraries/physics/src/ObjectConstraintConeTwist.cpp b/libraries/physics/src/ObjectConstraintConeTwist.cpp index 7a7120cd09..a0a9a5fe0c 100644 --- a/libraries/physics/src/ObjectConstraintConeTwist.cpp +++ b/libraries/physics/src/ObjectConstraintConeTwist.cpp @@ -293,10 +293,6 @@ QVariantMap ObjectConstraintConeTwist::getArguments() { arguments["softness"] = _softness; arguments["biasFactor"] = _biasFactor; arguments["relaxationFactor"] = _relaxationFactor; - - - // arguments["linearPosition"] = static_cast(_constraint)->getLinearPos(); - // arguments["angularPosition"] = static_cast(_constraint)->getAngularPos(); } }); return arguments; diff --git a/scripts/developer/tests/dynamics/dynamics-tests-interface.js b/scripts/developer/tests/dynamics/dynamics-tests-interface.js index 53517fab24..7db8b86a05 100644 --- a/scripts/developer/tests/dynamics/dynamics-tests-interface.js +++ b/scripts/developer/tests/dynamics/dynamics-tests-interface.js @@ -1,3 +1,15 @@ +// +// dynamics-tests-interface.js +// scripts/developer/tests/dynamics/ +// +// Created by Seth Alves 2017-4-30 +// Copyright 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 +// + + "use strict"; /* globals $, EventBridge */ diff --git a/scripts/developer/tests/dynamics/dynamicsTests.js b/scripts/developer/tests/dynamics/dynamicsTests.js index 18585ef152..7e55b831ca 100644 --- a/scripts/developer/tests/dynamics/dynamicsTests.js +++ b/scripts/developer/tests/dynamics/dynamicsTests.js @@ -1,3 +1,13 @@ +// +// dynamicsTests.js +// scripts/developer/tests/dynamics/ +// +// Created by Seth Alves 2017-4-30 +// Copyright 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 +// "use strict"; From 292065a918b7bb30ba3ae9b3801c554073594cbd Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 13:32:21 -0700 Subject: [PATCH 114/292] 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 2e23a073f717531f4899a738c4a62b83440d7986 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 14:03:45 -0700 Subject: [PATCH 115/292] trying to get electron package to download --- server-console/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/package.json b/server-console/package.json index f72ffc347f..16d6459656 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -10,7 +10,7 @@ "devDependencies": { "electron-compilers": "^1.0.1", "electron-packager": "^6.0.2", - "electron-prebuilt": "0.37.5" + "electron": "0.37.5" }, "repository": { "type": "git", From 6808cf62b61942796ae90a5dd4f2239296cead40 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Apr 2017 14:09:44 -0700 Subject: [PATCH 116/292] trying to get electron package to download --- server-console/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/package.json b/server-console/package.json index 16d6459656..f72ffc347f 100644 --- a/server-console/package.json +++ b/server-console/package.json @@ -10,7 +10,7 @@ "devDependencies": { "electron-compilers": "^1.0.1", "electron-packager": "^6.0.2", - "electron": "0.37.5" + "electron-prebuilt": "0.37.5" }, "repository": { "type": "git", From bc9049cb42b2ef7217e5f9be5127ef1fd789b69d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 14:19:33 -0700 Subject: [PATCH 117/292] 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 2bf7b12c7dc13682424d00ad6cf7dbb9d92ee4be Mon Sep 17 00:00:00 2001 From: druiz17 Date: Sun, 30 Apr 2017 15:27:34 -0700 Subject: [PATCH 118/292] open webview from tabletwebscreen --- interface/resources/qml/controls/TabletWebView.qml | 5 ----- interface/resources/qml/hifi/tablet/TabletRoot.qml | 8 +++++--- interface/resources/qml/hifi/tablet/WindowRoot.qml | 5 +++++ libraries/script-engine/src/TabletScriptingInterface.cpp | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index e43441045f..e202d89060 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -270,13 +270,8 @@ Item { } onNewViewRequested: { - console.log("--------------> new window opened <-------------"); request.openIn(webview); } - - onWindowCloseRequested: { - console.log("-------------> requested to cloes window <---------------"); - } } HiFiControls.Keyboard { diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index b19525d21a..e78cc74b7b 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -24,7 +24,6 @@ Item { option = value; } - Component { id: profileCreator; HFTabletWebEngineProfile {} } Component { id: inputDialogBuilder; TabletQueryDialog { } } function inputDialog(properties) { openModal = inputDialogBuilder.createObject(tabletRoot, properties); @@ -91,6 +90,11 @@ Item { loader.item.gotoPreviousApp = true; } } + + function loadWebBase() { + loader.source = ""; + loader.source = "TabletWebView.qml"; + } function returnToPreviousApp() { tabletApps.remove(currentApp); @@ -117,8 +121,6 @@ Item { function loadWebUrl(url, injectedJavaScriptUrl) { tabletApps.clear(); - var newProfile = profileCreator.createObject(); - loader.item.viewProfile = newProfile; loader.item.url = url; loader.item.scriptURL = injectedJavaScriptUrl; tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url}); diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 5f842df7b7..72dcdf0cbd 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -38,6 +38,11 @@ Windows.ScrollingWindow { loader.source = url; } + function loadWebBase() { + loader.source = ""; + loader.source = "WindowWebView.qml"; + } + function loadWebUrl(url, injectedJavaScriptUrl) { loader.item.url = url; loader.item.scriptURL = injectedJavaScriptUrl; diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index bffe318c11..d4eeecc82e 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -508,7 +508,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS if (root) { removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); + QMetaObject::invokeMethod(root, "loadWebBase"); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); } From e740fb67f4b1fa8b404c0cbaa145fd22e1acfeb8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 30 Apr 2017 15:50:30 -0700 Subject: [PATCH 119/292] 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 f60deb0cfcaffce2d06a73fadae17a34e9162f25 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Sun, 30 Apr 2017 22:55:44 -0700 Subject: [PATCH 120/292] fixed the twitter link --- .../resources/qml/controls/TabletWebView.qml | 24 +++++++++++++++---- interface/resources/qml/controls/WebView.qml | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index e202d89060..67fb724ad0 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -28,6 +28,7 @@ Item { property alias webView: webview property alias profile: webview.profile property bool remove: false + property bool newPage: false property int currentPage: -1 // used as a model for repeater @@ -144,9 +145,22 @@ Item { view.setEnabled(true); } + function isNewPageOpen() { + return (web.newPage && web.currentPage > 0); + } + + function shouldLoadUrl(url) { + switch (url) { + case "https://twitter.com/intent/sessions": + return true; + } + return false; + } function urlAppend(url) { - if (removingPage) { + console.log(url); + if (removingPage || shouldLoadUrl(url) || isNewPageOpen()) { removingPage = false; + web.newPage = false; return; } var lurl = decodeURIComponent(url) @@ -156,7 +170,7 @@ Item { if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) { timer.start(); pagesModel.append({webUrl: lurl}); - } + }; } onCurrentPageChanged: { @@ -228,6 +242,7 @@ Item { worldId: WebEngineScript.MainWorld } + property string urlTag: "noDownload=false"; userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] property string newUrl: "" @@ -254,9 +269,9 @@ Item { keyboard.resetShiftMode(false); // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { - urlAppend(loadRequest.url.toString()); + var url = loadRequest.url.toString(); + urlAppend(url); loadingPage = true; - var url = loadRequest.url.toString(); if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { root.stop(); @@ -270,6 +285,7 @@ Item { } onNewViewRequested: { + web.newPage = true; request.openIn(webview); } } diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 04ff731a25..b954fbc11b 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -99,6 +99,7 @@ Item { // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); + console.log(url); url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { From 28aaa545d84909cb44755c027ed9ddb4d0f80f8a Mon Sep 17 00:00:00 2001 From: CFresquet Date: Mon, 1 May 2017 08:07:16 -0700 Subject: [PATCH 121/292] Created RequestModule --- scripts/system/makeUserConnection.js | 69 ++++------------------ scripts/system/pal.js | 61 +++---------------- scripts/system/request.js | 87 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 112 deletions(-) create mode 100644 scripts/system/request.js diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 0ffea0c568..56eaf34a6d 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -9,6 +9,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +var RequestModule = Script.require('./request.js'); + (function() { // BEGIN LOCAL_SCOPE var LABEL = "makeUserConnection"; @@ -121,64 +124,13 @@ [].map.call(arguments, JSON.stringify))); } + function debugPrint() { + print( [].slice.call(arguments).join(' ') ); + } + function cleanId(guidWithCurlyBraces) { return guidWithCurlyBraces.slice(1, -1); } - 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; - } - } - if (error) { - response = {statusCode: httpRequest.status}; - } - 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) { - if (options.body.hasOwnProperty(key)) { - 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 || {}) { - if (options.headers.hasOwnProperty(key)) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); - } function handToString(hand) { if (hand === Controller.Standard.RightHand) { @@ -420,6 +372,7 @@ // 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. function startHandshake(fromKeyboard) { + if (fromKeyboard) { debug("adding animation"); // just in case order of press/unpress is broken @@ -492,7 +445,7 @@ } // 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); + RequestModule.request({ uri: requestUrl, method: 'DELETE' }, debug); } function updateTriggers(value, fromKeyboard, hand) { @@ -606,7 +559,7 @@ connectionRequestCompleted(); } else { // poll Script.setTimeout(function () { - request({ + RequestModule.request({ uri: requestUrl, // N.B.: server gives bad request if we specify json content type, so don't do that. body: requestBody @@ -658,7 +611,7 @@ // 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({ // + RequestModule.request({ // uri: requestUrl, method: 'POST', json: true, diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ae64065216..c543926fb3 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -12,6 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var RequestModule = Script.require('./request.js'); + (function() { // BEGIN LOCAL_SCOPE var populateNearbyUserList, color, textures, removeOverlays, @@ -271,7 +273,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See break; case 'removeConnection': connectionUserName = message.params; - request({ + RequestModule.request({ uri: METAVERSE_BASE + '/api/v1/user/connections/' + connectionUserName, method: 'DELETE' }, function (error, response) { @@ -285,7 +287,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See case 'removeFriend': friendUserName = message.params; - request({ + RequestModule.request({ uri: METAVERSE_BASE + '/api/v1/user/friends/' + friendUserName, method: 'DELETE' }, function (error, response) { @@ -298,7 +300,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See break case 'addFriend': friendUserName = message.params; - request({ + RequestModule.request({ uri: METAVERSE_BASE + '/api/v1/user/friends', method: 'POST', json: true, @@ -331,58 +333,9 @@ function updateUser(data) { // // These are prototype versions that will be changed when the back end changes. var METAVERSE_BASE = location.metaverseServerUrl; -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); -} - function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise. - request({ + RequestModule.request({ uri: url }, function (error, response) { if (error || (response.status !== 'success')) { @@ -394,7 +347,7 @@ function requestJSON(url, callback) { // callback(data) if successfull. Logs oth } function getProfilePicture(username, callback) { // callback(url) if successfull. (Logs otherwise) // FIXME Prototype scrapes profile picture. We should include in user status, and also make available somewhere for myself - request({ + RequestModule.request({ uri: METAVERSE_BASE + '/users/' + username }, function (error, html) { var matched = !error && html.match(/img class="users-img" src="([^"]*)"/); diff --git a/scripts/system/request.js b/scripts/system/request.js new file mode 100644 index 0000000000..a5c419892e --- /dev/null +++ b/scripts/system/request.js @@ -0,0 +1,87 @@ +"use strict"; + +// request.js +// +// Created by Cisco Fresquet on 04/24/2017. +// Copyright 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 +// + +/* global module */ +// @module request +// +// This module contains the `request` module implementation + +// =========================================================================================== +module.exports = { + + // ------------------------------------------------------------------ + test: function () { + debug("Test completed."); + }, + + // ------------------------------------------------------------------ + request: function (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; + } + } + if (error) { + response = { statusCode: httpRequest.status }; + } + 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) { + if (options.body.hasOwnProperty(key)) { + 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 || {}) { + if (options.headers.hasOwnProperty(key)) { + httpRequest.setRequestHeader(key, options.headers[key]); + } + } + httpRequest.open(options.method, options.uri, true); + httpRequest.send(options.body); + } +}; + +// =========================================================================================== +// @function - debug logging +function debug() { + print('RequestModule | ' + [].slice.call(arguments).join(' ')); +} From 0c09823f6731e15e50c5bf361144ea120fb94d20 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 May 2017 09:10:31 -0700 Subject: [PATCH 122/292] clang warning fixes, unused variables... --- interface/src/avatar/MyAvatar.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 05e5277746..2966f8a95d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -83,16 +83,12 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; // default values, used when avatar is missing joints... (avatar space) -static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; +// static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; -static const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f }; static const glm::vec3 DEFAULT_AVATAR_NECK_POS { 0.0f, 0.445f, 0.025f }; -static const glm::quat DEFAULT_AVATAR_NECK_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_SPINE2_POS { 0.0f, 0.32f, 0.02f }; -static const glm::quat DEFAULT_AVATAR_SPINE2_ROT { Quaternions::Y_180}; static const glm::vec3 DEFAULT_AVATAR_HIPS_POS { 0.0f, 0.0f, 0.0f }; -static const glm::quat DEFAULT_AVATAR_HIPS_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_LEFTFOOT_POS { -0.08f, -0.96f, 0.029f}; static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.9154590368270874f, -0.005437685176730156f, -0.023744143545627594f }; static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f }; From 9f33af479dd97c7b86d0011a88f0e3a4bdd28ccc Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 18:04:22 +0100 Subject: [PATCH 123/292] saving work --- .../resources/qml/controls/TabletWebView.qml | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 67fb724ad0..8fd92a0cb0 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -23,12 +23,15 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false property bool isDesktop: false + property bool startingUp: true property bool removingPage: false - property bool loadingPage: false property alias webView: webview property alias profile: webview.profile property bool remove: false - property bool newPage: false + property bool windowClosed: false + property bool loadingStarted: false + property bool loadingFinished: false + property var urlList: [] property int currentPage: -1 // used as a model for repeater @@ -144,10 +147,6 @@ Item { view.setActiveFocusOnPress(true); view.setEnabled(true); } - - function isNewPageOpen() { - return (web.newPage && web.currentPage > 0); - } function shouldLoadUrl(url) { switch (url) { @@ -156,30 +155,41 @@ Item { } return false; } + function urlAppend(url) { console.log(url); - if (removingPage || shouldLoadUrl(url) || isNewPageOpen()) { + if (removingPage || shouldLoadUrl(url)) { removingPage = false; - web.newPage = false; return; } var lurl = decodeURIComponent(url) if (lurl[lurl.length - 1] !== "/") { lurl = lurl + "/" } - if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) { + console.log("-------> trying to append url <------------"); + console.log(currentPage); + console.log(pagesModel.get(currentPage).webUrl !== lurl); + if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl)) { timer.start(); + console.log("---------> appending url <-------------"); pagesModel.append({webUrl: lurl}); }; } onCurrentPageChanged: { - if (currentPage >= 0 && currentPage < pagesModel.count) { + if (currentPage >= 0 && currentPage < pagesModel.count && removingPage) { timer.start(); webview.url = pagesModel.get(currentPage).webUrl; web.url = webview.url; web.address = webview.url; + removingPage = false; + } else if (startingUp) { + webview.url = pagesModel.get(currentPage).webUrl; + web.url = webview.url; + web.address = webview.url; + startingUp = false; } + } onUrlChanged: { @@ -270,8 +280,9 @@ Item { // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); - urlAppend(url); - loadingPage = true; + web.urlList.push(url); + //urlAppend(url); + web.loadingStarted = true; if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { root.stop(); @@ -282,10 +293,22 @@ Item { if (WebEngineView.LoadFailedStatus == loadRequest.status) { console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString()); } + + if (WebEngineView.LoadSucceededStatus == loadRequest.status) { + console.log + urlList = []; + } + } + + onWindowCloseRequested: { + console.log("---------->requested to closeWindow <--------------"); } onNewViewRequested: { - web.newPage = true; + console.log("-----------> newViewRequested <--------------"); + var currentUrl = webview.url; + console.log(currentUrl); + urlAppend(currentUrl); request.openIn(webview); } } From 763feccbe104fab542d80fcf67dc1d38188d8ae0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 18:07:42 +0100 Subject: [PATCH 124/292] removed tabs --- interface/resources/qml/hifi/tablet/TabletRoot.qml | 8 ++++---- interface/resources/qml/hifi/tablet/WindowRoot.qml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index e78cc74b7b..33af7da1ae 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -92,16 +92,16 @@ Item { } function loadWebBase() { - loader.source = ""; - loader.source = "TabletWebView.qml"; + loader.source = ""; + loader.source = "TabletWebView.qml"; } function returnToPreviousApp() { tabletApps.remove(currentApp); var isWebPage = tabletApps.get(currentApp).isWebUrl; if (isWebPage) { - var webUrl = tabletApps.get(currentApp).appWebUrl; - var scriptUrl = tabletApps.get(currentApp).scriptUrl; + var webUrl = tabletApps.get(currentApp).appWebUrl; + var scriptUrl = tabletApps.get(currentApp).scriptUrl; loadSource("TabletWebView.qml"); loadWebUrl(webUrl, scriptUrl); } else { diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 72dcdf0cbd..470fd4a830 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -39,8 +39,8 @@ Windows.ScrollingWindow { } function loadWebBase() { - loader.source = ""; - loader.source = "WindowWebView.qml"; + loader.source = ""; + loader.source = "WindowWebView.qml"; } function loadWebUrl(url, injectedJavaScriptUrl) { From 074a11306c02e850774a237f588d75296f63e099 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 13:21:52 -0700 Subject: [PATCH 125/292] 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 126/292] 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 c205bf0980a2d4450b5bcaf876cd68f11840406d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 21:23:38 +0100 Subject: [PATCH 127/292] working on cleaner navigation --- .../resources/qml/controls/TabletWebView.qml | 105 +++++++----------- 1 file changed, 40 insertions(+), 65 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 8fd92a0cb0..ca49dafe53 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -23,6 +23,7 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false property bool isDesktop: false + property string initialPage: "" property bool startingUp: true property bool removingPage: false property alias webView: webview @@ -32,6 +33,7 @@ Item { property bool loadingStarted: false property bool loadingFinished: false property var urlList: [] + property var forwardList: [] property int currentPage: -1 // used as a model for repeater @@ -103,15 +105,15 @@ Item { } function goBack() { - if (webview.canGoBack && !isUrlLoaded(webview.url)) { - if (currentPage > 0) { - removingPage = true; - pagesModel.remove(currentPage); - } + if (webview.canGoBack) { + forwardList.push(webview.url); webview.goBack(); - } else if (currentPage > 0) { + } else if (web.urlList.length > 0) { removingPage = true; - pagesModel.remove(currentPage); + var url = web.urlList.pop(); + loadUrl(url); + } else if (fowardList.length == 1) { + console.log("--------------> going foward <---------------"); } } @@ -147,53 +149,43 @@ Item { view.setActiveFocusOnPress(true); view.setEnabled(true); } - - function shouldLoadUrl(url) { - switch (url) { - case "https://twitter.com/intent/sessions": - return true; - } - return false; + + function loadUrl(url) { + webview.url = url + web.url = webview.url; + web.address = webview.url; } + + function onInitialPage(url) { + return (url === webview.url); + } + function urlAppend(url) { - console.log(url); - if (removingPage || shouldLoadUrl(url)) { - removingPage = false; - return; - } var lurl = decodeURIComponent(url) if (lurl[lurl.length - 1] !== "/") { lurl = lurl + "/" } - console.log("-------> trying to append url <------------"); - console.log(currentPage); - console.log(pagesModel.get(currentPage).webUrl !== lurl); - if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl)) { - timer.start(); - console.log("---------> appending url <-------------"); - pagesModel.append({webUrl: lurl}); - }; + web.urlList.push(url); + setBackButtonStatus(); } - onCurrentPageChanged: { - if (currentPage >= 0 && currentPage < pagesModel.count && removingPage) { - timer.start(); - webview.url = pagesModel.get(currentPage).webUrl; - web.url = webview.url; - web.address = webview.url; - removingPage = false; - } else if (startingUp) { - webview.url = pagesModel.get(currentPage).webUrl; - web.url = webview.url; - web.address = webview.url; - startingUp = false; + function setBackButtonStatus() { + if (web.urlList.length > 0 || webview.canGoBack) { + back.enabledColor = hifi.colors.darkGray; + back.enabled = true; + } else { + back.enabledColor = hifi.colors.baseGray; + back.enabled = false; } - } onUrlChanged: { - gotoPage(url) + loadUrl(url); + if (startingUp) { + web.initialPage = webview.url; + startingUp = false; + } } QtObject { @@ -201,18 +193,7 @@ Item { WebChannel.id: "eventBridgeWrapper" property var eventBridge; } - - Timer { - id: timer - interval: 200 - running: false - repeat: false - onTriggered: timer.stop(); - } - - - - + WebEngineView { id: webview objectName: "webEngineView" @@ -280,9 +261,6 @@ Item { // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); - web.urlList.push(url); - //urlAppend(url); - web.loadingStarted = true; if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { root.stop(); @@ -295,19 +273,16 @@ Item { } if (WebEngineView.LoadSucceededStatus == loadRequest.status) { - console.log - urlList = []; + web.address = webview.url; + if (startingUp) { + web.initialPage = webview.url; + startingUp = false; + } } } - - onWindowCloseRequested: { - console.log("---------->requested to closeWindow <--------------"); - } - + onNewViewRequested: { - console.log("-----------> newViewRequested <--------------"); var currentUrl = webview.url; - console.log(currentUrl); urlAppend(currentUrl); request.openIn(webview); } From 4f368cc867c31165fcae279eb3e7d5eb11373cb7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 27 Apr 2017 10:47:07 -0700 Subject: [PATCH 128/292] 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 55704236e3..be3bfcc0e9 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; } @@ -397,7 +401,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 bb9eb986257177f0025975a10e09eee15a30e04b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 21:49:57 +0100 Subject: [PATCH 129/292] prevent mouse events from propagating to far --- interface/resources/qml/controls/TabletWebView.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index e202d89060..dd0c3c8135 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -84,6 +84,13 @@ Item { horizontalCenter: parent.horizontalCenter; } } + + + MouseArea { + anchors.fill: parent + preventStealing: true + propagateComposedEvents: true + } } ListModel { From 3abd9f21e80c35f5185b4a830e03b679a7d6a798 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Mon, 1 May 2017 13:50:19 -0700 Subject: [PATCH 130/292] Debugging without understanding... --- libraries/gl/src/gl/Context.cpp | 4 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 47 ++++++++++++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index f7e08e607b..64d7dd635b 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -307,12 +307,12 @@ void Context::create() { qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); } - if (enableDebugLogger) { + // if (enableDebugLogger) { makeCurrent(); glDebugMessageCallback(debugMessageCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); doneCurrent(); - } + // } } #endif diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 8c249fc652..31fcf5b2d1 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -309,6 +309,30 @@ void GL41VariableAllocationTexture::syncSampler() const { } +void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { + GLuint fbo { 0 }; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = numMips; + // copy pre-existing mips + for (uint16_t mip = populatedMips; mip < mips; ++mip) { + auto mipDimensions = texture.evalMipDimensions(mip); + uint16_t targetMip = mip - destAllocatedMip; + uint16_t sourceMip = mip - srcAllocatedMip; + for (GLenum target : GLTexture::getFaceTargets(texTarget)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); +} + void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { GLuint pbo { 0 }; glGenBuffers(1, &pbo); @@ -347,12 +371,14 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; bufferOffset += sourceMip._size; + gpu::gl::checkGLError(); } (void)CHECK_GL_ERROR(); // Allocate the PBO to accomodate for all the mips to copy glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); (void)CHECK_GL_ERROR(); + gpu::gl::checkGLError(); // Transfer from source texture to pbo for (uint16_t mip = populatedMips; mip < numMips; ++mip) { @@ -364,6 +390,7 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*)(sourceMip._offset + f * sourceMip._faceSize)); } (void)CHECK_GL_ERROR(); + gpu::gl::checkGLError(); } // Now populate the new texture from the pbo @@ -380,8 +407,9 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint uint16_t destLevel = mip - destAllocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexImage2D(faceTargets[f], destLevel, internalFormat, sourceMip._width, sourceMip._height, 0, + glCompressedTexSubImage2D(faceTargets[f], destLevel,0, 0, sourceMip._width, sourceMip._height, internalFormat, sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + gpu::gl::checkGLError(); } } @@ -405,6 +433,8 @@ void GL41VariableAllocationTexture::promote() { // allocate storage for new level allocateStorage(targetAllocatedMip); + uint16_t numMips = _gpuObject.getNumMips(); + if (_texelFormat.isCompressed()) { #if defined(USE_45IMAGECOPY_PROMOTE) withPreservedTexture([&] { @@ -427,7 +457,6 @@ void GL41VariableAllocationTexture::promote() { syncSampler(); }); #else - uint16_t numMips = _gpuObject.getNumMips(); withPreservedTexture([&] { copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); @@ -435,7 +464,8 @@ void GL41VariableAllocationTexture::promote() { #endif } else { withPreservedTexture([&] { - GLuint fbo { 0 }; + copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + /* GLuint fbo { 0 }; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); @@ -456,7 +486,7 @@ void GL41VariableAllocationTexture::promote() { // destroy the transfer framebuffer glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); - + */ syncSampler(); }); } @@ -478,6 +508,8 @@ void GL41VariableAllocationTexture::demote() { allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); + uint16_t numMips = _gpuObject.getNumMips(); + if (_texelFormat.isCompressed()) { #if defined(USE_45IMAGECOPY_DEMOTE) withPreservedTexture([&] { @@ -500,7 +532,6 @@ void GL41VariableAllocationTexture::demote() { syncSampler(); }); #else - uint16_t numMips = _gpuObject.getNumMips(); withPreservedTexture([&] { copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); @@ -508,7 +539,9 @@ void GL41VariableAllocationTexture::demote() { #endif } else { withPreservedTexture([&] { - GLuint fbo { 0 }; + copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + + /* GLuint fbo { 0 }; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); @@ -529,7 +562,7 @@ void GL41VariableAllocationTexture::demote() { // destroy the transfer framebuffer glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); - +*/ syncSampler(); }); } From f81d01f179bd8eb1ff4760893e17f461e5b5c078 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 1 May 2017 14:13:59 -0700 Subject: [PATCH 131/292] 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 64b8237bd66d69ed2cb53d2728050c525f4780e1 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 22:45:13 +0100 Subject: [PATCH 132/292] fix twitter and facebook links --- .../resources/qml/controls/TabletWebView.qml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 42706d47c0..04e784e2ba 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -25,13 +25,9 @@ Item { property bool isDesktop: false property string initialPage: "" property bool startingUp: true - property bool removingPage: false property alias webView: webview property alias profile: webview.profile property bool remove: false - property bool windowClosed: false - property bool loadingStarted: false - property bool loadingFinished: false property var urlList: [] property var forwardList: [] @@ -85,9 +81,12 @@ Item { id: displayUrl color: hifi.colors.baseGray font.pixelSize: 12 + verticalAlignment: Text.AlignLeft anchors { top: nav.bottom horizontalCenter: parent.horizontalCenter; + left: parent.left + leftMargin: 20 } } @@ -116,15 +115,15 @@ Item { forwardList.push(webview.url); webview.goBack(); } else if (web.urlList.length > 0) { - removingPage = true; var url = web.urlList.pop(); loadUrl(url); - } else if (fowardList.length == 1) { - console.log("--------------> going foward <---------------"); + } else if (web.forwardList.length > 0) { + var url = web.forwardList.pop(); + loadUrl(url); + web.forwardList = []; } } - function closeWebEngine() { if (remove) { web.destroy(); From 3d6778e9cdd722ed1bef438179abb0f9014706a3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 15:02:23 -0700 Subject: [PATCH 133/292] 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 a08b55d5f560e21373bca67d08ec938b096b5573 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 1 May 2017 23:08:25 +0100 Subject: [PATCH 134/292] minimize diff --- interface/resources/qml/controls/WebView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index b954fbc11b..04ff731a25 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -99,7 +99,6 @@ Item { // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { var url = loadRequest.url.toString(); - console.log(url); url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; if (urlHandler.canHandleUrl(url)) { if (urlHandler.handleUrl(url)) { From 3ec3005c6dac00ddb261fa700c9f2a9e659d2240 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 May 2017 15:32:17 -0700 Subject: [PATCH 135/292] code review --- libraries/physics/src/ObjectActionSpring.cpp | 4 ++-- libraries/physics/src/ObjectConstraintHinge.cpp | 2 -- libraries/physics/src/ObjectConstraintSlider.cpp | 6 ------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 3cd3926073..8c73f43d42 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -85,10 +85,10 @@ bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm linearTimeScale = _linearTimeScale; angularTimeScale = _angularTimeScale; - if (_otherID != QUuid()) { + if (!_otherID.isNull()) { if (other) { rotation = _desiredRotationalTarget * other->getRotation(); - position = _desiredPositionalTarget + other->getPosition(); + position = other->getRotation() * _desiredPositionalTarget + other->getPosition(); } else { // we should have an "other" but can't find it, so disable the spring. linearTimeScale = FLT_MAX; diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 14a666f09e..dd82de924c 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -74,8 +74,6 @@ void ObjectConstraintHinge::updateHinge() { return; } - // auto bulletAxisInA = glmToBullet(axisInA); - // constraint->setAxis(bulletAxisInA); constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); } diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index 6a59199356..d7d4df78af 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -24,15 +24,9 @@ ObjectConstraintSlider::ObjectConstraintSlider(const QUuid& id, EntityItemPointe _pointInA(glm::vec3(0.0f)), _axisInA(glm::vec3(0.0f)) { - #if WANT_DEBUG - qCDebug(physics) << "ObjectConstraintSlider::ObjectConstraintSlider"; - #endif } ObjectConstraintSlider::~ObjectConstraintSlider() { - #if WANT_DEBUG - qCDebug(physics) << "ObjectConstraintSlider::~ObjectConstraintSlider"; - #endif } QList ObjectConstraintSlider::getRigidBodies() { From 61b738e3cda63103c9b29b99c8c3ddac2831a192 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 1 May 2017 15:35:17 -0700 Subject: [PATCH 136/292] announcment styling --- interface/resources/qml/hifi/Card.qml | 60 ++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 0b34a8f9ac..6b1beaec3a 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -31,7 +31,7 @@ Item { property bool drillDownToPlace: false; property bool showPlace: isConcurrency; - property string messageColor: hifi.colors.blueAccent; + property string messageColor: lozenge.visible ? "white" : hifi.colors.blueAccent; property string timePhrase: pastTime(timestamp); property int onlineUsers: 0; property bool isConcurrency: action === 'concurrency'; @@ -115,7 +115,7 @@ Item { id: lobby; visible: !hasGif || (animation.status !== Image.Ready); width: parent.width - (isConcurrency ? 0 : (2 * smallMargin)); - height: parent.height - messageHeight - (isConcurrency ? 0 : smallMargin); + height: parent.height -(isAnnouncement ? smallMargin : messageHeight) - (isConcurrency ? 0 : smallMargin); source: thumbnail || defaultThumbnail; fillMode: Image.PreserveAspectCrop; anchors { @@ -160,7 +160,24 @@ Item { margins: textPadding; } } + Rectangle { + id: lozenge; + visible: isAnnouncement; + color: hifi.colors.redHighlight; + anchors.fill: infoRow; + radius: lozenge.height / 2.0; + border.width: lozengeHot.containsMouse ? 4 : 0; + border.color: "white"; + } Row { + id: infoRow; + Image { + id: icon; + source: isAnnouncement ? "../../images/Announce-Blast.svg" : "../../images/snap-icon.svg"; + width: 40; + height: 40; + visible: ((action === 'snapshot') || isAnnouncement) && (messageHeight >= 40); + } FiraSansRegular { id: users; visible: isConcurrency || isAnnouncement; @@ -169,34 +186,42 @@ Item { color: messageColor; anchors.verticalCenter: message.verticalCenter; } - Image { - id: icon; - source: "../../images/snap-icon.svg" - width: 40; - height: 40; - visible: (action === 'snapshot') && (messageHeight >= 40); - } RalewayRegular { id: message; + visible: !isAnnouncement; text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (isAnnouncement ? "connections" : (drillDownToPlace ? "snapshots" : ("by " + userName))); size: textSizeSmall; color: messageColor; elide: Text.ElideRight; // requires a width to be specified` width: root.width - textPadding - - (users.visible ? users.width + parent.spacing : 0) - (icon.visible ? icon.width + parent.spacing : 0) + - (users.visible ? users.width + parent.spacing : 0) - (actionIcon.width + (2 * smallMargin)); anchors { bottom: parent.bottom; bottomMargin: parent.spacing; } } + Column { + visible: isAnnouncement; + RalewayRegular { + text: "connections" + " "; // fixme: pluralize + size: textSizeSmall; + color: "white"; // fixme not needed? get rid of complication in messageColor? + } + RalewayRegular { + text: "are here now"; // fixme pluralize + size: textSizeSmall * 0.7; + color: "white"; //' fixme not needed? get rid of complication in messageColor? + } + } spacing: textPadding; height: messageHeight; anchors { bottom: parent.bottom; left: parent.left; leftMargin: textPadding; + bottomMargin: isAnnouncement ? textPadding : 0; } } // These two can be supplied to provide hover behavior. @@ -214,6 +239,7 @@ Item { } StateImage { id: actionIcon; + visible: !isAnnouncement; imageURL: "../../images/info-icon-2-state.svg"; size: 30; buttonState: messageArea.containsMouse ? 1 : 0; @@ -223,13 +249,25 @@ Item { margins: smallMargin; } } + function go() { + goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); + } MouseArea { id: messageArea; + visible: !isAnnouncement; width: parent.width; height: messageHeight; anchors.top: lobby.bottom; acceptedButtons: Qt.LeftButton; - onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId)); + onClicked: go(); + hoverEnabled: true; + } + MouseArea { + id: lozengeHot; + visible: lozenge.visible; + anchors.fill: lozenge; + acceptedButtons: Qt.LeftButton; + onClicked: go(); hoverEnabled: true; } } From 545fd1355ff8cd043c44725b227995cfef576f22 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 1 May 2017 15:45:26 -0700 Subject: [PATCH 137/292] cleanup --- interface/resources/qml/hifi/Card.qml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 6b1beaec3a..9617b41150 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -31,7 +31,7 @@ Item { property bool drillDownToPlace: false; property bool showPlace: isConcurrency; - property string messageColor: lozenge.visible ? "white" : hifi.colors.blueAccent; + property string messageColor: isAnnouncement ? "white" : hifi.colors.blueAccent; property string timePhrase: pastTime(timestamp); property int onlineUsers: 0; property bool isConcurrency: action === 'concurrency'; @@ -71,6 +71,10 @@ Item { property bool hasGif: imageUrl.indexOf('.gif') === (imageUrl.length - 4); + function pluralize(count, singular, optionalPlural) { + return (count === 1) ? singular : (optionalPlural || (singular + "s")); + } + DropShadow { visible: isStacked; anchors.fill: shadow1; @@ -189,7 +193,7 @@ Item { RalewayRegular { id: message; visible: !isAnnouncement; - text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (isAnnouncement ? "connections" : (drillDownToPlace ? "snapshots" : ("by " + userName))); + text: isConcurrency ? pluralize(onlineUsers, "person", "people") : (drillDownToPlace ? "snapshots" : ("by " + userName)); size: textSizeSmall; color: messageColor; elide: Text.ElideRight; // requires a width to be specified` @@ -205,14 +209,14 @@ Item { Column { visible: isAnnouncement; RalewayRegular { - text: "connections" + " "; // fixme: pluralize + text: pluralize(onlineUsers, "connection") + " "; // hack padding size: textSizeSmall; - color: "white"; // fixme not needed? get rid of complication in messageColor? + color: messageColor; } RalewayRegular { - text: "are here now"; // fixme pluralize + text: pluralize(onlineUsers, "is here now", "are here now"); size: textSizeSmall * 0.7; - color: "white"; //' fixme not needed? get rid of complication in messageColor? + color: messageColor; } } spacing: textPadding; From ca5318f12176a6899685ed2cb2c8259196d6a63a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 1 May 2017 15:57:22 -0700 Subject: [PATCH 138/292] remove debugging code --- interface/resources/qml/hifi/tablet/TabletAddressDialog.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index b7c0d24b24..7159b078ee 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -280,7 +280,6 @@ StackView { cardHeight: 163 + (2 * 4); metaverseServerUrl: addressBarDialog.metaverseServerUrl; labelText: 'HAPPENING NOW'; - //actions: 'concurrency,snapshot'; // uncomment this line instead of next to produce fake announcement data for testing. actions: 'announcement'; filter: addressLine.text; goFunction: goCard; From dd739d73a01d8d4a178c4a7659a04914262d9e28 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 2 May 2017 12:01:55 +1200 Subject: [PATCH 139/292] 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 140/292] 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 141/292] 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 8b62fad85eaadd7b1a96b859d03157e447ddc9fd Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 1 May 2017 18:19:07 -0700 Subject: [PATCH 142/292] TRying to test with sysmem and still doesn;t work --- .../src/gpu/gl41/GL41BackendTexture.cpp | 147 ++++++------------ 1 file changed, 47 insertions(+), 100 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 31fcf5b2d1..e52c2398f9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -333,10 +333,23 @@ void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GL glDeleteFramebuffers(1, &fbo); } -void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { - GLuint pbo { 0 }; - glGenBuffers(1, &pbo); - glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); +void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { +#ifdef USE_GL45_COPYIMAGE + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto mipDimensions = texture.evalMipDimensions(mip); + uint16_t targetMip = mip - destAllocatedMip; + uint16_t sourceMip = mip - srcAllocatedMip; + auto faces = GLTexture::getFaceCount(texTarget); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + srcId, texTarget, sourceMip, 0, 0, face, + destId, texTarget, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } +#else struct MipDesc { GLint _faceSize; GLint _size; @@ -346,7 +359,9 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint }; std::vector sourceMips(numMips); - glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + std::vector bytes; + + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); glBindTexture(texTarget, srcId); const auto& faceTargets = GLTexture::getFaceTargets(texTarget); GLint internalFormat { 0 }; @@ -364,9 +379,12 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint } // Collect the size of the first face, and then compute the total size offset needed for this mip level + auto mipDimensions = texture.evalMipDimensions(sourceLevel); + sourceMip._width = mipDimensions.x; + sourceMip._height = mipDimensions.y; - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); - glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); + // glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); + // glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; @@ -376,9 +394,21 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint (void)CHECK_GL_ERROR(); // Allocate the PBO to accomodate for all the mips to copy +#ifdef USE_PBO + GLvoid* pboPointer = 0; + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); (void)CHECK_GL_ERROR(); gpu::gl::checkGLError(); +#else + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + bytes.resize(bufferOffset); + auto pboPointer = bytes.data(); +#endif + // Transfer from source texture to pbo for (uint16_t mip = populatedMips; mip < numMips; ++mip) { @@ -387,7 +417,7 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint uint16_t sourceLevel = mip - srcAllocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); } (void)CHECK_GL_ERROR(); gpu::gl::checkGLError(); @@ -395,10 +425,13 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint // Now populate the new texture from the pbo glBindTexture(texTarget, 0); +#ifdef USE_PBO glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); +#endif glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); + glBindTexture(texTarget, destId); // Transfer from pbo to new texture for (uint16_t mip = populatedMips; mip < numMips; ++mip) { @@ -408,13 +441,16 @@ void copyCompressedTexGPUMem(GLenum texTarget, GLuint srcId, GLuint destId, uint for (GLint f = 0; f < faceTargets.size(); f++) { glCompressedTexSubImage2D(faceTargets[f], destLevel,0, 0, sourceMip._width, sourceMip._height, internalFormat, - sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + sourceMip._faceSize, (void*)(pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); gpu::gl::checkGLError(); } } +#ifdef USE_PBO glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); +#endif +#endif } void GL41VariableAllocationTexture::promote() { @@ -436,57 +472,13 @@ void GL41VariableAllocationTexture::promote() { uint16_t numMips = _gpuObject.getNumMips(); if (_texelFormat.isCompressed()) { -#if defined(USE_45IMAGECOPY_PROMOTE) withPreservedTexture([&] { - uint16_t numMips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } + copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); }); -#else - withPreservedTexture([&] { - copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); -#endif } else { withPreservedTexture([&] { copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - /* GLuint fbo { 0 }; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - - uint16_t mips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); - (void)CHECK_GL_ERROR(); - } - } - - // destroy the transfer framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - */ syncSampler(); }); } @@ -511,58 +503,13 @@ void GL41VariableAllocationTexture::demote() { uint16_t numMips = _gpuObject.getNumMips(); if (_texelFormat.isCompressed()) { -#if defined(USE_45IMAGECOPY_DEMOTE) withPreservedTexture([&] { - uint16_t numMips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } + copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); syncSampler(); }); -#else - withPreservedTexture([&] { - copyCompressedTexGPUMem(_target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); -#endif } else { withPreservedTexture([&] { copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - - /* GLuint fbo { 0 }; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - - uint16_t numMips = _gpuObject.getNumMips(); - // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < numMips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - for (GLenum target : getFaceTargets(_target)) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); - (void)CHECK_GL_ERROR(); - glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); - (void)CHECK_GL_ERROR(); - } - } - - // destroy the transfer framebuffer - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); -*/ syncSampler(); }); } From 54ace01cd6f962f1b43faed541e2f24510cf470d Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Tue, 2 May 2017 00:32:32 -0700 Subject: [PATCH 143/292] Factorizing the code on both 45 and 41 backend --- .../src/gpu/gl41/GL41BackendTexture.cpp | 46 +++++++------- .../gpu/gl45/GL45BackendVariableTexture.cpp | 63 ++++++++++--------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index e52c2398f9..114f61dfc9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -374,17 +374,18 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t sourceLevel = mip - srcAllocatedMip; // Grab internal format once - if (internalFormat != 0) { + if (internalFormat == 0) { glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); } // Collect the size of the first face, and then compute the total size offset needed for this mip level - auto mipDimensions = texture.evalMipDimensions(sourceLevel); + auto mipDimensions = texture.evalMipDimensions(mip); sourceMip._width = mipDimensions.x; sourceMip._height = mipDimensions.y; - - // glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); - // glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); +#ifdef DEBUG_COPY + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); +#endif glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; sourceMip._offset = bufferOffset; @@ -394,21 +395,12 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui (void)CHECK_GL_ERROR(); // Allocate the PBO to accomodate for all the mips to copy -#ifdef USE_PBO - GLvoid* pboPointer = 0; + GLint pboPointer = 0; GLuint pbo { 0 }; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); (void)CHECK_GL_ERROR(); - gpu::gl::checkGLError(); -#else - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - bytes.resize(bufferOffset); - auto pboPointer = bytes.data(); -#endif - // Transfer from source texture to pbo for (uint16_t mip = populatedMips; mip < numMips; ++mip) { @@ -417,18 +409,15 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t sourceLevel = mip - srcAllocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); } (void)CHECK_GL_ERROR(); - gpu::gl::checkGLError(); } // Now populate the new texture from the pbo glBindTexture(texTarget, 0); -#ifdef USE_PBO glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); -#endif glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); glBindTexture(texTarget, destId); @@ -440,17 +429,21 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t destLevel = mip - destAllocatedMip; for (GLint f = 0; f < faceTargets.size(); f++) { - glCompressedTexSubImage2D(faceTargets[f], destLevel,0, 0, sourceMip._width, sourceMip._height, internalFormat, +#ifdef DEBUG_COPY + GLint destWidth, destHeight, destSize; + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); +#endif + glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, sourceMip._faceSize, (void*)(pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); gpu::gl::checkGLError(); } } -#ifdef USE_PBO glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); #endif -#endif } void GL41VariableAllocationTexture::promote() { @@ -462,15 +455,16 @@ void GL41VariableAllocationTexture::promote() { GLuint oldId = _id; auto oldSize = _size; + uint16_t oldAllocatedMip = _allocatedMip; + // create new texture const_cast(_id) = allocate(_gpuObject); - uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(targetAllocatedMip); + // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - if (_texelFormat.isCompressed()) { withPreservedTexture([&] { copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); @@ -495,13 +489,15 @@ void GL41VariableAllocationTexture::demote() { Q_ASSERT(_allocatedMip < _maxAllocatedMip); auto oldId = _id; auto oldSize = _size; + + // allocate new texture const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); + // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - if (_texelFormat.isCompressed()) { withPreservedTexture([&] { copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92d820e5f0..3381fa8cce 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -97,6 +97,24 @@ void GL45ResourceTexture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); } + +void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto mipDimensions = texture.evalMipDimensions(mip); + uint16_t targetMip = mip - destAllocatedMip; + uint16_t sourceMip = mip - srcAllocatedMip; + auto faces = GLTexture::getFaceCount(texTarget); + for (uint8_t face = 0; face < faces; ++face) { + glCopyImageSubData( + srcId, texTarget, sourceMip, 0, 0, face, + destId, texTarget, targetMip, 0, 0, face, + mipDimensions.x, mipDimensions.y, 1 + ); + (void)CHECK_GL_ERROR(); + } + } +} + void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); @@ -106,27 +124,18 @@ void GL45ResourceTexture::promote() { GLuint oldId = _id; auto oldSize = _size; + uint16_t oldAllocatedMip = _allocatedMip; + // create new texture const_cast(_id) = allocate(_gpuObject); - uint16_t oldAllocatedMip = _allocatedMip; + // allocate storage for new level allocateStorage(targetAllocatedMip); - uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = mip - oldAllocatedMip; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } + uint16_t numMips = _gpuObject.getNumMips(); + copyTexGPUMem(_gpuObject, _target, _id, oldId, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + // destroy the old texture glDeleteTextures(1, &oldId); // update the memory usage @@ -140,25 +149,17 @@ void GL45ResourceTexture::demote() { Q_ASSERT(_allocatedMip < _maxAllocatedMip); auto oldId = _id; auto oldSize = _size; + + // allocate new texture const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); - uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips - for (uint16_t mip = _populatedMip; mip < mips; ++mip) { - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - uint16_t targetMip = mip - _allocatedMip; - uint16_t sourceMip = targetMip + 1; - auto faces = getFaceCount(_target); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - oldId, _target, sourceMip, 0, 0, face, - _id, _target, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } + uint16_t numMips = _gpuObject.getNumMips(); + copyTexGPUMem(_gpuObject, _target, _id, oldId, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + // destroy the old texture glDeleteTextures(1, &oldId); // update the memory usage From ac86c134779c5ee356efb1e56aa7e9753cfd0ece Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 12:18:06 -0700 Subject: [PATCH 144/292] Fix sharing; disable HiFi buttons independently --- scripts/system/html/js/SnapshotReview.js | 32 ++-- scripts/system/snapshot.js | 199 +++++++++++++---------- 2 files changed, 128 insertions(+), 103 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 53f4d17930..b1a6bb2303 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -52,7 +52,7 @@ function clearImages() { imageCount = 0; idCounter = 0; } -function addImage(image_data, isGifLoading, isShowingPreviousImages, canSharePreviousImages, hifiShareButtonsDisabled) { +function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) { if (!image_data.localPath) { return; } @@ -80,22 +80,22 @@ function addImage(image_data, isGifLoading, isShowingPreviousImages, canSharePre if (isGif) { imageContainer.innerHTML += 'GIF'; } - if (!isGifLoading && !isShowingPreviousImages) { + if (!isGifLoading && !isShowingPreviousImages && canShare) { shareForUrl(id); - } else if (isShowingPreviousImages && canSharePreviousImages) { - appendShareBar(id, image_data.story_id, isGif, hifiShareButtonsDisabled) + } else if (isShowingPreviousImages && canShare) { + appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled) } } -function appendShareBar(divID, story_id, isGif, hifiShareButtonsDisabled) { +function appendShareBar(divID, story_id, isGif, blastButtonDisabled, hifiButtonDisabled) { var story_url = "https://highfidelity.com/user_stories/" + story_id; var parentDiv = document.getElementById(divID); parentDiv.setAttribute('data-story-id', story_id); - document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, hifiShareButtonsDisabled)); + document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, blastButtonDisabled, hifiButtonDisabled)); if (divID === "p0") { selectImageToShare(divID, true); } } -function createShareBar(parentID, isGif, shareURL, hifiShareButtonsDisabled) { +function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButtonDisabled) { var shareBar = document.createElement("div"); shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; @@ -109,8 +109,8 @@ function createShareBar(parentID, isGif, shareURL, hifiShareButtonsDisabled) { var twitterButtonID = parentID + "twitterButton"; shareBar.innerHTML += '' + '' + @@ -173,7 +173,6 @@ function blastToConnections(selectedID, isGif) { selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID document.getElementById(selectedID + "blastToConnectionsButton").disabled = true; - document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true; EventBridge.emitWebEvent(JSON.stringify({ type: "snapshot", @@ -185,7 +184,6 @@ function blastToConnections(selectedID, isGif) { function shareWithEveryone(selectedID, isGif) { selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID - document.getElementById(selectedID + "blastToConnectionsButton").disabled = true; document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true; EventBridge.emitWebEvent(JSON.stringify({ @@ -260,7 +258,7 @@ window.onload = function () { var messageOptions = message.options; imageCount = message.image_data.length; message.image_data.forEach(function (element, idx, array) { - addImage(element, true, true, message.canShare, message.image_data[idx].buttonDisabled); + addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled); }); break; case 'addImages': @@ -274,7 +272,7 @@ window.onload = function () { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon message.image_data.unshift({ localPath: messageOptions.loadingGifPath }); message.image_data.forEach(function (element, idx, array) { - addImage(element, idx === 0, false, false); + addImage(element, idx === 0, messageOptions.canShare, false); }); } else { var gifPath = message.image_data[0].localPath; @@ -282,12 +280,14 @@ window.onload = function () { p0img.src = gifPath; paths[0] = gifPath; - shareForUrl("p0"); + if (messageOptions.canShare) { + shareForUrl("p0"); + } } } else { imageCount = message.image_data.length; message.image_data.forEach(function (element, idx, array) { - addImage(element, false, false, false); + addImage(element, false, messageOptions.canShare, false); }); } break; @@ -329,6 +329,6 @@ function testInBrowser(isTestingSetupInstructions) { } else { imageCount = 1; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-04-26_10-26-53.gif' }, false, true, true, false); + addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-04-26_10-26-53.gif' }, false, true, true, false, false); } } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 1cc24b8265..d8635dcb03 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -117,13 +117,15 @@ function onMessage(message) { setting: Settings.getValue("alsoTakeAnimatedSnapshot", true) })); if (Snapshot.getSnapshotsLocation() !== "") { - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "showPreviousImages", - options: snapshotOptions, - image_data: imageData, - canShare: !isDomainOpen(Settings.getValue("previousSnapshotDomainID")) - })); + isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "showPreviousImages", + options: snapshotOptions, + image_data: imageData, + canShare: canShare + })); + }); } else { tablet.emitScriptEvent(JSON.stringify({ type: "snapshot", @@ -131,10 +133,12 @@ function onMessage(message) { })); Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapStoryID", ""); - Settings.setValue("previousStillSnapSharingDisabled", false); + Settings.setValue("previousStillSnapBlastingDisabled", false); + Settings.setValue("previousStillSnapHifiSharingDisabled", false); Settings.setValue("previousAnimatedSnapPath", ""); Settings.setValue("previousAnimatedSnapStoryID", ""); - Settings.setValue("previousAnimatedSnapSharingDisabled", false); + Settings.setValue("previousAnimatedSnapBlastingDisabled", false); + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false); } break; case 'chooseSnapshotLocation': @@ -180,9 +184,9 @@ function onMessage(message) { isLoggedIn = Account.isLoggedIn(); storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); if (message.isGif) { - Settings.setValue("previousAnimatedSnapSharingDisabled", true); + Settings.setValue("previousAnimatedSnapBlastingDisabled", true); } else { - Settings.setValue("previousStillSnapSharingDisabled", true); + Settings.setValue("previousStillSnapBlastingDisabled", true); } if (isLoggedIn) { @@ -220,9 +224,9 @@ function onMessage(message) { if (error || (response.status !== 'success')) { print("ERROR uploading announcement story: ", error || response.status); if (message.isGif) { - Settings.setValue("previousAnimatedSnapSharingDisabled", false); + Settings.setValue("previousAnimatedSnapBlastingDisabled", false); } else { - Settings.setValue("previousStillSnapSharingDisabled", false); + Settings.setValue("previousStillSnapBlastingDisabled", false); } return; } else { @@ -240,9 +244,9 @@ function onMessage(message) { isLoggedIn = Account.isLoggedIn(); storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); if (message.isGif) { - Settings.setValue("previousAnimatedSnapSharingDisabled", true); + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); } else { - Settings.setValue("previousStillSnapSharingDisabled", true); + Settings.setValue("previousStillSnapHifiSharingDisabled", true); } if (isLoggedIn) { @@ -264,9 +268,9 @@ function onMessage(message) { if (error || (response.status !== 'success')) { print("ERROR changing audience: ", error || response.status); if (message.isGif) { - Settings.setValue("previousAnimatedSnapSharingDisabled", false); + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false); } else { - Settings.setValue("previousStillSnapSharingDisabled", false); + Settings.setValue("previousStillSnapHifiSharingDisabled", false); } return; } else { @@ -301,10 +305,12 @@ function onButtonClicked() { shouldActivateButton = true; var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); - var previousStillSnapSharingDisabled = Settings.getValue("previousStillSnapSharingDisabled"); + var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled"); + var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled"); var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); - var previousAnimatedSnapSharingDisabled = Settings.getValue("previousAnimatedSnapSharingDisabled"); + var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); + var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); snapshotOptions = { containsGif: previousAnimatedSnapPath !== "", processingGif: false, @@ -312,10 +318,20 @@ function onButtonClicked() { } imageData = []; if (previousAnimatedSnapPath !== "") { - imageData.push({ localPath: previousAnimatedSnapPath, story_id: previousAnimatedSnapStoryID, buttonDisabled: previousAnimatedSnapSharingDisabled }); + imageData.push({ + localPath: previousAnimatedSnapPath, + story_id: previousAnimatedSnapStoryID, + blastButtonDisabled: previousAnimatedSnapBlastingDisabled, + hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled + }); } if (previousStillSnapPath !== "") { - imageData.push({ localPath: previousStillSnapPath, story_id: previousStillSnapStoryID, buttonDisabled: previousStillSnapSharingDisabled }); + imageData.push({ + localPath: previousStillSnapPath, + story_id: previousStillSnapStoryID, + blastButtonDisabled: previousStillSnapBlastingDisabled, + hifiButtonDisabled: previousStillSnapHifiSharingDisabled + }); } tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); tablet.webEventReceived.connect(onMessage); @@ -355,10 +371,12 @@ function takeSnapshot() { })); Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapStoryID", ""); - Settings.setValue("previousStillSnapSharingDisabled", false); + Settings.setValue("previousStillSnapBlastingDisabled", false); + Settings.setValue("previousStillSnapHifiSharingDisabled", false); Settings.setValue("previousAnimatedSnapPath", ""); Settings.setValue("previousAnimatedSnapStoryID", ""); - Settings.setValue("previousAnimatedSnapSharingDisabled", false); + Settings.setValue("previousAnimatedSnapBlastingDisabled", false); + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false); // Raising the desktop for the share dialog at end will interact badly with clearOverlayWhenMoving. // Turn it off now, before we start futzing with things (and possibly moving). @@ -403,32 +421,34 @@ function takeSnapshot() { }, FINISH_SOUND_DELAY); } -function isDomainOpen(id) { +function isDomainOpen(id, callback) { print("Checking open status of domain with ID:", id); - if (!id) { - return false; + var status = false; + if (id) { + var options = [ + 'now=' + new Date().toISOString(), + 'include_actions=concurrency', + 'domain_id=' + id.slice(1, -1), + 'restriction=open,hifi' // If we're sharing, we're logged in + // If we're here, protocol matches, and it is online + ]; + var url = METAVERSE_BASE + "/api/v1/user_stories?" + options.join('&'); + + request({ + uri: url, + method: 'GET' + }, function (error, response) { + if (error || (response.status !== 'success')) { + print("ERROR getting open status of domain: ", error || response.status); + } else { + status = response.total_entries ? true : false; + } + print("Domain open status:", status); + callback(status); + }); + } else { + callback(status); } - - var options = [ - 'now=' + new Date().toISOString(), - 'include_actions=concurrency', - 'domain_id=' + id.slice(1, -1), - 'restriction=open,hifi' // If we're sharing, we're logged in - // If we're here, protocol matches, and it is online - ]; - var url = METAVERSE_BASE + "/api/v1/user_stories?" + options.join('&'); - - return request({ - uri: url, - method: 'GET' - }, function (error, response) { - if (error || (response.status !== 'success')) { - print("ERROR getting open status of domain: ", error || response.status); - return false; - } else { - return response.total_entries; - } - }); } function stillSnapshotTaken(pathStillSnapshot, notify) { @@ -448,25 +468,27 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { // during which time the user may have moved. So stash that info in the dialog so that // it records the correct href. (We can also stash in .jpegs, but not .gifs.) // last element in data array tells dialog whether we can share or not - snapshotOptions = { - containsGif: false, - processingGif: false, - canShare: !isDomainOpen(domainId) - }; - imageData = [{ localPath: pathStillSnapshot, href: href }]; Settings.setValue("previousStillSnapPath", pathStillSnapshot); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "addImages", - options: snapshotOptions, - image_data: imageData - })); - if (clearOverlayWhenMoving) { MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog } HMD.openTablet(); + + isDomainOpen(domainId, function (canShare) { + snapshotOptions = { + containsGif: false, + processingGif: false, + canShare: canShare + }; + imageData = [{ localPath: pathStillSnapshot, href: href }]; + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "addImages", + options: snapshotOptions, + image_data: imageData + })); + }); } function processingGifStarted(pathStillSnapshot) { @@ -478,27 +500,28 @@ function processingGifStarted(pathStillSnapshot) { if (resetOverlays) { Menu.setIsOptionChecked("Overlays", true); } - - snapshotOptions = { - containsGif: true, - processingGif: true, - loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'), - canShare: !isDomainOpen(domainId) - }; - imageData = [{ localPath: pathStillSnapshot, href: href }]; Settings.setValue("previousStillSnapPath", pathStillSnapshot); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "addImages", - options: snapshotOptions, - image_data: imageData - })); - if (clearOverlayWhenMoving) { MyAvatar.setClearOverlayWhenMoving(true); // not until after the share dialog } HMD.openTablet(); + + isDomainOpen(domainId, function (canShare) { + snapshotOptions = { + containsGif: true, + processingGif: true, + loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'), + canShare: canShare + }; + imageData = [{ localPath: pathStillSnapshot, href: href }]; + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "addImages", + options: snapshotOptions, + image_data: imageData + })); + }); } function processingGifCompleted(pathAnimatedSnapshot) { @@ -508,20 +531,22 @@ function processingGifCompleted(pathAnimatedSnapshot) { buttonConnected = true; } - snapshotOptions = { - containsGif: true, - processingGif: false, - canShare: !isDomainOpen(domainId) - } - imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "addImages", - options: snapshotOptions, - image_data: imageData - })); + isDomainOpen(domainId, function (canShare) { + snapshotOptions = { + containsGif: true, + processingGif: false, + canShare: canShare + }; + imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "addImages", + options: snapshotOptions, + image_data: imageData + })); + }); } function maybeDeleteSnapshotStories() { storyIDsToMaybeDelete.forEach(function (element, idx, array) { From 5b168301bcac0fc6f046806f892e72f3c6c625c4 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 15:28:50 -0700 Subject: [PATCH 145/292] Update some typefaces; Add margins to pics --- scripts/system/html/css/SnapshotReview.css | 33 ++++++++++------------ scripts/system/html/css/hifi-style.css | 9 +++--- scripts/system/html/js/SnapshotReview.js | 9 +++--- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 12b91d372b..fc0f9b461b 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -80,6 +80,8 @@ input[type=button].naked:active { #snapshot-images { width: 100%; + display: flex; + justify-content: center; } #snapshot-images img { @@ -119,13 +121,13 @@ input[type=button].naked:active { .shareButtons { display: flex; align-items: center; - margin-left: 30px; + margin-left: 15px; height: 100%; - width: 80%; + width: 75%; } .blastToConnections { text-align: left; - margin-right: 25px; + margin-right: 20px; height: 29px; } .shareWithEveryone { @@ -158,10 +160,11 @@ input[type=button].naked:active { font-family: Raleway-SemiBold; font-size: 14px; color: white; - text-shadow: 2px 2px 3px #000000; height: 100%; margin-right: 10px; - width: 20%; +} +.showShareButtonsButtonDiv > label { + text-shadow: 2px 2px 3px #000000; } .showShareButton { width: 40px; @@ -193,23 +196,17 @@ input[type=button].naked:active { background-color: white; } .showShareButtonDots { - display: flex; - width: 32px; + display: block; + width: 40px; height: 40px; + font-family: HiFi-Glyphs; + font-size: 60px; position: absolute; - top: 5px; - right: 14px; + right: 20px; + bottom: 15px; + color: #00b4ef; pointer-events: none; } -.showShareButtonDots > span { - width: 10px; - height: 10px; - margin: auto; - background-color: #0093C5; - border-radius: 50%; - border-width: 0; - display: inline; -} /* // END styling of share overlay */ diff --git a/scripts/system/html/css/hifi-style.css b/scripts/system/html/css/hifi-style.css index f1ace02eb0..ac34cee09f 100644 --- a/scripts/system/html/css/hifi-style.css +++ b/scripts/system/html/css/hifi-style.css @@ -101,9 +101,11 @@ input[type=radio] { opacity: 0; } input[type=radio] + label{ - display: inline-block; - margin-left: -2em; - line-height: 2em; + display: inline-block; + margin-left: -2em; + line-height: 2em; + font-family: Raleway-SemiBold; + font-size: 14px; } input[type=radio] + label > span{ display: inline-block; @@ -157,7 +159,6 @@ input[type=radio]:active + label > span > span{ border-width: 0px; background-image: linear-gradient(#00B4EF, #1080B8); min-height: 30px; - } .blueButton:hover { background-image: linear-gradient(#00B4EF, #00B4EF); diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index b1a6bb2303..475a1e71ff 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -60,8 +60,9 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b // imageContainer setup var imageContainer = document.createElement("DIV"); imageContainer.id = id; - imageContainer.style.width = "100%"; - imageContainer.style.height = "251px"; + imageContainer.style.width = "95%"; + imageContainer.style.height = "240px"; + imageContainer.style.margin = "5px 0"; imageContainer.style.display = "flex"; imageContainer.style.justifyContent = "center"; imageContainer.style.alignItems = "center"; @@ -118,7 +119,7 @@ function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButt '' + '' + '
    ' + - '' + + '' + '
    ' + ''; @@ -231,7 +232,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(true); + testInBrowser(false); 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) { From d295b5a69d6067ca516945c7dd21b53596c31776 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 15:31:22 -0700 Subject: [PATCH 146/292] ShareBarHeight-= 5px; Remove test call --- scripts/system/html/css/SnapshotReview.css | 4 ++-- scripts/system/html/js/SnapshotReview.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index fc0f9b461b..f8fa21a1f7 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -110,7 +110,7 @@ input[type=button].naked:active { justify-content: space-between; flex-direction: row; align-items: center; - height: 50px; + height: 45px; line-height: 60px; width: calc(100% - 8px); position: absolute; @@ -203,7 +203,7 @@ input[type=button].naked:active { font-size: 60px; position: absolute; right: 20px; - bottom: 15px; + bottom: 12px; color: #00b4ef; pointer-events: none; } diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 475a1e71ff..ad7578b4c0 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -232,7 +232,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(false); + //testInBrowser(false); 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) { From 0ead9e5a2468c0877dd5a8eb670ca7ef619c78d0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 16:17:38 -0700 Subject: [PATCH 147/292] Visual fixes; Select only 1 image at a time --- scripts/system/html/css/SnapshotReview.css | 1 + scripts/system/html/js/SnapshotReview.js | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index f8fa21a1f7..ccc9386d13 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -82,6 +82,7 @@ input[type=button].naked:active { width: 100%; display: flex; justify-content: center; + flex-direction: column; } #snapshot-images img { diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index ad7578b4c0..b7380e83c9 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -62,7 +62,7 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b imageContainer.id = id; imageContainer.style.width = "95%"; imageContainer.style.height = "240px"; - imageContainer.style.margin = "5px 0"; + imageContainer.style.margin = "5px auto"; imageContainer.style.display = "flex"; imageContainer.style.justifyContent = "center"; imageContainer.style.alignItems = "center"; @@ -150,6 +150,15 @@ function selectImageToShare(selectedID, isSelected) { shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; shareButtonsDiv.style.visibility = "visible"; + + var containers = document.getElementsByClassName("shareControls"); + var parentID; + for (var i = 0; i < containers.length; ++i) { + parentID = containers[i].id.slice(0, 2); + if (parentID !== selectedID) { + selectImageToShare(parentID, false); + } + } } else { showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true) }; showShareButtonsButton.classList.remove("active"); @@ -232,7 +241,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(false); + testInBrowser(false); 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) { @@ -330,6 +339,7 @@ function testInBrowser(isTestingSetupInstructions) { } else { imageCount = 1; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-04-26_10-26-53.gif' }, false, true, true, false, false); + addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.gif' }, false, true, true, false, false); + addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.jpg' }, false, true, true, false, false); } } From a754c4d6cc2639e36206a52f956ab262568fe991 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 16:40:47 -0700 Subject: [PATCH 148/292] Still image on top --- scripts/system/html/js/SnapshotReview.js | 18 +++++++++--------- scripts/system/snapshot.js | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index b7380e83c9..ed290e9756 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -83,7 +83,7 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b } if (!isGifLoading && !isShowingPreviousImages && canShare) { shareForUrl(id); - } else if (isShowingPreviousImages && canShare) { + } else if (isShowingPreviousImages && canShare && image_data.story_id) { appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled) } } @@ -241,7 +241,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(false); + //testInBrowser(false); 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) { @@ -280,18 +280,18 @@ window.onload = function () { if (messageOptions.containsGif) { if (messageOptions.processingGif) { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon - message.image_data.unshift({ localPath: messageOptions.loadingGifPath }); + message.image_data.push({ localPath: messageOptions.loadingGifPath }); message.image_data.forEach(function (element, idx, array) { - addImage(element, idx === 0, messageOptions.canShare, false); + addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); }); } else { var gifPath = message.image_data[0].localPath; - var p0img = document.getElementById('p0img'); - p0img.src = gifPath; + var p1img = document.getElementById('p1img'); + p1img.src = gifPath; - paths[0] = gifPath; + paths[1] = gifPath; if (messageOptions.canShare) { - shareForUrl("p0"); + shareForUrl("p1"); } } } else { @@ -306,7 +306,7 @@ window.onload = function () { break; case 'snapshotUploadComplete': var isGif = message.image_url.split('.').pop().toLowerCase() === "gif"; - appendShareBar(isGif || imageCount === 1 ? "p0" : "p1", message.story_id, isGif); + appendShareBar(isGif ? "p1" : "p0", message.story_id, isGif); break; default: console.log("Unknown message action received in SnapshotReview.js."); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index d8635dcb03..b2d39484b0 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -317,14 +317,6 @@ function onButtonClicked() { shouldUpload: false } imageData = []; - if (previousAnimatedSnapPath !== "") { - imageData.push({ - localPath: previousAnimatedSnapPath, - story_id: previousAnimatedSnapStoryID, - blastButtonDisabled: previousAnimatedSnapBlastingDisabled, - hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled - }); - } if (previousStillSnapPath !== "") { imageData.push({ localPath: previousStillSnapPath, @@ -333,6 +325,14 @@ function onButtonClicked() { hifiButtonDisabled: previousStillSnapHifiSharingDisabled }); } + if (previousAnimatedSnapPath !== "") { + imageData.push({ + localPath: previousAnimatedSnapPath, + story_id: previousAnimatedSnapStoryID, + blastButtonDisabled: previousAnimatedSnapBlastingDisabled, + hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled + }); + } tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); tablet.webEventReceived.connect(onMessage); HMD.openTablet(); From f453df36cf22974202a6831a20e136e7b516c6d2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 1 May 2017 16:53:44 -0700 Subject: [PATCH 149/292] Prevent cursor appearance in snaps --- scripts/system/snapshot.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index b2d39484b0..94630d3a72 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -395,6 +395,7 @@ function takeSnapshot() { resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked. reticleVisible = Reticle.visible; Reticle.visible = false; + Reticle.allowMouseCapture = false; var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true); if (includeAnimated) { @@ -454,6 +455,7 @@ function isDomainOpen(id, callback) { function stillSnapshotTaken(pathStillSnapshot, notify) { // show hud Reticle.visible = reticleVisible; + Reticle.allowMouseCapture = true; // show overlays if they were on if (resetOverlays) { Menu.setIsOptionChecked("Overlays", true); @@ -496,6 +498,7 @@ function processingGifStarted(pathStillSnapshot) { Window.processingGifCompleted.connect(processingGifCompleted); // show hud Reticle.visible = reticleVisible; + Reticle.allowMouseCapture = true; // show overlays if they were on if (resetOverlays) { Menu.setIsOptionChecked("Overlays", true); From c9e0679b70b6d07fd456f33b575cf1625b0e2ec7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 10:55:57 -0700 Subject: [PATCH 150/292] Subscribe to changes to snapshotsLocation setter for better UX --- interface/src/ui/PreferencesDialog.cpp | 2 +- interface/src/ui/Snapshot.h | 3 +++ scripts/system/snapshot.js | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index bf4be7fd17..e2e22fe366 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -111,7 +111,7 @@ void setupPreferences() { static const QString SNAPSHOTS { "Snapshots" }; { auto getter = []()->QString { return Snapshot::snapshotsLocation.get(); }; - auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); }; + auto setter = [](const QString& value) { Snapshot::snapshotsLocation.set(value); emit DependencyManager::get()->snapshotLocationSet(value); }; auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter); preferences->addPreference(preference); } diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 93ffbbc7bb..1246e1d004 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -44,6 +44,9 @@ public: static Setting::Handle snapshotsLocation; static void uploadSnapshot(const QString& filename, const QUrl& href = QUrl("")); +signals: + void snapshotLocationSet(const QString& value); + public slots: Q_INVOKABLE QString getSnapshotsLocation(); Q_INVOKABLE void setSnapshotsLocation(const QString& location); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 94630d3a72..d0d1bd96f5 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -582,12 +582,21 @@ function onUsernameChanged() { shareAfterLogin = false; } } +function snapshotLocationSet(location) { + if (location !== "") { + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "snapshotLocationChosen" + })); + } +} button.clicked.connect(onButtonClicked); buttonConnected = true; Window.snapshotShared.connect(snapshotUploaded); tablet.screenChanged.connect(onTabletScreenChanged); Account.usernameChanged.connect(onUsernameChanged); +Snapshot.snapshotLocationSet.connect(snapshotLocationSet); Script.scriptEnding.connect(function () { if (buttonConnected) { button.clicked.disconnect(onButtonClicked); @@ -598,6 +607,7 @@ Script.scriptEnding.connect(function () { } Window.snapshotShared.disconnect(snapshotUploaded); tablet.screenChanged.disconnect(onTabletScreenChanged); + Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet); }); }()); // END LOCAL_SCOPE From 3a36feacad422a1f7d9238a68acd1d2aa49c171c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 2 May 2017 11:29:51 -0700 Subject: [PATCH 151/292] 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 47082e882f..33d6b363ab 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 9b117165bd21d7a3ff21c47fa5190ab4dcd2590d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 2 May 2017 11:44:16 -0700 Subject: [PATCH 152/292] 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 32d675c82bee798b8d8e25ed9c98c19ddd2d19d1 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 11:46:51 -0700 Subject: [PATCH 153/292] Clean up the new code and adress review comments --- libraries/gl/src/gl/Context.cpp | 4 +-- libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp | 2 ++ libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 3 +++ .../src/gpu/gl41/GL41BackendTexture.cpp | 26 +++++-------------- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 2 ++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 3 +++ .../gpu/gl45/GL45BackendVariableTexture.cpp | 4 +-- libraries/gpu/src/gpu/Context.cpp | 4 +++ libraries/gpu/src/gpu/Context.h | 5 ++++ libraries/gpu/src/gpu/Texture.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 6 ++--- 11 files changed, 33 insertions(+), 28 deletions(-) diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index 64d7dd635b..f7e08e607b 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -307,12 +307,12 @@ void Context::create() { qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); } - // if (enableDebugLogger) { + if (enableDebugLogger) { makeCurrent(); glDebugMessageCallback(debugMessageCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); doneCurrent(); - // } + } } #endif diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index a87d0ad6b8..32bfb777a8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(gpugl41logging, "hifi.gpu.gl41") using namespace gpu; using namespace gpu::gl41; +const std::string GL41Backend::GL41_VERSION { "GL41" }; + void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 3662253a66..4caa29574c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -44,6 +44,9 @@ public: explicit GL41Backend(bool syncCache) : Parent(syncCache) {} GL41Backend() : Parent() {} + static const std::string GL41_VERSION; + const std::string& getVersion() const override { return GL41_VERSION; } + class GL41Texture : public GLTexture { using Parent = GLTexture; friend class GL41Backend; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 114f61dfc9..87df109d16 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -310,6 +310,8 @@ void GL41VariableAllocationTexture::syncSampler() const { void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { + // DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT + GLuint fbo { 0 }; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); @@ -334,22 +336,8 @@ void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GL } void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { -#ifdef USE_GL45_COPYIMAGE - for (uint16_t mip = populatedMips; mip < numMips; ++mip) { - auto mipDimensions = texture.evalMipDimensions(mip); - uint16_t targetMip = mip - destAllocatedMip; - uint16_t sourceMip = mip - srcAllocatedMip; - auto faces = GLTexture::getFaceCount(texTarget); - for (uint8_t face = 0; face < faces; ++face) { - glCopyImageSubData( - srcId, texTarget, sourceMip, 0, 0, face, - destId, texTarget, targetMip, 0, 0, face, - mipDimensions.x, mipDimensions.y, 1 - ); - (void)CHECK_GL_ERROR(); - } - } -#else + // DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT + struct MipDesc { GLint _faceSize; GLint _size; @@ -361,7 +349,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui std::vector bytes; - glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); + glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); glBindTexture(texTarget, srcId); const auto& faceTargets = GLTexture::getFaceTargets(texTarget); GLint internalFormat { 0 }; @@ -399,7 +387,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui GLuint pbo { 0 }; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); - glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STREAM_COPY); + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY); (void)CHECK_GL_ERROR(); // Transfer from source texture to pbo @@ -420,7 +408,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); glActiveTexture(GL_TEXTURE0 + GL41Backend::RESOURCE_TRANSFER_TEX_UNIT); - glBindTexture(texTarget, destId); // Transfer from pbo to new texture for (uint16_t mip = populatedMips; mip < numMips; ++mip) { @@ -443,7 +430,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteBuffers(1, &pbo); -#endif } void GL41VariableAllocationTexture::promote() { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index c9d1fb1b2f..c119e27d5b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -18,6 +18,8 @@ Q_LOGGING_CATEGORY(gpugl45logging, "hifi.gpu.gl45") using namespace gpu; using namespace gpu::gl45; +const std::string GL45Backend::GL45_VERSION { "GL45" }; + void GL45Backend::recycle() const { Parent::recycle(); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index dbedd81c76..8319e61382 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -41,6 +41,9 @@ public: explicit GL45Backend(bool syncCache) : Parent(syncCache) {} GL45Backend() : Parent() {} + static const std::string GL45_VERSION; + const std::string& getVersion() const override { return GL45_VERSION; } + class GL45Texture : public GLTexture { using Parent = GLTexture; friend class GL45Backend; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 3381fa8cce..6ea8d15704 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -134,7 +134,7 @@ void GL45ResourceTexture::promote() { // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - copyTexGPUMem(_gpuObject, _target, _id, oldId, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + copyTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); // destroy the old texture glDeleteTextures(1, &oldId); @@ -158,7 +158,7 @@ void GL45ResourceTexture::demote() { // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - copyTexGPUMem(_gpuObject, _target, _id, oldId, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); + copyTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); // destroy the old texture glDeleteTextures(1, &oldId); diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 0030b2fa88..78fef0af59 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -50,6 +50,10 @@ Context::Context(const Context& context) { Context::~Context() { } +const std::string& Context::getBackendVersion() const { + return _backend->getVersion(); +} + void Context::beginFrame(const glm::mat4& renderPose) { assert(!_frameActive); _frameActive = true; diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index b56255f862..c4b9453df3 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -54,6 +54,9 @@ class Backend { public: virtual~ Backend() {}; + + virtual const std::string& getVersion() const = 0; + void setStereoState(const StereoState& stereo) { _stereo = stereo; } virtual void render(const Batch& batch) = 0; @@ -153,6 +156,8 @@ public: Context(); ~Context(); + const std::string& getBackendVersion() const; + void beginFrame(const glm::mat4& renderPose = glm::mat4()); void appendFrameBatch(Batch& batch); FramePointer endFrame(); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index b12ae5bc97..0f84d2a3c9 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -408,7 +408,7 @@ void Texture::setStoredMipFormat(const Element& format) { _storage->setFormat(format); } -const Element Texture::getStoredMipFormat() const { +Element Texture::getStoredMipFormat() const { if (_storage) { return _storage->getFormat(); } else { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 21a1b0ec57..924c1ee148 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -285,7 +285,7 @@ public: Stamp bumpStamp() { return ++_stamp; } void setFormat(const Element& format) { _format = format; } - const Element& getFormat() const { return _format; } + Element getFormat() const { return _format; } private: Stamp _stamp { 0 }; @@ -372,7 +372,7 @@ public: bool isColorRenderTarget() const; bool isDepthStencilRenderTarget() const; - const Element getTexelFormat() const { return _texelFormat; } + Element getTexelFormat() const { return _texelFormat; } Vec3u getDimensions() const { return Vec3u(_width, _height, _depth); } uint16 getWidth() const { return _width; } @@ -468,7 +468,7 @@ public: // Mip storage format is constant across all mips void setStoredMipFormat(const Element& format); - const Element getStoredMipFormat() const; + Element getStoredMipFormat() const; // Manually allocate the mips down until the specified maxMip // this is just allocating the sysmem version of it From a22be93d69a113fc26ca9aeafc2c4d1035aa060b Mon Sep 17 00:00:00 2001 From: Anshuman Dewangan Date: Tue, 2 May 2017 12:15:06 -0700 Subject: [PATCH 154/292] Move old recorder.js script to script-archive --- .../developer/utilities/record/recorder.js | 360 ------------------ 1 file changed, 360 deletions(-) delete mode 100644 scripts/developer/utilities/record/recorder.js diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js deleted file mode 100644 index bda4edc125..0000000000 --- a/scripts/developer/utilities/record/recorder.js +++ /dev/null @@ -1,360 +0,0 @@ -// -// Recorder.js -// examples -// -// Created by Clément Brisset on 8/20/14. -// Copyright 2014 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 -// - -/* globals HIFI_PUBLIC_BUCKET:true, Tool, ToolBar */ - -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("/~/system/libraries/toolBars.js"); - -var recordingFile = "recording.hfr"; - -function setDefaultPlayerOptions() { - Recording.setPlayFromCurrentLocation(true); - Recording.setPlayerUseDisplayName(false); - Recording.setPlayerUseAttachments(false); - Recording.setPlayerUseHeadModel(false); - Recording.setPlayerUseSkeletonModel(true); -} - -var windowDimensions = Controller.getViewportDimensions(); -var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; -var ALPHA_ON = 1.0; -var ALPHA_OFF = 0.7; -var COLOR_ON = { red: 128, green: 0, blue: 0 }; -var COLOR_OFF = { red: 128, green: 128, blue: 128 }; -var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; - -var toolBar = null; -var recordIcon; -var playIcon; -var playLoopIcon; -var saveIcon; -var loadIcon; -var spacing; -var timerOffset; -var timer = null; -var slider = null; - -setupToolBar(); -setupTimer(); - -var watchStop = false; - -function setupToolBar() { - if (toolBar !== null) { - print("Multiple calls to Recorder.js:setupToolBar()"); - return; - } - Tool.IMAGE_HEIGHT /= 2; - Tool.IMAGE_WIDTH /= 2; - - toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); - - toolBar.onMove = onToolbarMove; - - toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); - - recordIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "recording-record.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - x: 0, y: 0, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, - visible: true - }, true, !Recording.isRecording()); - - var playLoopWidthFactor = 1.65; - playIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "play-pause.svg", - width: playLoopWidthFactor * Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, - visible: true - }, false); - - playLoopIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "play-and-loop.svg", - subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: playLoopWidthFactor * Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, - visible: true - }, false); - - timerOffset = toolBar.width + ToolBar.SPACING; - spacing = toolBar.addSpacing(0); - - saveIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "recording-save.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: (Recording.isRecording() || Recording.isPlaying() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, - visible: true - }, false); - - loadIcon = toolBar.addTool({ - imageURL: TOOL_ICON_URL + "recording-upload.svg", - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT, - alpha: (Recording.isRecording() || Recording.isPlaying()) ? ALPHA_OFF : ALPHA_ON, - visible: true - }, false); -} - -function setupTimer() { - timer = Overlays.addOverlay("text", { - font: { size: 15 }, - text: (0.00).toFixed(3), - backgroundColor: COLOR_OFF, - x: 0, y: 0, - width: 200, height: 25, - leftMargin: 5, topMargin: 3, - alpha: 1.0, backgroundAlpha: 1.0, - visible: true - }); - - slider = { x: 0, y: 0, - w: 200, h: 20, - pos: 0.0 // 0.0 <= pos <= 1.0 - }; - slider.background = Overlays.addOverlay("text", { - text: "", - backgroundColor: { red: 128, green: 128, blue: 128 }, - x: slider.x, y: slider.y, - width: slider.w, - height: slider.h, - alpha: 1.0, - backgroundAlpha: 1.0, - visible: true - }); - slider.foreground = Overlays.addOverlay("text", { - text: "", - backgroundColor: { red: 200, green: 200, blue: 200 }, - x: slider.x, y: slider.y, - width: slider.pos * slider.w, - height: slider.h, - alpha: 1.0, - backgroundAlpha: 1.0, - visible: true - }); -} - -function onToolbarMove(newX, newY, deltaX, deltaY) { - Overlays.editOverlay(timer, { - x: newX + timerOffset - ToolBar.SPACING, - y: newY - }); - - slider.x = newX - ToolBar.SPACING; - slider.y = newY - slider.h - ToolBar.SPACING; - - Overlays.editOverlay(slider.background, { - x: slider.x, - y: slider.y - }); - Overlays.editOverlay(slider.foreground, { - x: slider.x, - y: slider.y - }); -} - -function updateTimer() { - var text = ""; - if (Recording.isRecording()) { - text = formatTime(Recording.recorderElapsed()); - } else { - text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength()); - } - - var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0); - - Overlays.editOverlay(timer, { - text: text, - width: timerWidth - }); - toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing); - - if (Recording.isRecording()) { - slider.pos = 1.0; - } else if (Recording.playerLength() > 0) { - slider.pos = Recording.playerElapsed() / Recording.playerLength(); - } - - Overlays.editOverlay(slider.foreground, { - width: slider.pos * slider.w - }); -} - -function formatTime(time) { - var MIN_PER_HOUR = 60; - var SEC_PER_MIN = 60; - var MSEC_DIGITS = 3; - - var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); - time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); - - var minutes = Math.floor(time / (SEC_PER_MIN)); - time -= minutes * (SEC_PER_MIN); - - var seconds = time; - - var text = ""; - text += (hours > 0) ? hours + ":" : ""; - text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : ""; - text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS); - return text; -} - -function moveUI() { - var relative = { x: 70, y: 40 }; - toolBar.move(relative.x, windowDimensions.y - relative.y); -} - -function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - - if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { - if (!Recording.isRecording()) { - Recording.startRecording(); - toolBar.selectTool(recordIcon, false); - toolBar.setAlpha(ALPHA_OFF, playIcon); - toolBar.setAlpha(ALPHA_OFF, playLoopIcon); - toolBar.setAlpha(ALPHA_OFF, saveIcon); - toolBar.setAlpha(ALPHA_OFF, loadIcon); - } else { - Recording.stopRecording(); - toolBar.selectTool(recordIcon, true); - setDefaultPlayerOptions(); - // Plays the recording at the same spot as you recorded it - Recording.setPlayFromCurrentLocation(false); - Recording.setPlayerTime(0); - Recording.loadLastRecording(); - toolBar.setAlpha(ALPHA_ON, playIcon); - toolBar.setAlpha(ALPHA_ON, playLoopIcon); - toolBar.setAlpha(ALPHA_ON, saveIcon); - toolBar.setAlpha(ALPHA_ON, loadIcon); - } - } else if (playIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { - if (Recording.isPlaying()) { - Recording.pausePlayer(); - toolBar.setAlpha(ALPHA_ON, recordIcon); - toolBar.setAlpha(ALPHA_ON, saveIcon); - toolBar.setAlpha(ALPHA_ON, loadIcon); - } else if (Recording.playerLength() > 0) { - Recording.setPlayerLoop(false); - Recording.startPlaying(); - toolBar.setAlpha(ALPHA_OFF, recordIcon); - toolBar.setAlpha(ALPHA_OFF, saveIcon); - toolBar.setAlpha(ALPHA_OFF, loadIcon); - watchStop = true; - } - } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { - if (Recording.isPlaying()) { - Recording.pausePlayer(); - toolBar.setAlpha(ALPHA_ON, recordIcon); - toolBar.setAlpha(ALPHA_ON, saveIcon); - toolBar.setAlpha(ALPHA_ON, loadIcon); - } else if (Recording.playerLength() > 0) { - Recording.setPlayerLoop(true); - Recording.startPlaying(); - toolBar.setAlpha(ALPHA_OFF, recordIcon); - toolBar.setAlpha(ALPHA_OFF, saveIcon); - toolBar.setAlpha(ALPHA_OFF, loadIcon); - } - } else if (saveIcon === toolBar.clicked(clickedOverlay)) { - if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() !== 0) { - recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); - if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { - Recording.saveRecording(recordingFile); - } - } - } else if (loadIcon === toolBar.clicked(clickedOverlay)) { - if (!Recording.isRecording() && !Recording.isPlaying()) { - recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); - if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { - Recording.loadRecording(recordingFile, function(success) { - if (success) { - setDefaultPlayerOptions(); - } else { - print("Failed to load recording from " + recordingFile); - } - }); - } - if (Recording.playerLength() > 0) { - toolBar.setAlpha(ALPHA_ON, playIcon); - toolBar.setAlpha(ALPHA_ON, playLoopIcon); - toolBar.setAlpha(ALPHA_ON, saveIcon); - } - } - } else if (Recording.playerLength() > 0 && - slider.x < event.x && event.x < slider.x + slider.w && - slider.y < event.y && event.y < slider.y + slider.h) { - isSliding = true; - slider.pos = (event.x - slider.x) / slider.w; - Recording.setPlayerTime(slider.pos * Recording.playerLength()); - } -} -var isSliding = false; - -function mouseMoveEvent(event) { - if (isSliding) { - slider.pos = (event.x - slider.x) / slider.w; - if (slider.pos < 0.0 || slider.pos > 1.0) { - Recording.stopPlaying(); - slider.pos = 0.0; - } - Recording.setPlayerTime(slider.pos * Recording.playerLength()); - } -} - -function mouseReleaseEvent(event) { - isSliding = false; -} - -function update() { - var newDimensions = Controller.getViewportDimensions(); - if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) { - windowDimensions = newDimensions; - moveUI(); - } - - updateTimer(); - - if (watchStop && !Recording.isPlaying()) { - watchStop = false; - toolBar.setAlpha(ALPHA_ON, recordIcon); - toolBar.setAlpha(ALPHA_ON, saveIcon); - toolBar.setAlpha(ALPHA_ON, loadIcon); - } -} - -function scriptEnding() { - if (Recording.isRecording()) { - Recording.stopRecording(); - } - if (Recording.isPlaying()) { - Recording.stopPlaying(); - } - toolBar.cleanup(); - Overlays.deleteOverlay(timer); - Overlays.deleteOverlay(slider.background); - Overlays.deleteOverlay(slider.foreground); -} - -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseMoveEvent.connect(mouseMoveEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); -Script.update.connect(update); -Script.scriptEnding.connect(scriptEnding); - -// Should be called last to put everything into position -moveUI(); From 0d1d6924e86deada4651c9c6b85a9e742e08e269 Mon Sep 17 00:00:00 2001 From: Anshuman Dewangan Date: Tue, 2 May 2017 12:20:05 -0700 Subject: [PATCH 155/292] Adding to script-archive --- script-archive/recorder.js | 360 +++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 script-archive/recorder.js diff --git a/script-archive/recorder.js b/script-archive/recorder.js new file mode 100644 index 0000000000..bda4edc125 --- /dev/null +++ b/script-archive/recorder.js @@ -0,0 +1,360 @@ +// +// Recorder.js +// examples +// +// Created by Clément Brisset on 8/20/14. +// Copyright 2014 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 +// + +/* globals HIFI_PUBLIC_BUCKET:true, Tool, ToolBar */ + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +Script.include("/~/system/libraries/toolBars.js"); + +var recordingFile = "recording.hfr"; + +function setDefaultPlayerOptions() { + Recording.setPlayFromCurrentLocation(true); + Recording.setPlayerUseDisplayName(false); + Recording.setPlayerUseAttachments(false); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(true); +} + +var windowDimensions = Controller.getViewportDimensions(); +var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; +var ALPHA_ON = 1.0; +var ALPHA_OFF = 0.7; +var COLOR_ON = { red: 128, green: 0, blue: 0 }; +var COLOR_OFF = { red: 128, green: 128, blue: 128 }; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; + +var toolBar = null; +var recordIcon; +var playIcon; +var playLoopIcon; +var saveIcon; +var loadIcon; +var spacing; +var timerOffset; +var timer = null; +var slider = null; + +setupToolBar(); +setupTimer(); + +var watchStop = false; + +function setupToolBar() { + if (toolBar !== null) { + print("Multiple calls to Recorder.js:setupToolBar()"); + return; + } + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + + toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); + + toolBar.onMove = onToolbarMove; + + toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); + + recordIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-record.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, + visible: true + }, true, !Recording.isRecording()); + + var playLoopWidthFactor = 1.65; + playIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "play-pause.svg", + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + playLoopIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "play-and-loop.svg", + subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: playLoopWidthFactor * Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + timerOffset = toolBar.width + ToolBar.SPACING; + spacing = toolBar.addSpacing(0); + + saveIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-save.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (Recording.isRecording() || Recording.isPlaying() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); + + loadIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-upload.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: (Recording.isRecording() || Recording.isPlaying()) ? ALPHA_OFF : ALPHA_ON, + visible: true + }, false); +} + +function setupTimer() { + timer = Overlays.addOverlay("text", { + font: { size: 15 }, + text: (0.00).toFixed(3), + backgroundColor: COLOR_OFF, + x: 0, y: 0, + width: 200, height: 25, + leftMargin: 5, topMargin: 3, + alpha: 1.0, backgroundAlpha: 1.0, + visible: true + }); + + slider = { x: 0, y: 0, + w: 200, h: 20, + pos: 0.0 // 0.0 <= pos <= 1.0 + }; + slider.background = Overlays.addOverlay("text", { + text: "", + backgroundColor: { red: 128, green: 128, blue: 128 }, + x: slider.x, y: slider.y, + width: slider.w, + height: slider.h, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + slider.foreground = Overlays.addOverlay("text", { + text: "", + backgroundColor: { red: 200, green: 200, blue: 200 }, + x: slider.x, y: slider.y, + width: slider.pos * slider.w, + height: slider.h, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); +} + +function onToolbarMove(newX, newY, deltaX, deltaY) { + Overlays.editOverlay(timer, { + x: newX + timerOffset - ToolBar.SPACING, + y: newY + }); + + slider.x = newX - ToolBar.SPACING; + slider.y = newY - slider.h - ToolBar.SPACING; + + Overlays.editOverlay(slider.background, { + x: slider.x, + y: slider.y + }); + Overlays.editOverlay(slider.foreground, { + x: slider.x, + y: slider.y + }); +} + +function updateTimer() { + var text = ""; + if (Recording.isRecording()) { + text = formatTime(Recording.recorderElapsed()); + } else { + text = formatTime(Recording.playerElapsed()) + " / " + formatTime(Recording.playerLength()); + } + + var timerWidth = text.length * 8 + ((Recording.isRecording()) ? 15 : 0); + + Overlays.editOverlay(timer, { + text: text, + width: timerWidth + }); + toolBar.changeSpacing(timerWidth + ToolBar.SPACING, spacing); + + if (Recording.isRecording()) { + slider.pos = 1.0; + } else if (Recording.playerLength() > 0) { + slider.pos = Recording.playerElapsed() / Recording.playerLength(); + } + + Overlays.editOverlay(slider.foreground, { + width: slider.pos * slider.w + }); +} + +function formatTime(time) { + var MIN_PER_HOUR = 60; + var SEC_PER_MIN = 60; + var MSEC_DIGITS = 3; + + var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); + time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); + + var minutes = Math.floor(time / (SEC_PER_MIN)); + time -= minutes * (SEC_PER_MIN); + + var seconds = time; + + var text = ""; + text += (hours > 0) ? hours + ":" : ""; + text += (minutes > 0) ? ((minutes < 10 && text !== "") ? "0" : "") + minutes + ":" : ""; + text += ((seconds < 10 && text !== "") ? "0" : "") + seconds.toFixed(MSEC_DIGITS); + return text; +} + +function moveUI() { + var relative = { x: 70, y: 40 }; + toolBar.move(relative.x, windowDimensions.y - relative.y); +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + + if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { + if (!Recording.isRecording()) { + Recording.startRecording(); + toolBar.selectTool(recordIcon, false); + toolBar.setAlpha(ALPHA_OFF, playIcon); + toolBar.setAlpha(ALPHA_OFF, playLoopIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + } else { + Recording.stopRecording(); + toolBar.selectTool(recordIcon, true); + setDefaultPlayerOptions(); + // Plays the recording at the same spot as you recorded it + Recording.setPlayFromCurrentLocation(false); + Recording.setPlayerTime(0); + Recording.loadLastRecording(); + toolBar.setAlpha(ALPHA_ON, playIcon); + toolBar.setAlpha(ALPHA_ON, playLoopIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } + } else if (playIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { + if (Recording.isPlaying()) { + Recording.pausePlayer(); + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } else if (Recording.playerLength() > 0) { + Recording.setPlayerLoop(false); + Recording.startPlaying(); + toolBar.setAlpha(ALPHA_OFF, recordIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + watchStop = true; + } + } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { + if (Recording.isPlaying()) { + Recording.pausePlayer(); + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } else if (Recording.playerLength() > 0) { + Recording.setPlayerLoop(true); + Recording.startPlaying(); + toolBar.setAlpha(ALPHA_OFF, recordIcon); + toolBar.setAlpha(ALPHA_OFF, saveIcon); + toolBar.setAlpha(ALPHA_OFF, loadIcon); + } + } else if (saveIcon === toolBar.clicked(clickedOverlay)) { + if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() !== 0) { + recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); + if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { + Recording.saveRecording(recordingFile); + } + } + } else if (loadIcon === toolBar.clicked(clickedOverlay)) { + if (!Recording.isRecording() && !Recording.isPlaying()) { + recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); + if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { + Recording.loadRecording(recordingFile, function(success) { + if (success) { + setDefaultPlayerOptions(); + } else { + print("Failed to load recording from " + recordingFile); + } + }); + } + if (Recording.playerLength() > 0) { + toolBar.setAlpha(ALPHA_ON, playIcon); + toolBar.setAlpha(ALPHA_ON, playLoopIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + } + } + } else if (Recording.playerLength() > 0 && + slider.x < event.x && event.x < slider.x + slider.w && + slider.y < event.y && event.y < slider.y + slider.h) { + isSliding = true; + slider.pos = (event.x - slider.x) / slider.w; + Recording.setPlayerTime(slider.pos * Recording.playerLength()); + } +} +var isSliding = false; + +function mouseMoveEvent(event) { + if (isSliding) { + slider.pos = (event.x - slider.x) / slider.w; + if (slider.pos < 0.0 || slider.pos > 1.0) { + Recording.stopPlaying(); + slider.pos = 0.0; + } + Recording.setPlayerTime(slider.pos * Recording.playerLength()); + } +} + +function mouseReleaseEvent(event) { + isSliding = false; +} + +function update() { + var newDimensions = Controller.getViewportDimensions(); + if (windowDimensions.x !== newDimensions.x || windowDimensions.y !== newDimensions.y) { + windowDimensions = newDimensions; + moveUI(); + } + + updateTimer(); + + if (watchStop && !Recording.isPlaying()) { + watchStop = false; + toolBar.setAlpha(ALPHA_ON, recordIcon); + toolBar.setAlpha(ALPHA_ON, saveIcon); + toolBar.setAlpha(ALPHA_ON, loadIcon); + } +} + +function scriptEnding() { + if (Recording.isRecording()) { + Recording.stopRecording(); + } + if (Recording.isPlaying()) { + Recording.stopPlaying(); + } + toolBar.cleanup(); + Overlays.deleteOverlay(timer); + Overlays.deleteOverlay(slider.background); + Overlays.deleteOverlay(slider.foreground); +} + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + +// Should be called last to put everything into position +moveUI(); From 7f1db38390f03a2f8dfcf53c2c0e1c511285ecbb Mon Sep 17 00:00:00 2001 From: Vladyslav Stelmakhovskyi Date: Thu, 27 Apr 2017 09:30:00 +0200 Subject: [PATCH 156/292] 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 157/292] 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 158/292] 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 159/292] 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 160/292] 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 161/292] 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 162/292] 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 163/292] 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 164/292] 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 165/292] 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 166/292] 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 167/292] 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 168/292] 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 169/292] 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 170/292] 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 171/292] 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 d1dff7606cc779a802c54dbc999649dd80a17adf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 14:10:24 -0700 Subject: [PATCH 172/292] Lint cleanup; Immediate share button visibility; --- interface/src/ui/SnapshotUploader.cpp | 2 + scripts/system/html/css/SnapshotReview.css | 24 +- scripts/system/html/img/fb_logo72.png | Bin 0 -> 1587 bytes scripts/system/html/img/twitter_logo72.png | Bin 0 -> 1394 bytes scripts/system/html/js/SnapshotReview.js | 421 +++++++++++++-------- scripts/system/snapshot.js | 5 +- 6 files changed, 289 insertions(+), 163 deletions(-) create mode 100644 scripts/system/html/img/fb_logo72.png create mode 100644 scripts/system/html/img/twitter_logo72.png diff --git a/interface/src/ui/SnapshotUploader.cpp b/interface/src/ui/SnapshotUploader.cpp index aa37608476..3408cb8512 100644 --- a/interface/src/ui/SnapshotUploader.cpp +++ b/interface/src/ui/SnapshotUploader.cpp @@ -31,6 +31,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { auto dataObject = doc.object().value("data").toObject(); QString thumbnailUrl = dataObject.value("thumbnail_url").toString(); QString imageUrl = dataObject.value("image_url").toString(); + QString snapshotID = dataObject.value("id").toString(); auto addressManager = DependencyManager::get(); QString placeName = _inWorldLocation.authority(); // We currently only upload shareable places, in which case this is just host. QString currentPath = _inWorldLocation.path(); @@ -43,6 +44,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { if (dataObject.contains("shareable_url")) { detailsObject.insert("shareable_url", dataObject.value("shareable_url").toString()); } + detailsObject.insert("snapshot_id", snapshotID); QString pickledDetails = QJsonDocument(detailsObject).toJson(); userStoryObject.insert("details", pickledDetails); userStoryObject.insert("thumbnail_url", thumbnailUrl); diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index ccc9386d13..0aa422ca84 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -104,7 +104,7 @@ input[type=button].naked:active { */ /* -// START styling of share bar +// START styling of share overlay */ .shareControls { display: flex; @@ -212,6 +212,28 @@ input[type=button].naked:active { // END styling of share overlay */ +/* +// START styling of uploading message +*/ +.uploadingMessage { + width: 100%; + height: 100%; + position: absolute; + text-align: center; + background-color: rgba(0, 0, 0, 0.8); + left: 0; + top: 0; +} +.uploadingMessage > img { + width: 72px; + height: 72px; + display: block; + margin: 60px auto 10px auto; +} +/* +// END styling of uploading message +*/ + /* // START styling of snapshot controls (bottom panel) and its contents */ diff --git a/scripts/system/html/img/fb_logo72.png b/scripts/system/html/img/fb_logo72.png new file mode 100644 index 0000000000000000000000000000000000000000..8e3a2370dd2bab99c363f5bf5d06d8e89ee30e5c GIT binary patch literal 1587 zcmbVMeM}Q)7(Y=F4GtK@PgstVZElmc*DtOoEwpz9Di*p@uoI(P+oNsD_3HJY2P_-- zfgke&L`)o_Btz%?V#w5>PIM}cIMiS^6#=JNK#Vw*MFjkMhhQ>VDWdcd8RRqJJTrPm6GPxX{2Z}|oSSS(;Me^D9^hK@rfS29!+PQcBh-j!)a?iMY>rXu7o(sBd|Gb zs7Qv0U^q@HiW7_E2n-`4M1oGgM(&iuY>l0ScV*&t0$~Z zHy`+fzo1uoaguQskhUa-rohn&qt3TVqg35ecu>X zL25j?YLCTb-O|&sWMSU$&fJRb>YcTFT9qO3UpzoIg*-j=^wfk)-p7w)V!GPvSo5sX z6B*^Is_t#42C|m-)D@qu9V%I}^*!Ar>0nc8uMz~12&Mbt#aI&RL!bB>J+n41odYc#bKHLUn$@#Gts;TN~(|4`a` zq5j+1dwV_eTLkAT?ka)J7e*qx_Une%u6p6PHGQbxd^MnNNYFV$pYvDVbZOOw>@{XX zSsL>qc&Ny-qwZK|UC)`CYV2jy_e9B|rAXNbH)+{G!MdYm%c+=4`Q@K}*IRtMvUPi+ z{L%B1h;Jx#QTx7sqBcfHJPb>z3h!z-e(UU}rIVr**Y7>+tS=1DNbk-O94WcHEI6W# z85rNc8GXL;!^+R@44n(kI_!Ed%TiI^IM{oJy|?Sn`&?~KqL1yqXzHk`{ORh{zV`LE z9~^8cxN*HRCiI$mbQb4A_eMXl2=}zyEBLuMr1z2IiEI~`sj6#?v>bW-Rdh0#`I~WK z>h^%rt!G{EnuM;SO;he#(aYDqY#X%0skI&0hV(1)`e%`)ropU{P3af+Z*17|!Aa(2 z{h{)jrJIZ4fUvcRe4!bBCkO}2Mk7Hu7!daB?}>pkx5D#k?X01vX`(c`?W!epUS;;Q sE7^S|?6hW8Memb{eba`wwS|le1lD8gSC&6Z!uW4or%l5TX>wNl3u=i}%K!iX literal 0 HcmV?d00001 diff --git a/scripts/system/html/img/twitter_logo72.png b/scripts/system/html/img/twitter_logo72.png new file mode 100644 index 0000000000000000000000000000000000000000..862f2452285534cc699d9b8cd50ede0e92c2c083 GIT binary patch literal 1394 zcmV-&1&#WNP)3WXL}mU6Q! zd!4HfH9!}ZopW{;GGxBDo$Sm%-<)&)^UuGFHrD%LHNvd)4WtS~Om7%sdczRY8-|$P zFvRqRA*MGBF}<;B#n_b%@8D2=I+I~RVFCL6vNtGeb>)>2!w4!p?EWHdXBKAA-0=aw z+|Va{`?nAfl$cZQ&BpzHc_I>fJRXR&HBgqzwvLKI5>M7?Lm!t~OId8ViY2iu9=EEsAl5?I~BjG<-+H~aDH z9=_2l0ssO~8Ivq4?#%vw{!|s3J}j|tggLc(E4M9^>>LH4u7^L|8NJVm4;Gv6bf`U@ zEKII#ZlyoF#dPdpn{(cgr7>fuF5(h@?{>TaxgZ_?`ApjJy|&??a&=nm;wBSAw_Dur z#6LZ5-@8%Od@jjhGc9VL!Ymuom{J#ZaHy__uj}U3pGYP)8j79aSyohAVLe)I-IQqM z+`XQ%7*GI?UgqCD7bRNJWXYp*LRxqb0X*hm_qg%#KC!l&zdp10Y{|CLhzT;h-4^-2 zcfr1v2(n;JBhoa#cnC8_FBX`7f689J$NJ!@}I2nb38xfPI(SV&aruL$!K zaSRNC3O8HfX2T+MPDtm*rLIY--!FTE%6M3_a!C!uCDrF$e5HrYPfsw^Ru)Qfa7oTw zc_C7H1!+bLfOINu%fy1LRR0$Cg@|vHrOF-ulBJ9}$fDKk;F_McR~K;&y#n>KB*Gh{ zj6yaQug}Pp9`>bz<$H_kRlOs}Kz55~M;A%=DWz+YG9sB+vrinMof{O0GIWeDiZQ+MS&9o^igQ? zTSS6a8pH2(L>G1@G^EKV4#p`v)j*lJIR?Jz;dh@59`1}zs&25TM**nqPHMtacBzV; z>{sPhURKgf2ot|#C8qs`)Z$BOwo{H+i%q@5F#m-56HiolOWYK}~bSALm_` zh-AyzGhT7`$>67#_~hH}vH}O%BFRs}TM)On@0dA-&9pdWu?IT=c8YAX=VkQh^@DveR-yri@0^X)u1P%D;gMXzn=wq{@x0}eBC*^re* z$!yM13F>^&?kTB#O!~to zbxlc{S1R$)W*@@{Tz1B3MQb@^#vm#X5@0GKkAxIW$_NUy*~cUWh6D0&K$DAU!>awC zhasjn3^Bc7i0KVOOm7%sdczRY8-|$PShZsR0V1^GX^jP^B>(^b07*qoM6N<$f{Z' + - '

    This app lets you take and share snaps and GIFs with your connections in High Fidelity.

    ' + - "

    Setup Instructions

    " + - "

    Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:

    " + - '
    ' + - '
    ' + - '' + - '
    '; + '
    ' + + '

    This app lets you take and share snaps and GIFs with your connections in High Fidelity.

    ' + + "

    Setup Instructions

    " + + "

    Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:

    " + + '
    ' + + '
    ' + + '' + + '
    '; document.getElementById("snap-button").disabled = true; } function showSetupComplete() { var snapshotImagesDiv = document.getElementById("snapshot-images"); snapshotImagesDiv.className = "snapshotInstructions"; snapshotImagesDiv.innerHTML = 'Snapshot Instructions' + - '
    ' + - "

    You're all set!

    " + - '

    Try taking a snapshot by pressing the red button below.

    '; + '
    ' + + "

    You're all set!

    " + + '

    Try taking a snapshot by pressing the red button below.

    '; } function chooseSnapshotLocation() { EventBridge.emitWebEvent(JSON.stringify({ @@ -52,68 +54,71 @@ function clearImages() { imageCount = 0; idCounter = 0; } -function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) { - if (!image_data.localPath) { - return; + +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 } - var id = "p" + idCounter++; - // imageContainer setup - var imageContainer = document.createElement("DIV"); - imageContainer.id = id; - imageContainer.style.width = "95%"; - imageContainer.style.height = "240px"; - imageContainer.style.margin = "5px auto"; - imageContainer.style.display = "flex"; - imageContainer.style.justifyContent = "center"; - imageContainer.style.alignItems = "center"; - imageContainer.style.position = "relative"; - // img setup - var img = document.createElement("IMG"); - img.id = id + "img"; - if (imageCount > 1) { - img.setAttribute("class", "multiple"); - } - img.src = image_data.localPath; - imageContainer.appendChild(img); - document.getElementById("snapshot-images").appendChild(imageContainer); - paths.push(image_data.localPath); - var isGif = img.src.split('.').pop().toLowerCase() === "gif"; - if (isGif) { - imageContainer.innerHTML += 'GIF'; - } - if (!isGifLoading && !isShowingPreviousImages && canShare) { - shareForUrl(id); - } else if (isShowingPreviousImages && canShare && image_data.story_id) { - appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled) + var imageContainer = document.getElementById(selectedID), + image = document.getElementById(selectedID + 'img'), + shareBar = document.getElementById(selectedID + "shareBar"), + shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"), + showShareButtonsButton = document.getElementById(selectedID + "showShareButtonsButton"), + itr, + containers = document.getElementsByClassName("shareControls"); + + if (isSelected) { + showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, false); }; + showShareButtonsButton.classList.remove("inactive"); + showShareButtonsButton.classList.add("active"); + + image.onclick = function () { selectImageToShare(selectedID, false); }; + imageContainer.style.outline = "4px solid #00b4ef"; + imageContainer.style.outlineOffset = "-4px"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; + + shareButtonsDiv.style.visibility = "visible"; + + for (itr = 0; itr < containers.length; itr += 1) { + var parentID = containers[itr].id.slice(0, 2); + if (parentID !== selectedID) { + selectImageToShare(parentID, false); + } + } + } else { + showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true); }; + showShareButtonsButton.classList.remove("active"); + showShareButtonsButton.classList.add("inactive"); + + image.onclick = function () { selectImageToShare(selectedID, true); }; + imageContainer.style.outline = "none"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + + shareButtonsDiv.style.visibility = "hidden"; } } -function appendShareBar(divID, story_id, isGif, blastButtonDisabled, hifiButtonDisabled) { - var story_url = "https://highfidelity.com/user_stories/" + story_id; - var parentDiv = document.getElementById(divID); - parentDiv.setAttribute('data-story-id', story_id); - document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, blastButtonDisabled, hifiButtonDisabled)); - if (divID === "p0") { - selectImageToShare(divID, true); - } -} -function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButtonDisabled) { - var shareBar = document.createElement("div"); +function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled) { + var shareBar = document.createElement("div"), + shareButtonsDivID = parentID + "shareButtonsDiv", + showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv", + showShareButtonsButtonID = parentID + "showShareButtonsButton", + showShareButtonsLabelID = parentID + "showShareButtonsLabel", + blastToConnectionsButtonID = parentID + "blastToConnectionsButton", + shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", + facebookButtonID = parentID + "facebookButton", + twitterButtonID = parentID + "twitterButton"; + shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareButtonsDivID = parentID + "shareButtonsDiv"; - var showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv"; - var showShareButtonsButtonID = parentID + "showShareButtonsButton"; - var showShareButtonsLabelID = parentID + "showShareButtonsLabel"; - var blastToConnectionsButtonID = parentID + "blastToConnectionsButton"; - var shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton"; - var facebookButtonID = parentID + "facebookButton"; - var twitterButtonID = parentID + "twitterButton"; - shareBar.innerHTML += '' + - ''; + 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) { - document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled)); +function appendShareBar(divID, 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)); if (divID === "p0") { selectImageToShare(divID, true); } @@ -159,7 +165,7 @@ function shareForUrl(selectedID) { data: paths[parseInt(selectedID.substring(1), 10)] })); } -function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) { +function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (!image_data.localPath) { return; } @@ -184,11 +190,11 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b imageContainer.innerHTML += 'GIF'; } if (!isGifLoading && !isShowingPreviousImages && canShare) { + appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true); shareForUrl(id); - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled); } if (isShowingPreviousImages && image_data.story_id) { - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled); + appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); updateShareInfo(id, image_data.story_id); } } @@ -353,7 +359,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(2); + //testInBrowser(1); 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) { @@ -382,7 +388,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); + addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); }); } else { showSnapshotInstructions(); @@ -408,7 +414,7 @@ window.onload = function () { paths[1] = gifPath; if (messageOptions.canShare) { shareForUrl("p1"); - appendShareBar("p1", true, false, false); + appendShareBar("p1", true, false, false, true); } } } else { @@ -454,13 +460,13 @@ function testInBrowser(test) { if (test === 0) { showSetupInstructions(); } else if (test === 1) { - imageCount = 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.gif' }, false, true, true, false, false); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg' }, false, true, true, false, false); + 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 }, false, true, 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.gif' }, false, true, true, false, false); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg' }, false, true, true, false, false); + 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 }, false, true, true, false, false, true); showUploadingMessage("p0", 'facebook'); showUploadingMessage("p1", 'twitter'); } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index eb45a07e2f..966f43cc55 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -311,7 +311,8 @@ function onButtonClicked() { snapshotOptions = { containsGif: previousAnimatedSnapPath !== "", processingGif: false, - shouldUpload: false + shouldUpload: false, + canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID") } imageData = []; if (previousStillSnapPath !== "") { From 41f0b1682ed2a7fdb228208a3a7cfafe57cdabd5 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 14:45:41 -0700 Subject: [PATCH 176/292] Fixing warnings too --- .../src/gpu/gl41/GL41BackendTexture.cpp | 54 +++++++++---------- .../gpu/gl45/GL45BackendVariableTexture.cpp | 6 +-- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 87df109d16..796abda5a3 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -309,7 +309,7 @@ void GL41VariableAllocationTexture::syncSampler() const { } -void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { +void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { // DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT GLuint fbo { 0 }; @@ -320,8 +320,8 @@ void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GL // copy pre-existing mips for (uint16_t mip = populatedMips; mip < mips; ++mip) { auto mipDimensions = texture.evalMipDimensions(mip); - uint16_t targetMip = mip - destAllocatedMip; - uint16_t sourceMip = mip - srcAllocatedMip; + uint16_t targetMip = mip - destMipOffset; + uint16_t sourceMip = mip - srcMipOffset; for (GLenum target : GLTexture::getFaceTargets(texTarget)) { glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip); (void)CHECK_GL_ERROR(); @@ -335,7 +335,7 @@ void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GL glDeleteFramebuffers(1, &fbo); } -void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { +void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { // DestID must be bound to the GL41Backend::RESOURCE_TRANSFER_TEX_UNIT struct MipDesc { @@ -359,7 +359,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui for (uint16_t mip = populatedMips; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; - uint16_t sourceLevel = mip - srcAllocatedMip; + uint16_t sourceLevel = mip - srcMipOffset; // Grab internal format once if (internalFormat == 0) { @@ -383,7 +383,6 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui (void)CHECK_GL_ERROR(); // Allocate the PBO to accomodate for all the mips to copy - GLint pboPointer = 0; GLuint pbo { 0 }; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); @@ -394,10 +393,10 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui for (uint16_t mip = populatedMips; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; - uint16_t sourceLevel = mip - srcAllocatedMip; + uint16_t sourceLevel = mip - srcMipOffset; - for (GLint f = 0; f < faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); + for (GLuint f = 0; f < faceTargets.size(); f++) { + glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (sourceMip._offset + f * sourceMip._faceSize)); } (void)CHECK_GL_ERROR(); } @@ -413,9 +412,9 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui for (uint16_t mip = populatedMips; mip < numMips; ++mip) { auto& sourceMip = sourceMips[mip]; - uint16_t destLevel = mip - destAllocatedMip; + uint16_t destLevel = mip - destMipOffset; - for (GLint f = 0; f < faceTargets.size(); f++) { + for (GLuint f = 0; f < faceTargets.size(); f++) { #ifdef DEBUG_COPY GLint destWidth, destHeight, destSize; glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); @@ -423,7 +422,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); #endif glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, - sourceMip._faceSize, (void*)(pboPointer + (sourceMip._offset + f * sourceMip._faceSize))); + sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); gpu::gl::checkGLError(); } } @@ -451,17 +450,14 @@ void GL41VariableAllocationTexture::promote() { // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - if (_texelFormat.isCompressed()) { - withPreservedTexture([&] { + withPreservedTexture([&] { + if (_texelFormat.isCompressed()) { copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); - } else { - withPreservedTexture([&] { + } else { copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); - } + } + syncSampler(); + }); // destroy the old texture glDeleteTextures(1, &oldId); @@ -484,17 +480,15 @@ void GL41VariableAllocationTexture::demote() { // copy pre-existing mips uint16_t numMips = _gpuObject.getNumMips(); - if (_texelFormat.isCompressed()) { - withPreservedTexture([&] { + withPreservedTexture([&] { + if (_texelFormat.isCompressed()) { copyCompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); - } else { - withPreservedTexture([&] { + } else { copyUncompressedTexGPUMem(_gpuObject, _target, oldId, _id, numMips, oldAllocatedMip, _allocatedMip, _populatedMip); - syncSampler(); - }); - } + } + syncSampler(); + }); + // destroy the old texture glDeleteTextures(1, &oldId); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 6ea8d15704..4f40f47085 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -98,11 +98,11 @@ void GL45ResourceTexture::syncSampler() const { } -void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcAllocatedMip, uint16_t destAllocatedMip, uint16_t populatedMips) { +void copyTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { for (uint16_t mip = populatedMips; mip < numMips; ++mip) { auto mipDimensions = texture.evalMipDimensions(mip); - uint16_t targetMip = mip - destAllocatedMip; - uint16_t sourceMip = mip - srcAllocatedMip; + uint16_t targetMip = mip - destMipOffset; + uint16_t sourceMip = mip - srcMipOffset; auto faces = GLTexture::getFaceCount(texTarget); for (uint8_t face = 0; face < faces; ++face) { glCopyImageSubData( From 0353940d9ebe721cb17e51f1ea1fb7d494bde917 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 17:42:23 -0400 Subject: [PATCH 177/292] 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 0beceae59b8a263f8e78e32cccbcee03b8f0b1bd Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 15:19:20 -0700 Subject: [PATCH 178/292] Checkpoint --- scripts/system/html/css/SnapshotReview.css | 117 +++++++++++---------- scripts/system/html/js/SnapshotReview.js | 72 ++++++++----- 2 files changed, 110 insertions(+), 79 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 0aa422ca84..7d4239985f 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -111,20 +111,70 @@ input[type=button].naked:active { justify-content: space-between; flex-direction: row; align-items: center; - height: 45px; - line-height: 60px; + height: 65px; + line-height: 65px; width: calc(100% - 8px); position: absolute; bottom: 4px; left: 4px; right: 4px; } +.showShareButtonsButtonDiv { + display: inline-flex; + align-items: center; + font-family: Raleway-SemiBold; + font-size: 14px; + color: white; + width: 60px; + height: 100%; + margin-right: 10px; + margin-bottom: 15px; +} +.showShareButtonsButtonDiv > label { + text-shadow: 2px 2px 3px #000000; + margin-bottom: -10px; + margin-left: 12px; +} +.showShareButton.active { + border-color: #00b4ef; + border-width: 3px; + background-color: white; +} +.showShareButton.active:hover { + background-color: #afafaf; +} +.showShareButton.active:active { + background-color: white; +} +.showShareButton.inactive { + border-width: 0; + background-color: white; +} +.showShareButton.inactive:hover { + background-color: #afafaf; +} +.showShareButton.inactive:active { + background-color: white; +} +.showShareButtonDots { + display: block; + width: 40px; + height: 40px; + font-family: HiFi-Glyphs; + font-size: 60px; + position: absolute; + left: 6px; + bottom: 42px; + color: white; + pointer-events: none; +} .shareButtons { display: flex; align-items: center; - margin-left: 15px; + margin-left: 0; + margin-bottom: 15px; height: 100%; - width: 75%; + width: calc(100% - 60px); } .blastToConnections { text-align: left; @@ -155,58 +205,15 @@ input[type=button].naked:active { margin-right: 8px; border-radius: 3px; } -.showShareButtonsButtonDiv { - display: inline-flex; - align-items: center; - font-family: Raleway-SemiBold; - font-size: 14px; - color: white; - height: 100%; - margin-right: 10px; -} -.showShareButtonsButtonDiv > label { - text-shadow: 2px 2px 3px #000000; -} -.showShareButton { - width: 40px; - height: 40px; - border-radius: 50%; - border-width: 0; - margin-left: 5px; - outline: none; -} -.showShareButton.active { - border-color: #00b4ef; - border-width: 3px; - background-color: white; -} -.showShareButton.active:hover { - background-color: #afafaf; -} -.showShareButton.active:active { - background-color: white; -} -.showShareButton.inactive { - border-width: 0; - background-color: white; -} -.showShareButton.inactive:hover { - background-color: #afafaf; -} -.showShareButton.inactive:active { - background-color: white; -} -.showShareButtonDots { - display: block; - width: 40px; - height: 40px; - font-family: HiFi-Glyphs; - font-size: 60px; +.shareControlsHelp { + height: 25px; + line-height: 25px; position: absolute; - right: 20px; - bottom: 12px; - color: #00b4ef; - pointer-events: none; + bottom: 0; + left: 65px; + right: 0; + font-family: Raleway-Regular; + font-weight: 400; } /* // END styling of share overlay diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 8a569bdf5c..5045f1a5e9 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -76,14 +76,15 @@ function selectImageToShare(selectedID, isSelected) { image = document.getElementById(selectedID + 'img'), shareBar = document.getElementById(selectedID + "shareBar"), shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"), - showShareButtonsButton = document.getElementById(selectedID + "showShareButtonsButton"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), itr, containers = document.getElementsByClassName("shareControls"); if (isSelected) { - showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, false); }; - showShareButtonsButton.classList.remove("inactive"); - showShareButtonsButton.classList.add("active"); + showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, false); }; + showShareButtonsButtonDiv.classList.remove("inactive"); + showShareButtonsButtonDiv.classList.add("active"); image.onclick = function () { selectImageToShare(selectedID, false); }; imageContainer.style.outline = "4px solid #00b4ef"; @@ -92,6 +93,7 @@ function selectImageToShare(selectedID, isSelected) { shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; shareButtonsDiv.style.visibility = "visible"; + shareBarHelp.style.visibility = "visible"; for (itr = 0; itr < containers.length; itr += 1) { var parentID = containers[itr].id.slice(0, 2); @@ -100,9 +102,9 @@ function selectImageToShare(selectedID, isSelected) { } } } else { - showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true); }; - showShareButtonsButton.classList.remove("active"); - showShareButtonsButton.classList.add("inactive"); + showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, true); }; + showShareButtonsButtonDiv.classList.remove("active"); + showShareButtonsButtonDiv.classList.add("inactive"); image.onclick = function () { selectImageToShare(selectedID, true); }; imageContainer.style.outline = "none"; @@ -110,13 +112,14 @@ function selectImageToShare(selectedID, isSelected) { shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; shareButtonsDiv.style.visibility = "hidden"; + shareBarHelp.style.visibility = "hidden"; } } function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { var shareBar = document.createElement("div"), + shareBarHelpID = parentID + "shareBarHelp", shareButtonsDivID = parentID + "shareButtonsDiv", showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv", - showShareButtonsButtonID = parentID + "showShareButtonsButton", showShareButtonsLabelID = parentID + "showShareButtonsLabel", blastToConnectionsButtonID = parentID + "blastToConnectionsButton", shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", @@ -125,25 +128,25 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareBarInnerHTML = '' + - '
    ' + - '' + - '' + - '
    ' + + var shareBarInnerHTML = '
    ' + + '' + + '' + '' + '
    ' + + '
    ' + + ''; 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); }; @@ -323,6 +326,27 @@ function shareWithEveryone(selectedID, isGif) { showUploadingMessage(selectedID, 'hifi'); } } +function shareButtonHovered(destination, selectedID) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + + switch (destination) { + case 'blast': + shareBarHelp.style.backgroundColor = "#EA4C5F"; + break; + case 'hifi': + shareBarHelp.style.backgroundColor = "#1FC6A6"; + break; + case 'facebook': + shareBarHelp.style.backgroundColor = "#3C58A0"; + break; + case 'twitter': + shareBarHelp.style.backgroundColor = "#00B4EE"; + break; + } +} function shareButtonClicked(destination, selectedID) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID @@ -359,7 +383,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(1); + testInBrowser(1); 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) { @@ -462,11 +486,11 @@ 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.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 }, 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); } else if (test === 2) { - 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 }, 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); showUploadingMessage("p0", 'facebook'); showUploadingMessage("p1", 'twitter'); } From eed4da3570833de3667d7f41931d505921f1cf98 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:36:38 -0700 Subject: [PATCH 179/292] 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 ec70b0b1c8..33fd2b8fd9 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 f1170dc17dbd7ead1a8c0353ed2268e93ad04340 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 15:48:12 -0700 Subject: [PATCH 180/292] 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 33fd2b8fd9..15aa52b5c0 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 7817c72ea13b67d466a5231ac1f0b19c78dee4c5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 15:58:43 -0700 Subject: [PATCH 181/292] Another checkpoint --- scripts/system/html/css/SnapshotReview.css | 83 +++++++-------------- scripts/system/html/img/blast_icon.svg | 20 +++++ scripts/system/html/img/fb_icon.svg | 13 ++++ scripts/system/html/img/fb_logo.png | Bin 1257 -> 0 bytes scripts/system/html/img/fb_logo72.png | Bin 1587 -> 0 bytes scripts/system/html/img/hifi_icon.svg | 19 +++++ scripts/system/html/img/shareIcon.png | Bin 15201 -> 0 bytes scripts/system/html/img/shareToFeed.png | Bin 15486 -> 0 bytes scripts/system/html/img/twitter_icon.svg | 12 +++ scripts/system/html/img/twitter_logo.png | Bin 552 -> 0 bytes scripts/system/html/img/twitter_logo72.png | Bin 1394 -> 0 bytes scripts/system/html/js/SnapshotReview.js | 34 ++++++--- 12 files changed, 116 insertions(+), 65 deletions(-) create mode 100644 scripts/system/html/img/blast_icon.svg create mode 100644 scripts/system/html/img/fb_icon.svg delete mode 100644 scripts/system/html/img/fb_logo.png delete mode 100644 scripts/system/html/img/fb_logo72.png create mode 100644 scripts/system/html/img/hifi_icon.svg delete mode 100644 scripts/system/html/img/shareIcon.png delete mode 100644 scripts/system/html/img/shareToFeed.png create mode 100644 scripts/system/html/img/twitter_icon.svg delete mode 100644 scripts/system/html/img/twitter_logo.png delete mode 100644 scripts/system/html/img/twitter_logo72.png diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 7d4239985f..c0d5991e0b 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -125,36 +125,21 @@ input[type=button].naked:active { font-family: Raleway-SemiBold; font-size: 14px; color: white; - width: 60px; + width: 75px; height: 100%; - margin-right: 10px; - margin-bottom: 15px; + margin-bottom: 0px; +} +.showShareButtonsButtonDiv.active:hover { + background-color: rgba(0, 0, 0, 0.45); + background-size: 2px; } .showShareButtonsButtonDiv > label { text-shadow: 2px 2px 3px #000000; - margin-bottom: -10px; + margin-bottom: -14px; margin-left: 12px; } -.showShareButton.active { - border-color: #00b4ef; - border-width: 3px; - background-color: white; -} -.showShareButton.active:hover { - background-color: #afafaf; -} -.showShareButton.active:active { - background-color: white; -} -.showShareButton.inactive { - border-width: 0; - background-color: white; -} -.showShareButton.inactive:hover { - background-color: #afafaf; -} -.showShareButton.inactive:active { - background-color: white; +.showShareButtonsButtonDiv:hover > label { + text-shadow: none; } .showShareButtonDots { display: block; @@ -164,56 +149,44 @@ input[type=button].naked:active { font-size: 60px; position: absolute; left: 6px; - bottom: 42px; + bottom: 32px; color: white; pointer-events: none; } .shareButtons { display: flex; - align-items: center; - margin-left: 0; - margin-bottom: 15px; - height: 100%; + align-items: flex-end; + height: 40px; width: calc(100% - 60px); + margin-bottom: 25px; + margin-left: 0; +} +.shareButtons img { + width: 40px; + height: 40px; +} +.shareButton { + width: 40px; + height: 40px; + display: inline-block; } .blastToConnections { - text-align: left; - margin-right: 20px; - height: 29px; + background-image: url(../img/fb_logo.png); } .shareWithEveryone { background: #DDDDDD url(../img/shareToFeed.png) no-repeat scroll center; - border-width: 0px; - text-align: left; - margin-right: 8px; - height: 29px; - width: 30px; - border-radius: 3px; -} -.facebookButton { - background-image: url(../img/fb_logo.png); - width: 29px; - height: 29px; - display: inline-block; - margin-right: 8px; -} -.twitterButton { - background-image: url(../img/twitter_logo.png); - width: 29px; - height: 29px; - display: inline-block; - margin-right: 8px; - border-radius: 3px; } .shareControlsHelp { height: 25px; line-height: 25px; position: absolute; bottom: 0; - left: 65px; + left: 73px; right: 0; font-family: Raleway-Regular; - font-weight: 400; + font-weight: 500; + padding-left: 8px; + color: white; } /* // END styling of share overlay diff --git a/scripts/system/html/img/blast_icon.svg b/scripts/system/html/img/blast_icon.svg new file mode 100644 index 0000000000..31df8e7f53 --- /dev/null +++ b/scripts/system/html/img/blast_icon.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/scripts/system/html/img/fb_icon.svg b/scripts/system/html/img/fb_icon.svg new file mode 100644 index 0000000000..6d67d17bb2 --- /dev/null +++ b/scripts/system/html/img/fb_icon.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/scripts/system/html/img/fb_logo.png b/scripts/system/html/img/fb_logo.png deleted file mode 100644 index 1de20bacd8fb5719db3291365576d6368a5ff510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1257 zcmbVMTWl0n7(QC5q@|jWMiwtH8D1>y?#%4$&dv;bo1NJ$YuM6kx44mqGMzcw9oo4# zGj*qp1_ML_4>k(n5-tsiiSfl4LTZe`R*D1>$y#dyO^uc(e5f=9v2sbxlwI(l;lY!f z8-82B4;>;C8A*8a< zC=xd10-F_5m0YG=h@eH=HdqWRl}f%6>C?>~j9?hX)$sehrd;q^1DY*5Ud`H2RS=*h znTla6x`w!lVz=IJM^LnCK{Ya&$HJObX_Ta^m?Ih(;lnXib$u-vw(Kl?QpU1qD>q<3 zEDJ5Y-;`h$7B*DLj%qwTkqb038Fs6w^eCFxC+JeY3N#1l72don)SDk?NqHgO}Q8y62gF;@OxAIXydJhLV(a5-nH`|JzDKU^9cYk%%SL;T8keAC>Dovm2? z*1frSCL1C3k&nJYi8fw*!=Cj^TVTe!(4sS$f09vJMZ5g`mMPB z)0@99?y9}F{f&crZ8^C*JwLkmKr;4sU6}j(R_xwCcYeA(h_ W+`9J^=-T9dKIvqe@Ofg#?tcLbL$|sB diff --git a/scripts/system/html/img/fb_logo72.png b/scripts/system/html/img/fb_logo72.png deleted file mode 100644 index 8e3a2370dd2bab99c363f5bf5d06d8e89ee30e5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1587 zcmbVMeM}Q)7(Y=F4GtK@PgstVZElmc*DtOoEwpz9Di*p@uoI(P+oNsD_3HJY2P_-- zfgke&L`)o_Btz%?V#w5>PIM}cIMiS^6#=JNK#Vw*MFjkMhhQ>VDWdcd8RRqJJTrPm6GPxX{2Z}|oSSS(;Me^D9^hK@rfS29!+PQcBh-j!)a?iMY>rXu7o(sBd|Gb zs7Qv0U^q@HiW7_E2n-`4M1oGgM(&iuY>l0ScV*&t0$~Z zHy`+fzo1uoaguQskhUa-rohn&qt3TVqg35ecu>X zL25j?YLCTb-O|&sWMSU$&fJRb>YcTFT9qO3UpzoIg*-j=^wfk)-p7w)V!GPvSo5sX z6B*^Is_t#42C|m-)D@qu9V%I}^*!Ar>0nc8uMz~12&Mbt#aI&RL!bB>J+n41odYc#bKHLUn$@#Gts;TN~(|4`a` zq5j+1dwV_eTLkAT?ka)J7e*qx_Une%u6p6PHGQbxd^MnNNYFV$pYvDVbZOOw>@{XX zSsL>qc&Ny-qwZK|UC)`CYV2jy_e9B|rAXNbH)+{G!MdYm%c+=4`Q@K}*IRtMvUPi+ z{L%B1h;Jx#QTx7sqBcfHJPb>z3h!z-e(UU}rIVr**Y7>+tS=1DNbk-O94WcHEI6W# z85rNc8GXL;!^+R@44n(kI_!Ed%TiI^IM{oJy|?Sn`&?~KqL1yqXzHk`{ORh{zV`LE z9~^8cxN*HRCiI$mbQb4A_eMXl2=}zyEBLuMr1z2IiEI~`sj6#?v>bW-Rdh0#`I~WK z>h^%rt!G{EnuM;SO;he#(aYDqY#X%0skI&0hV(1)`e%`)ropU{P3af+Z*17|!Aa(2 z{h{)jrJIZ4fUvcRe4!bBCkO}2Mk7Hu7!daB?}>pkx5D#k?X01vX`(c`?W!epUS;;Q sE7^S|?6hW8Memb{eba`wwS|le1lD8gSC&6Z!uW4or%l5TX>wNl3u=i}%K!iX diff --git a/scripts/system/html/img/hifi_icon.svg b/scripts/system/html/img/hifi_icon.svg new file mode 100644 index 0000000000..acbb98a3b3 --- /dev/null +++ b/scripts/system/html/img/hifi_icon.svg @@ -0,0 +1,19 @@ + + + + + + + diff --git a/scripts/system/html/img/shareIcon.png b/scripts/system/html/img/shareIcon.png deleted file mode 100644 index 0486ac920222964fc18287bd03131bc70ed942bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15201 zcmeI3TZ|J`7{|{F5-IM!pkbp3!>mRyPUq6=jMFYlyU<3uuFINTObkqCPP-G@nPFzS z?d}u0(FBA@l&l7fl1PX?NRS6_5Wx_m5i}A*P*CK-#E1%-5LbvsGrhOF-RueB#hhQ# zK78N#f8U({xlCWWy}xf=yXzho0H8e{iw*z)I-SoQ3jhF@@}a)~01J)SMhk#Ni=9s! z*!yZH0Q|4&;IKWMSTD$W)-5S|61odn12~A?1w)dzK$}Ryw5o;3t4EKMgsOzdAzy+? z7!f$4#>P#!VZ3ip9^WGK3fUcYi3OpMHL}o_h(b1_SwbO17V8R{B{*T4B#J}qEg`bU zX-Etw`iY2cLc-_vQ!?ub5&_=L`uHH{eVE`F)=x7$&3Y+@6&PM%SfUIWb`b<2no3F- zh^{TSGnG_`9I>ll3fJkPMo2Gjt14A~Cxxo1IZ<*}bFo>`H5J z%z*R&wDg=Q!`@M-*`q?#MxB=V&#yk zDSF;oCDP53+IrWj!Kh@zu%o0HFU9!>S)btJ1h0=`0s_O-6)BBus*!-Uq)4_jmnsco zo9pO`ni`u^mD z&&HEgxe%tGL{X82lx}7vJFI4r~Fm z$+;z$%@4nRzs|L{IGM{Gr8jU0LL&4L$l6zSz4cNmTsI~TAyx~)|5|ZsvYLa z-I~I6D(4Man)L=ll402I^Lcz?@x@vnZdUZG1(+F*s#&O6&V9vs%bMoIjKYnrgmsCs zu(TEGewGyzrRYF(ZP+E!)gPP{&8?+2qgx1Wf&{h;32YY<*e)cnT}WWNkid2!f$c&9 z+l2(S3khr&64)*zuw6)CyO6+kA%X2e0^5ZIwhIYt7ZTVmB(PmbV7rjOb|Hc7LIT@` z1hxwaY!?#PE+nvBNMO5=z;+>l?Lq?Eg#@+>32YY<*e)cnT}WWNkid2!F{@p!+Fv-K zri92m*&Y7qaNr;SfRJMY2>`|)0ASk$0N4KoVB60C@K3jnwp zkM;}}&L5i$bxbgKKHL4_DBK^>=ZEfo^uk-;zrViYtBcQdpIPazUb@M4o2UV zyDxnFET7E(;a(aLp!HVuUXpp^t{Ep+SfgPU~B)pPj@|X zwk`DK>J?1K{vE-#Wvd^IeX*O|{$29Y07Op|9@#PE+O?#wXJ6;kTD-d+)JR UXL=U)Ie&tU_x42(t=as{e=9bQEdT%j diff --git a/scripts/system/html/img/shareToFeed.png b/scripts/system/html/img/shareToFeed.png deleted file mode 100644 index f681c49d8f83a3f77ec8db52f62ff2bd1e6ee628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15486 zcmeI3ZEO_B8OH}h;@XV~#K_=)(De}10rvLg-n^^3a~z+I-E!tIZn0Y+-rJqCx4zrm z?yh}j%OR&yC{C=pA*!IHp$$@6wIz{h(S%v{3E6 z_+D%iMo~UQnxABUn3@0c?EL3>X79@m2AfxvmEK(n04Qr}^tS*2EC7H-U%DLt0FFFw z1pt(&jqN%BUoJQQW`Ny4T>t?4rqtSIv;|gkf)cgyq7s6(cvLk-z*7@fd7&E`mJsZe zWUuvK2mfrfNTS!e-Wi|*Y8~v78v8Z4uD`ie=zp}ES}n;XMz_~mYbLa`1%j43MS~Wn%|!^bz1retZM2iEW*jRm3`M(0iX~|W zLD3w=aujVzWA&9FG&T-#PLI2&?ME66xm|NPn9$!1`V&8%w5o2vL}bqxNW#q(7oS!U&8) z+0aSaMo;+&YL{XtdY3ZIz9*)q-Mt}&vxk%CnK?79492BtWadmtmMn!7o_JK{>NLn3 ziq@(q5nrmxf|;kc)YX|)<+fCGycZLP_?#v(ccRH=xf{5|o2rT&=W=bGQh~ z%~4cNgw(Qv00C(5B5&}Y8X|9DVGu=>!hN3}BD*X&J8QJHTe^L~J`!6*BY_9Wj>d1Hm^+sr-om$?!4hYz4zPD)W_@+Vwh>7ogAOblRPs3q&0;TQwrO&LZ&)bm@|FHQM5D@RfSViC>r*&F4#Y zN1r1&2@=>YB(PmbV7rjOb|Hc7LIT@`1hxwaY!?#PE+nvBNMO5=z;+>l?Lq?Eg#@+> z32YY<*e)cnT}WWNkid2!f$c&9+l2(S3khr&64)*zuw6)CyO6+kA%X2e0^5ZIwhIYt z7ZTVmB(PmbV7rjOb|Hc7LIT@`#7*rg&3>~9WzlPmS!;YN7oKi3KL-^WTLJ*|F9jg6 z1%PWe07$$GKo14LdmR99y8xK4?706}0{}$}oBXw{@z>u!>+Nc-om2epw{BZobZz+qU??!Ptvi9hLDn4!!pHPi~w%aXohb6B}&bUL);4_1hU8>%Mu$`H|;= zHy`T0cyw{(#dl*Rk1lv&&gS*YF8pWBg;V#IyhSJeS2k;Y_nl*|UmRUux3r3%_jurM z+N!I|-??q1Y&f|6<6lu9+_CZN``9gqvFX`jl?@(sFFLYe ztgF~IR@A#~^Xh+!byN3__aOiqHW4kcpN4=j6gr82la(CI;-5)jASM0cS;pMx| zghrp)J#b=Hge$jo6fLQB-E(Q^e0yR8TmH%;57stsxjJ<2^n=^azqCJm5-h5%neF$k zsC@s}(t(oq9(cY2%pT{%JH;m#eo}PevGA3@$akDA*&~m?J<>im@#Kj^^Y@*7>E1u| zA8FXOtKu@)wfguAV;}#wboLVuogDPLAN^(V@$ssy2M_erMATX;^=I;|6j0zW$m!6R%wyJM;6G<)NoOY!1%;+3)&dhZmf3e(g8T zS1(6?U$eG*p>I&QviQ)LGPCx^`SK6GKm6;;m63s6``n2eGr``uQv0)ehPRnt2shO? L`=7qA<9q)H#YG)p diff --git a/scripts/system/html/img/twitter_icon.svg b/scripts/system/html/img/twitter_icon.svg new file mode 100644 index 0000000000..0393d963f2 --- /dev/null +++ b/scripts/system/html/img/twitter_icon.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/scripts/system/html/img/twitter_logo.png b/scripts/system/html/img/twitter_logo.png deleted file mode 100644 index 59fd027c2a59cffb4c4f353dbc0ddce2cc3e92d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 552 zcmV+@0@wYCP)ca5h;T6e`N!ZW%_7Fh zh-L^Db6)RjfuD}F`pZz7)+*=?1MeO>1_Vr(aX5V=K z@A6xs&1GO<5aVQ2;b%fmI~ZW%B^;rP%iP^h{_K772fv|vpZwYO5T_dK=KlZBP{N0|RFI72{;g)@5^- zWs&5@629+${-1W`*WByB{$NjC2*8N7-ZT_pc9Uh%6JeI*Vq|7y`0(@p?T`PCJpa4v q@gE|SAa-*p2ScsC7&Vu+<^lkzl%{SoyW=zf00003WXL}mU6Q! zd!4HfH9!}ZopW{;GGxBDo$Sm%-<)&)^UuGFHrD%LHNvd)4WtS~Om7%sdczRY8-|$P zFvRqRA*MGBF}<;B#n_b%@8D2=I+I~RVFCL6vNtGeb>)>2!w4!p?EWHdXBKAA-0=aw z+|Va{`?nAfl$cZQ&BpzHc_I>fJRXR&HBgqzwvLKI5>M7?Lm!t~OId8ViY2iu9=EEsAl5?I~BjG<-+H~aDH z9=_2l0ssO~8Ivq4?#%vw{!|s3J}j|tggLc(E4M9^>>LH4u7^L|8NJVm4;Gv6bf`U@ zEKII#ZlyoF#dPdpn{(cgr7>fuF5(h@?{>TaxgZ_?`ApjJy|&??a&=nm;wBSAw_Dur z#6LZ5-@8%Od@jjhGc9VL!Ymuom{J#ZaHy__uj}U3pGYP)8j79aSyohAVLe)I-IQqM z+`XQ%7*GI?UgqCD7bRNJWXYp*LRxqb0X*hm_qg%#KC!l&zdp10Y{|CLhzT;h-4^-2 zcfr1v2(n;JBhoa#cnC8_FBX`7f689J$NJ!@}I2nb38xfPI(SV&aruL$!K zaSRNC3O8HfX2T+MPDtm*rLIY--!FTE%6M3_a!C!uCDrF$e5HrYPfsw^Ru)Qfa7oTw zc_C7H1!+bLfOINu%fy1LRR0$Cg@|vHrOF-ulBJ9}$fDKk;F_McR~K;&y#n>KB*Gh{ zj6yaQug}Pp9`>bz<$H_kRlOs}Kz55~M;A%=DWz+YG9sB+vrinMof{O0GIWeDiZQ+MS&9o^igQ? zTSS6a8pH2(L>G1@G^EKV4#p`v)j*lJIR?Jz;dh@59`1}zs&25TM**nqPHMtacBzV; z>{sPhURKgf2ot|#C8qs`)Z$BOwo{H+i%q@5F#m-56HiolOWYK}~bSALm_` zh-AyzGhT7`$>67#_~hH}vH}O%BFRs}TM)On@0dA-&9pdWu?IT=c8YAX=VkQh^@DveR-yri@0^X)u1P%D;gMXzn=wq{@x0}eBC*^re* z$!yM13F>^&?kTB#O!~to zbxlc{S1R$)W*@@{Tz1B3MQb@^#vm#X5@0GKkAxIW$_NUy*~cUWh6D0&K$DAU!>awC zhasjn3^Bc7i0KVOOm7%sdczRY8-|$PShZsR0V1^GX^jP^B>(^b07*qoM6N<$f{Z' + ''; shareBar.innerHTML = shareBarInnerHTML; @@ -214,16 +214,16 @@ function showUploadingMessage(selectedID, destination) { var socialIcon = document.createElement("img"); switch (destination) { case 'blast': - socialIcon.src = "img/shareIcon.png"; + socialIcon.src = "img/blast_icon.svg"; break; case 'hifi': - socialIcon.src = "img/shareToFeed.png"; + socialIcon.src = "img/hifi_icon.svg"; break; case 'facebook': - socialIcon.src = "img/fb_logo72.png"; + socialIcon.src = "img/fb_icon.svg"; break; case 'twitter': - socialIcon.src = "img/twitter_logo72.png"; + socialIcon.src = "img/twitter_icon.svg"; break; } uploadingMessage.appendChild(socialIcon); @@ -330,20 +330,34 @@ function shareButtonHovered(destination, selectedID) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes, + itr; + + for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { + shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; + } switch (destination) { case 'blast': shareBarHelp.style.backgroundColor = "#EA4C5F"; + shareBarHelp.innerHTML = "Blast to my connections"; + document.getElementById(selectedID + "blastToConnectionsButton").style.backgroundColor = "#EA4C5F"; break; case 'hifi': shareBarHelp.style.backgroundColor = "#1FC6A6"; + shareBarHelp.innerHTML = "Share in Snapshots Feed"; + document.getElementById(selectedID + "shareWithEveryoneButton").style.backgroundColor = "#1FC6A6"; break; case 'facebook': shareBarHelp.style.backgroundColor = "#3C58A0"; + shareBarHelp.innerHTML = "Share on Facebook"; + document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; break; case 'twitter': shareBarHelp.style.backgroundColor = "#00B4EE"; + shareBarHelp.innerHTML = "Share on Twitter"; + document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; break; } } From 5d38cd543d8f48f3bc71f37aec5844dd22484d7e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 16:06:13 -0700 Subject: [PATCH 182/292] Yet another checkpoint --- scripts/system/html/js/SnapshotReview.js | 59 +++++++++--------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 117e0d568f..beae7d0884 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -15,6 +15,10 @@ var paths = []; var idCounter = 0; var imageCount = 0; +var blastShareText = "Blast to my Connections", + hifiShareText = "Share to Snaps Feed", + facebookShareText = "Share to Facebook", + twitterShareText = "Share to Twitter"; function showSetupInstructions() { var snapshotImagesDiv = document.getElementById("snapshot-images"); snapshotImagesDiv.className = "snapshotInstructions"; @@ -206,59 +210,42 @@ function showUploadingMessage(selectedID, destination) { selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID } - var uploadingMessage = document.createElement("div"); - uploadingMessage.id = selectedID + "uploadingMessage"; - uploadingMessage.className = "uploadingMessage"; - uploadingMessage.setAttribute("data-destination", destination); + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); - var socialIcon = document.createElement("img"); - switch (destination) { - case 'blast': - socialIcon.src = "img/blast_icon.svg"; - break; - case 'hifi': - socialIcon.src = "img/hifi_icon.svg"; - break; - case 'facebook': - socialIcon.src = "img/fb_icon.svg"; - break; - case 'twitter': - socialIcon.src = "img/twitter_icon.svg"; - break; - } - uploadingMessage.appendChild(socialIcon); - - uploadingMessage.innerHTML += 'Preparing Snapshot for Sharing...'; - - document.getElementById(selectedID).appendChild(uploadingMessage); + shareBarHelp.innerHTML = "...Preparing to Share"; + shareBarHelp.setAttribute("data-destination", destination); } -function hideUploadingMessage(selectedID, storyID) { +function hideUploadingMessageAndShare(selectedID, storyID) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID } - var uploadingMessage = document.getElementById(selectedID + "uploadingMessage"); - if (uploadingMessage) { - uploadingMessage.remove(); - var destination = uploadingMessage.getAttribute("data-destination"); - - switch (destination) { + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + shareBarHelpDestination = shareBarHelp.getAttribute("data-destination"); + if (shareBarHelpDestination) { + switch (shareBarHelpDestination) { case 'blast': blastToConnections(selectedID, selectedID === "p1"); + shareBarHelp.innerHTML = blastShareText; break; case 'hifi': shareWithEveryone(selectedID, selectedID === "p1"); + shareBarHelp.innerHTML = hifiShareText; break; case 'facebook': var facebookButton = document.getElementById(selectedID + "facebookButton"); window.open(facebookButton.getAttribute("href"), "_blank"); + shareBarHelp.innerHTML = facebookShareText; break; case 'twitter': var twitterButton = document.getElementById(selectedID + "twitterButton"); window.open(twitterButton.getAttribute("href"), "_blank"); + shareBarHelp.innerHTML = twitterShareText; break; } + shareBarHelp.setAttribute("data-destination", ""); + EventBridge.emitWebEvent(JSON.stringify({ type: "snapshot", action: "removeFromStoryIDsToMaybeDelete", @@ -284,7 +271,7 @@ function updateShareInfo(containerID, storyID) { twitterButton.setAttribute("target", "_blank"); twitterButton.setAttribute("href", 'https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi'); - hideUploadingMessage(containerID, storyID); + hideUploadingMessageAndShare(containerID, storyID); } function blastToConnections(selectedID, isGif) { if (selectedID.id) { @@ -341,22 +328,22 @@ function shareButtonHovered(destination, selectedID) { switch (destination) { case 'blast': shareBarHelp.style.backgroundColor = "#EA4C5F"; - shareBarHelp.innerHTML = "Blast to my connections"; + shareBarHelp.innerHTML = blastShareText; document.getElementById(selectedID + "blastToConnectionsButton").style.backgroundColor = "#EA4C5F"; break; case 'hifi': shareBarHelp.style.backgroundColor = "#1FC6A6"; - shareBarHelp.innerHTML = "Share in Snapshots Feed"; + shareBarHelp.innerHTML = hifiShareText; document.getElementById(selectedID + "shareWithEveryoneButton").style.backgroundColor = "#1FC6A6"; break; case 'facebook': shareBarHelp.style.backgroundColor = "#3C58A0"; - shareBarHelp.innerHTML = "Share on Facebook"; + shareBarHelp.innerHTML = facebookShareText; document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; break; case 'twitter': shareBarHelp.style.backgroundColor = "#00B4EE"; - shareBarHelp.innerHTML = "Share on Twitter"; + shareBarHelp.innerHTML = twitterShareText; document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; break; } From 0cd0e46ca5a42835d6e9bb742cf917d7417c1bca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 May 2017 16:20:59 -0700 Subject: [PATCH 183/292] 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 15aa52b5c0..db0840b078 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 dc50db7ee5f0fa59d68f4604b96af6dcb31416a3 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 16:27:28 -0700 Subject: [PATCH 184/292] One more warning from gcc --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 796abda5a3..527b503571 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -395,7 +395,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t sourceLevel = mip - srcMipOffset; - for (GLuint f = 0; f < faceTargets.size(); f++) { + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (sourceMip._offset + f * sourceMip._faceSize)); } (void)CHECK_GL_ERROR(); @@ -414,7 +414,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t destLevel = mip - destMipOffset; - for (GLuint f = 0; f < faceTargets.size(); f++) { + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { #ifdef DEBUG_COPY GLint destWidth, destHeight, destSize; glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); From 68fa8e06ef9eca7e47ebc1642003c34fb9b9f90b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 16:38:16 -0700 Subject: [PATCH 185/292] Super amped about this. Only need to add disabled state? --- scripts/system/html/css/SnapshotReview.css | 26 +++++----- scripts/system/html/js/SnapshotReview.js | 55 +++++++++++++++++++--- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index c0d5991e0b..b03b748082 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -170,12 +170,6 @@ input[type=button].naked:active { height: 40px; display: inline-block; } -.blastToConnections { - background-image: url(../img/fb_logo.png); -} -.shareWithEveryone { - background: #DDDDDD url(../img/shareToFeed.png) no-repeat scroll center; -} .shareControlsHelp { height: 25px; line-height: 25px; @@ -193,22 +187,32 @@ input[type=button].naked:active { */ /* -// START styling of uploading message +// START styling of confirmation message */ -.uploadingMessage { +.confirmationMessageContainer { width: 100%; height: 100%; position: absolute; + background-color: rgba(0, 0, 0, 0.45); text-align: center; - background-color: rgba(0, 0, 0, 0.8); left: 0; top: 0; + pointer-events: none; + color: white; + font-weight: bold; + font-size: 16px; } -.uploadingMessage > img { +.confirmationMessage { + width: 130px; + height: 130px; + margin: 50px auto 0 auto; +} +.confirmationMessage > img { width: 72px; height: 72px; display: block; - margin: 60px auto 10px auto; + margin: 0 auto; + padding: 10px 0 0 0; } /* // END styling of uploading message diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index beae7d0884..c2577bb8c0 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -140,9 +140,9 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled '
    ' + ''; @@ -205,6 +205,45 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b updateShareInfo(id, image_data.story_id); } } +function showConfirmationMessage(selectedID, destination) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + + var opacity = 2.0, + confirmationMessageContainer = document.createElement("div"), + confirmationMessage = document.createElement("div"); + confirmationMessageContainer.className = "confirmationMessageContainer"; + + confirmationMessage.className = "confirmationMessage"; + + var socialIcon = document.createElement("img"); + switch (destination) { + case 'blast': + socialIcon.src = "img/blast_icon.svg"; + confirmationMessage.appendChild(socialIcon); + confirmationMessage.innerHTML += 'Blast Sent!'; + confirmationMessage.style.backgroundColor = "#EA4C5F"; + break; + case 'hifi': + socialIcon.src = "img/hifi_icon.svg"; + confirmationMessage.appendChild(socialIcon); + confirmationMessage.innerHTML += 'Snap Shared!'; + confirmationMessage.style.backgroundColor = "#1FC6A6"; + break; + } + + confirmationMessageContainer.appendChild(confirmationMessage); + document.getElementById(selectedID).appendChild(confirmationMessageContainer); + + setInterval(function () { + if (opacity <= 0.05) { + confirmationMessageContainer.remove(); + } + opacity -= 0.05; + confirmationMessageContainer.style.opacity = opacity; + }, 50); +} function showUploadingMessage(selectedID, destination) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID @@ -278,7 +317,7 @@ function blastToConnections(selectedID, isGif) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(selectedID + "blastToConnectionsButton").disabled = true; + document.getElementById(selectedID + "blastToConnectionsButton").onclick = function () { }; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -289,6 +328,7 @@ function blastToConnections(selectedID, isGif) { story_id: storyID, isGif: isGif })); + showConfirmationMessage(selectedID, 'blast'); } else { showUploadingMessage(selectedID, 'blast'); } @@ -298,7 +338,7 @@ function shareWithEveryone(selectedID, isGif) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true; + document.getElementById(selectedID + "shareWithEveryoneButton").onclick = function () { }; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -309,6 +349,7 @@ function shareWithEveryone(selectedID, isGif) { story_id: storyID, isGif: isGif })); + showConfirmationMessage(selectedID, 'hifi'); } else { showUploadingMessage(selectedID, 'hifi'); } @@ -384,7 +425,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(1); + //testInBrowser(2); 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) { @@ -492,7 +533,7 @@ function testInBrowser(test) { } 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); - showUploadingMessage("p0", 'facebook'); - showUploadingMessage("p1", 'twitter'); + showConfirmationMessage("p0", 'blast'); + showConfirmationMessage("p1", 'hifi'); } } From 964e79ce14c728263e5840680e2c7e5f422f59b8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 16:58:33 -0700 Subject: [PATCH 186/292] It's working! --- scripts/system/html/js/SnapshotReview.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index c2577bb8c0..11502c3d81 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -142,7 +142,7 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled if (canBlast) { shareBarInnerHTML += ''; } - shareBarInnerHTML += '' + + shareBarInnerHTML += '' + '' + '' + ''; @@ -163,6 +163,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); + } } } function shareForUrl(selectedID) { @@ -179,7 +184,7 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b var id = "p" + (idCounter++), imageContainer = document.createElement("DIV"), img = document.createElement("IMG"), - isGif = img.src.split('.').pop().toLowerCase() === "gif"; + isGif; imageContainer.id = id; imageContainer.style.width = "95%"; imageContainer.style.height = "240px"; @@ -190,6 +195,7 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b imageContainer.style.position = "relative"; img.id = id + "img"; img.src = image_data.localPath; + isGif = img.src.split('.').pop().toLowerCase() === "gif"; imageContainer.appendChild(img); document.getElementById("snapshot-images").appendChild(imageContainer); paths.push(image_data.localPath); @@ -425,7 +431,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(2); + testInBrowser(1); 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) { From ad8d3e549045846d10e0cf17048ca0eaf2fb82f7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 19:57:09 -0400 Subject: [PATCH 187/292] 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 1e3df955ba..64b18ec522 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2526,6 +2526,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; } @@ -6491,10 +6496,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 aa46534b6075f265c733d33c981586dd6454bde9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 17:10:57 -0700 Subject: [PATCH 188/292] Whoops. --- scripts/system/html/js/SnapshotReview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 11502c3d81..e3632b5105 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -431,7 +431,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(1); + //testInBrowser(1); 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) { From 0ecf599267f76c0a71dfea3bccd8bfbc22942b32 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 17:19:22 -0700 Subject: [PATCH 189/292] Adressing the warning --- libraries/gpu-gl/src/gpu/gl/GLShared.h | 2 ++ libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index 22f63363d1..1b898e5c22 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -18,6 +18,8 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) +#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) + namespace gpu { namespace gl { // Create a fence and inject a GPU wait on the fence diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 527b503571..c2cef5a21e 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -396,7 +396,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui uint16_t sourceLevel = mip - srcMipOffset; for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { - glGetCompressedTexImage(faceTargets[f], sourceLevel, (void*) (sourceMip._offset + f * sourceMip._faceSize)); + glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); } (void)CHECK_GL_ERROR(); } @@ -422,7 +422,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); #endif glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, - sourceMip._faceSize, (void*)(sourceMip._offset + f * sourceMip._faceSize)); + sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); gpu::gl::checkGLError(); } } From 653dda70003353711c2c745e75bfafcbc7f7ae09 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 17:21:50 -0700 Subject: [PATCH 190/292] Fix pausing after snapshot in HMD --- scripts/system/snapshot.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 966f43cc55..b93595f0b4 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -393,7 +393,9 @@ function takeSnapshot() { resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked. reticleVisible = Reticle.visible; Reticle.visible = false; - Reticle.allowMouseCapture = false; + if (!HMD.active) { + Reticle.allowMouseCapture = false; + } var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true); if (includeAnimated) { From 84305c20498fe5ceda8fea22b214770c3b5f83cc Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 17:30:31 -0700 Subject: [PATCH 191/292] 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 ff3a76f7162b115df97eac1a6b216e1f4010e401 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 2 May 2017 18:23:15 -0700 Subject: [PATCH 192/292] No magic numbers! --- scripts/system/html/js/SnapshotReview.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index e3632b5105..99364169f1 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -217,6 +217,8 @@ function showConfirmationMessage(selectedID, destination) { } var opacity = 2.0, + fadeRate = 0.05, + timeBetweenFadesMS = 50, confirmationMessageContainer = document.createElement("div"), confirmationMessage = document.createElement("div"); confirmationMessageContainer.className = "confirmationMessageContainer"; @@ -243,12 +245,12 @@ function showConfirmationMessage(selectedID, destination) { document.getElementById(selectedID).appendChild(confirmationMessageContainer); setInterval(function () { - if (opacity <= 0.05) { + if (opacity <= fadeRate) { confirmationMessageContainer.remove(); } - opacity -= 0.05; + opacity -= fadeRate; confirmationMessageContainer.style.opacity = opacity; - }, 50); + }, timeBetweenFadesMS); } function showUploadingMessage(selectedID, destination) { if (selectedID.id) { From 7b387c52c37cd0a777e9a408869811c2d6a908bd Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 3 May 2017 14:42:06 +1200 Subject: [PATCH 193/292] 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 7fdcb9b1f005e9cabf3fa834c97197672074c251 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 3 May 2017 14:59:41 +1200 Subject: [PATCH 194/292] Fix numeric keys not displaying in social media 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 04e784e2ba..1d8aa479a3 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -297,6 +297,7 @@ Item { HiFiControls.Keyboard { id: keyboard raised: parent.keyboardEnabled && parent.keyboardRaised + numeric: parent.punctuationMode anchors { left: parent.left From 15db80c8ba7420aff0eb2f9c51c6fdc86455bf33 Mon Sep 17 00:00:00 2001 From: CFresquet Date: Wed, 3 May 2017 07:32:23 -0700 Subject: [PATCH 195/292] request module updates --- scripts/modules/request.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/modules/request.js b/scripts/modules/request.js index a5c419892e..c7bf98d815 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -17,11 +17,6 @@ // =========================================================================================== module.exports = { - // ------------------------------------------------------------------ - test: function () { - debug("Test completed."); - }, - // ------------------------------------------------------------------ request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. var httpRequest = new XMLHttpRequest(), key; From b54ea63af7dfbe082afd3dd550dd2d95ed978658 Mon Sep 17 00:00:00 2001 From: CFresquet Date: Wed, 3 May 2017 07:35:30 -0700 Subject: [PATCH 196/292] incremental request module fixes --- scripts/system/makeUserConnection.js | 3 +- scripts/system/pal.js | 3 +- scripts/system/tablet-goto.js | 54 ++++------------------------ 3 files changed, 10 insertions(+), 50 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 4426ce5c24..beee766d5c 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -14,7 +14,8 @@ (function() { // BEGIN LOCAL_SCOPE - var request = Script.require('../modules/request.js').request; + var REQUEST_URL = Script.resolvePath('request.js'); + var request = Script.require(REQUEST_URL).request; var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ffde058cfc..d81946562b 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -14,7 +14,8 @@ (function() { // BEGIN LOCAL_SCOPE - var request = Script.require('../modules/request.js').request; + var REQUEST_URL = Script.resolvePath('request.js'); + var request = Script.require(REQUEST_URL).request; var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index fec7a6de90..68706ffb68 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -14,6 +14,10 @@ // (function () { // BEGIN LOCAL_SCOPE + + var REQUEST_URL = Script.resolvePath('request.js'); + var request = Script.require(REQUEST_URL).request; + var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; var onGotoScreen = false; @@ -30,54 +34,7 @@ text: buttonName, sortOrder: 8 }); - 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); - } + function fromQml(message) { var response = {id: message.id, jsonrpc: "2.0"}; switch (message.method) { @@ -99,6 +56,7 @@ // No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway. }); } + var hasEventBridge = false; function wireEventBridge(on) { if (on) { From 28b166c51fd74201502a1af0622795817bd95331 Mon Sep 17 00:00:00 2001 From: CFresquet Date: Wed, 3 May 2017 07:51:49 -0700 Subject: [PATCH 197/292] further request fiddling --- scripts/system/makeUserConnection.js | 3 +-- scripts/system/pal.js | 3 +-- scripts/system/tablet-goto.js | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index beee766d5c..34100806b5 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -14,8 +14,7 @@ (function() { // BEGIN LOCAL_SCOPE - var REQUEST_URL = Script.resolvePath('request.js'); - var request = Script.require(REQUEST_URL).request; + var request = Script.require('request').request; var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d81946562b..9229ec772a 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -14,8 +14,7 @@ (function() { // BEGIN LOCAL_SCOPE - var REQUEST_URL = Script.resolvePath('request.js'); - var request = Script.require(REQUEST_URL).request; + var request = Script.require('request').request; var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 68706ffb68..4e023f5460 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -15,8 +15,7 @@ (function () { // BEGIN LOCAL_SCOPE - var REQUEST_URL = Script.resolvePath('request.js'); - var request = Script.require(REQUEST_URL).request; + var request = Script.require('request').request; var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; From 8e12c1959d352c2ae456e313f6001f52e43416ab Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 3 May 2017 09:53:32 -0700 Subject: [PATCH 198/292] 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 199/292] 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 200/292] 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 201/292] 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 afa72210e0bb594700323393d5d0d8ddf9d3f8d8 Mon Sep 17 00:00:00 2001 From: CFresquet Date: Wed, 3 May 2017 10:28:44 -0700 Subject: [PATCH 202/292] requested tweaks to makeUserConnection.js --- scripts/system/makeUserConnection.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 34100806b5..60e289d2cf 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE var request = Script.require('request').request; @@ -126,10 +126,6 @@ [].map.call(arguments, JSON.stringify))); } - function debugPrint() { - print( [].slice.call(arguments).join(' ') ); - } - function cleanId(guidWithCurlyBraces) { return guidWithCurlyBraces.slice(1, -1); } From 09488c49873290acf818060d76f3fafaa5532262 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 11:02:44 -0700 Subject: [PATCH 203/292] Disabled state for share butotns --- scripts/system/html/css/SnapshotReview.css | 4 ++ scripts/system/html/img/loader.gif | Bin 0 -> 21466 bytes scripts/system/html/js/SnapshotReview.js | 67 +++++++++++++++++---- 3 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 scripts/system/html/img/loader.gif diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index b03b748082..e327ab75e9 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -170,6 +170,10 @@ input[type=button].naked:active { height: 40px; display: inline-block; } +.shareButton.disabled { + background-color: #000000; + opacity: 0.5; +} .shareControlsHelp { height: 25px; line-height: 25px; diff --git a/scripts/system/html/img/loader.gif b/scripts/system/html/img/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..c464703c848bb5b00f67d5105653f6681385c2b8 GIT binary patch literal 21466 zcmeI4_fyk%7{|kuB|`*}p)5i6h#*ry_LRN1Ajnii?mW-JRjDUEMQ9lj3JA7L!O#$i zfDj|dQcyqy!%&8tavTQ;;z}>U&~H`Rp4O5(p4VTHUtW2Cp7-bZd@ZdlbaaCxQIe=7 z6cr{WCT3=478Vv(R#rAPHgS%y}g5j!+`?_9335)6;+YYJUDwGBPrcxu}Gcz-@v$Jz^bMy1_3kwS`U%p&i zTwGdOdiCnn^78WQ*RS8adGnUWJ38;zfI^{2QMet-| z7o|*lkNZ8^lZY6#COU{C>-&)s`Cr>6NC7n5Q=G?h+=g*%{O-=oJ zW4}~YDj5heGc&WYva++YFI~EnpPye;R8(ACTv1U`RaJ$<;c9DZSMTZnPbOk7D@V3^3S4QP8EK4R55l0WXEIE@ zD7LNv*)aZG`|q_WHA*520bThlr+L{#7eYHRERij@tsTSc^4N`j5cir3Y0sc|Cy3kP zD+YcK?(^^*YY7CxjT<-W>gww2>l+$A_LM}T8rs}URr1aqs(@{6pHUB)pvd?fps|{u zt09Wa(ed%|XV0EZPfyRz&d$$MO+*IgN_PGm6IY=sc?xCt_u-f(r4mnhf8587-<@!A zB2dV;PeoSGn{_Ce$NK7M8)kybIcK>`!X(d-oB5rGqg+?M!6|7&QNi#Yj27pxSt(k9 zm?~wNS&xYe?MPJ9r-kDOB{Kt!KZGP0`G1~i87(A{fRSM2#+x$1$hD?S$VhrdB1T#` zwSMzcEA&2V8OaE}4^%oEr-5a(Y(z9VQ0YLWvk@;v7)?neFhIfp2?OMM-+LedBf&^8 z5{#rT4nU;?l@6@(&~^i0m1hO&MI>zLY?!4wMw=g@(pd$zbT;OmE2Ali1O`YLAYp)n z0TKqtl~q>Qn_Hi^Adr!3?F2xj1C;M1&00000008{g`~jd&y@&t+ literal 0 HcmV?d00001 diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 99364169f1..e3417893dd 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -16,7 +16,9 @@ var paths = []; var idCounter = 0; var imageCount = 0; var blastShareText = "Blast to my Connections", + blastAlreadySharedText = "Already Blasted to Connections", hifiShareText = "Share to Snaps Feed", + hifiAlreadySharedText = "Already Shared to Snaps Feed", facebookShareText = "Share to Facebook", twitterShareText = "Share to Twitter"; function showSetupInstructions() { @@ -140,16 +142,16 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled '' + ''; shareBar.innerHTML = shareBarInnerHTML; - shareBar.innerHTML += '
    '; + shareBar.innerHTML += ''; // Add onclick handler to parent DIV's img to toggle share buttons document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; @@ -273,11 +275,11 @@ function hideUploadingMessageAndShare(selectedID, storyID) { switch (shareBarHelpDestination) { case 'blast': blastToConnections(selectedID, selectedID === "p1"); - shareBarHelp.innerHTML = blastShareText; + shareBarHelp.innerHTML = blastAlreadySharedText; break; case 'hifi': shareWithEveryone(selectedID, selectedID === "p1"); - shareBarHelp.innerHTML = hifiShareText; + shareBarHelp.innerHTML = hifiAlreadySharedText; break; case 'facebook': var facebookButton = document.getElementById(selectedID + "facebookButton"); @@ -325,7 +327,15 @@ function blastToConnections(selectedID, isGif) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(selectedID + "blastToConnectionsButton").onclick = function () { }; + var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"), + shareBar = document.getElementById(selectedID + "shareBar"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + blastToConnectionsButton.onclick = function () { }; + blastToConnectionsButton.classList.add("disabled"); + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -346,7 +356,15 @@ function shareWithEveryone(selectedID, isGif) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(selectedID + "shareWithEveryoneButton").onclick = function () { }; + var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"), + shareBar = document.getElementById(selectedID + "shareBar"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + shareWithEveryoneButton.onclick = function () { }; + shareWithEveryoneButton.classList.add("disabled"); + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -373,17 +391,40 @@ function shareButtonHovered(destination, selectedID) { 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': - shareBarHelp.style.backgroundColor = "#EA4C5F"; - shareBarHelp.innerHTML = blastShareText; - document.getElementById(selectedID + "blastToConnectionsButton").style.backgroundColor = "#EA4C5F"; + 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': - shareBarHelp.style.backgroundColor = "#1FC6A6"; - shareBarHelp.innerHTML = hifiShareText; - document.getElementById(selectedID + "shareWithEveryoneButton").style.backgroundColor = "#1FC6A6"; + 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"; From 0263021c0c5ad88026e1df9e865f2abcd0905085 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 May 2017 10:26:51 -0700 Subject: [PATCH 204/292] 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 205/292] 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 206/292] 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 207/292] 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 208/292] 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 c82a07016d903d949b444f321b8f97c213aa9554 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 11:30:44 -0700 Subject: [PATCH 209/292] New Preparing text; Better disabled behavior --- scripts/system/html/css/SnapshotReview.css | 1 + scripts/system/html/js/SnapshotReview.js | 30 +++++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index e327ab75e9..29c5f465d7 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -183,6 +183,7 @@ input[type=button].naked:active { right: 0; font-family: Raleway-Regular; font-weight: 500; + font-size: 16px; padding-left: 8px; color: white; } diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index e3417893dd..6365c2be18 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -261,7 +261,7 @@ function showUploadingMessage(selectedID, destination) { var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); - shareBarHelp.innerHTML = "...Preparing to Share"; + shareBarHelp.innerHTML = 'Preparing to Share'; shareBarHelp.setAttribute("data-destination", destination); } function hideUploadingMessageAndShare(selectedID, storyID) { @@ -331,11 +331,6 @@ function blastToConnections(selectedID, isGif) { shareBar = document.getElementById(selectedID + "shareBar"), shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); blastToConnectionsButton.onclick = function () { }; - blastToConnectionsButton.classList.add("disabled"); - blastToConnectionsButton.style.backgroundColor = "#000000"; - blastToConnectionsButton.style.opacity = "0.5"; - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -347,6 +342,11 @@ function blastToConnections(selectedID, isGif) { isGif: isGif })); showConfirmationMessage(selectedID, 'blast'); + blastToConnectionsButton.classList.add("disabled"); + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; } else { showUploadingMessage(selectedID, 'blast'); } @@ -360,11 +360,6 @@ function shareWithEveryone(selectedID, isGif) { shareBar = document.getElementById(selectedID + "shareBar"), shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); shareWithEveryoneButton.onclick = function () { }; - shareWithEveryoneButton.classList.add("disabled"); - shareWithEveryoneButton.style.backgroundColor = "#000000"; - shareWithEveryoneButton.style.opacity = "0.5"; - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); @@ -376,6 +371,11 @@ function shareWithEveryone(selectedID, isGif) { isGif: isGif })); showConfirmationMessage(selectedID, 'hifi'); + shareWithEveryoneButton.classList.add("disabled"); + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; } else { showUploadingMessage(selectedID, 'hifi'); } @@ -474,7 +474,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(1); + //testInBrowser(3); 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) { @@ -584,5 +584,11 @@ function testInBrowser(test) { 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); 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); + showUploadingMessage("p0", 'hifi'); } } From 7f6a1be2c7b2d294254d130d291ab2a2d7d44773 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 May 2017 19:57:09 -0400 Subject: [PATCH 210/292] 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 211/292] 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 212/292] 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 213/292] 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 066b2db73f23a2554793b391e77944813b433306 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 14:57:31 -0700 Subject: [PATCH 214/292] Make the change --- .../resources/qml/controls/TabletWebView.qml | 1 + interface/resources/qml/hifi/Pal.qml | 142 ++---------------- 2 files changed, 11 insertions(+), 132 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 04e784e2ba..603623026c 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -308,6 +308,7 @@ Item { Component.onCompleted: { web.isDesktop = (typeof desktop !== "undefined"); address = url; + console.log("FIXME", eventBridge); } Keys.onPressed: { diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 833e641b09..60439cf63f 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -44,6 +44,7 @@ Rectangle { property var activeTab: "nearbyTab"; property bool currentlyEditingDisplayName: false property bool punctuationMode: false; + property var eventBridge; HifiConstants { id: hifi; } @@ -1036,139 +1037,16 @@ Rectangle { } } // Keyboard - Item { - id: webViewContainer; - anchors.fill: parent; - - Rectangle { - id: navigationContainer; - visible: userInfoViewer.visible; - height: 60; - anchors { - top: parent.top; - left: parent.left; - right: parent.right; - } - color: hifi.colors.faintGray; - - Item { - id: backButton - anchors { - top: parent.top; - left: parent.left; - } - height: parent.height - addressBar.height; - width: parent.width/2; - - FiraSansSemiBold { - // Properties - text: "BACK"; - elide: Text.ElideRight; - // Anchors - anchors.fill: parent; - // Text Size - size: 16; - // Text Positioning - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter; - // Style - color: backButtonMouseArea.containsMouse || !userInfoViewer.canGoBack ? hifi.colors.lightGray : hifi.colors.darkGray; - MouseArea { - id: backButtonMouseArea; - anchors.fill: parent - hoverEnabled: enabled - onClicked: { - if (userInfoViewer.canGoBack) { - userInfoViewer.goBack(); - } - } - } - } - } - - Item { - id: closeButtonContainer - anchors { - top: parent.top; - right: parent.right; - } - height: parent.height - addressBar.height; - width: parent.width/2; - - FiraSansSemiBold { - id: closeButton; - // Properties - text: "CLOSE"; - elide: Text.ElideRight; - // Anchors - anchors.fill: parent; - // Text Size - size: 16; - // Text Positioning - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter; - // Style - color: hifi.colors.redHighlight; - MouseArea { - anchors.fill: parent - hoverEnabled: enabled - onClicked: userInfoViewer.visible = false; - onEntered: closeButton.color = hifi.colors.redAccent; - onExited: closeButton.color = hifi.colors.redHighlight; - } - } - } - - Item { - id: addressBar - anchors { - top: closeButtonContainer.bottom; - left: parent.left; - right: parent.right; - } - height: 30; - width: parent.width; - - FiraSansRegular { - // Properties - text: userInfoViewer.url; - elide: Text.ElideRight; - // Anchors - anchors.fill: parent; - anchors.leftMargin: 5; - // Text Size - size: 14; - // Text Positioning - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft; - // Style - color: hifi.colors.lightGray; - } - } - } - - Rectangle { - id: webViewBackground; - color: "white"; - visible: userInfoViewer.visible; - anchors { - top: navigationContainer.bottom; - bottom: parent.bottom; - left: parent.left; - right: parent.right; - } - } - - HifiControls.WebView { - id: userInfoViewer; - anchors { - top: navigationContainer.bottom; - bottom: parent.bottom; - left: parent.left; - right: parent.right; - } - visible: false; + HifiControls.TabletWebView { + eventBridge: pal.eventBridge; + id: userInfoViewer; + anchors { + top: parent.top; + bottom: parent.bottom; + left: parent.left; + right: parent.right; } + visible: false; } // Timer used when selecting nearbyTable rows that aren't yet present in the model From eaf819d11dad99de39e806ad9ae7e7723952fe9a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 14:59:07 -0700 Subject: [PATCH 215/292] Remove FIXME --- interface/resources/qml/controls/TabletWebView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 603623026c..04e784e2ba 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -308,7 +308,6 @@ Item { Component.onCompleted: { web.isDesktop = (typeof desktop !== "undefined"); address = url; - console.log("FIXME", eventBridge); } Keys.onPressed: { From 5390a29859df2d7ce7a19247494b5074190f5be5 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 3 May 2017 23:05:05 +0100 Subject: [PATCH 216/292] tabletwebview-url-fix --- interface/resources/qml/controls/TabletWebView.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 04e784e2ba..05cf307cfb 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -16,7 +16,6 @@ Item { property var parentStackItem: null property int headerHeight: 70 property string url - property alias address: displayUrl.text //for compatibility property string scriptURL property alias eventBridge: eventBridgeWrapper.eventBridge property bool keyboardEnabled: HMD.active @@ -82,6 +81,7 @@ Item { color: hifi.colors.baseGray font.pixelSize: 12 verticalAlignment: Text.AlignLeft + text: webview.url anchors { top: nav.bottom horizontalCenter: parent.horizontalCenter; @@ -159,7 +159,6 @@ Item { function loadUrl(url) { webview.url = url web.url = webview.url; - web.address = webview.url; } function onInitialPage(url) { @@ -253,7 +252,6 @@ Item { }); webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"; - web.address = url; } onFeaturePermissionRequested: { @@ -279,7 +277,6 @@ Item { } if (WebEngineView.LoadSucceededStatus == loadRequest.status) { - web.address = webview.url; if (startingUp) { web.initialPage = webview.url; startingUp = false; @@ -307,7 +304,6 @@ Item { Component.onCompleted: { web.isDesktop = (typeof desktop !== "undefined"); - address = url; } Keys.onPressed: { From 07d3e482cf0d7e0a15a830f497bcde8240ab1a29 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:40:39 -0700 Subject: [PATCH 217/292] 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 4d49ae5b098eef9d8e6e83f389ea46b74b0a288c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:41:00 -0700 Subject: [PATCH 218/292] 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 cb51aaab6189b085f2422f9cf13bbaa2e04060ad Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 15:40:39 -0700 Subject: [PATCH 219/292] 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 220/292] 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 221/292] 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 222/292] 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 223/292] 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 224/292] 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 f867a8e7184e66c5b22b893f8bab49a027217179 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 20:12:42 -0700 Subject: [PATCH 225/292] guard against bad hinge-constraint settings --- .../physics/src/ObjectConstraintHinge.cpp | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index dd82de924c..730fe5683e 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -17,12 +17,12 @@ const uint16_t ObjectConstraintHinge::constraintVersion = 1; - +const glm::vec3 DEFAULT_HINGE_AXIS(1.0f, 0.0f, 0.0f); ObjectConstraintHinge::ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity) : ObjectConstraint(DYNAMIC_TYPE_HINGE, id, ownerEntity), - _pivotInA(glm::vec3(0.0f)), - _axisInA(glm::vec3(0.0f)) + _axisInA(DEFAULT_HINGE_AXIS), + _axisInB(DEFAULT_HINGE_AXIS) { #if WANT_DEBUG qCDebug(physics) << "ObjectConstraintHinge::ObjectConstraintHinge"; @@ -104,12 +104,27 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { return nullptr; } + if (glm::length(axisInA) == 0.0f) { + qCWarning(physics) << "hinge axis cannot be a zero vector"; + axisInA = DEFAULT_HINGE_AXIS; + } + + axisInA = glm::normalize(axisInA); + if (!otherEntityID.isNull()) { // This hinge is between two entities... find the other rigid body. btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { return nullptr; } + + if (glm::length(axisInB)) { + qCWarning(physics) << "hinge axis cannot be a zero vector"; + axisInB = DEFAULT_HINGE_AXIS; + } + + axisInB = glm::normalize(axisInB); + constraint = new btHingeConstraint(*rigidBodyA, *rigidBodyB, glmToBullet(pivotInA), glmToBullet(pivotInB), glmToBullet(axisInA), glmToBullet(axisInB), From c693687927eba1482e7f21e96523e346c0cd4787 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 20:23:10 -0700 Subject: [PATCH 226/292] react to very small vectors instead of just zero-length ones --- libraries/physics/src/ObjectConstraintHinge.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 730fe5683e..cf91ca904b 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -104,13 +104,13 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { return nullptr; } - if (glm::length(axisInA) == 0.0f) { + if (glm::length(axisInA) < FLT_EPSILON) { qCWarning(physics) << "hinge axis cannot be a zero vector"; axisInA = DEFAULT_HINGE_AXIS; + } else { + axisInA = glm::normalize(axisInA); } - axisInA = glm::normalize(axisInA); - if (!otherEntityID.isNull()) { // This hinge is between two entities... find the other rigid body. btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); @@ -118,13 +118,13 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { return nullptr; } - if (glm::length(axisInB)) { + if (glm::length(axisInB) < FLT_EPSILON) { qCWarning(physics) << "hinge axis cannot be a zero vector"; axisInB = DEFAULT_HINGE_AXIS; + } else { + axisInB = glm::normalize(axisInB); } - axisInB = glm::normalize(axisInB); - constraint = new btHingeConstraint(*rigidBodyA, *rigidBodyB, glmToBullet(pivotInA), glmToBullet(pivotInB), glmToBullet(axisInA), glmToBullet(axisInB), From ed09dcbdc948d102918efc09a233e50051a6adc3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 May 2017 21:32:06 -0700 Subject: [PATCH 227/292] 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 228/292] 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 229/292] 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 230/292] 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 231/292] 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 232/292] 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 233/292] 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 234/292] 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 235/292] 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 236/292] 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 237/292] 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 238/292] 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 239/292] 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 240/292] 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 241/292] 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 242/292] 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 243/292] 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 244/292] 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 245/292] 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 246/292] 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 247/292] 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 248/292] 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 249/292] 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 250/292] 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 251/292] 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 252/292] 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 253/292] 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 254/292] 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 255/292] 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 256/292] 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 257/292] 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 258/292] 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 259/292] * 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 260/292] 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 261/292] 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 262/292] 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 263/292] 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 264/292] 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 '' + '' + '' + - '' + '' + - '' + '' + - '