From 2d28ae1ffe5da8bcce64548d49ef39e1c72acda9 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 29 Nov 2016 18:15:40 +0000 Subject: [PATCH 01/30] fixed context menu showing up in the correct location --- interface/resources/controllers/oculus_touch.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json index 236a200841..aa574e36dd 100644 --- a/interface/resources/controllers/oculus_touch.json +++ b/interface/resources/controllers/oculus_touch.json @@ -3,6 +3,8 @@ "channels": [ { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb", "peek": true }, { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb", "peek": true }, + { "from": "OculusTouch.B", "to": "Standard.RightSecondaryThumb", "peek": true}, + { "from": "OculusTouch.Y", "to": "Standard.LeftSecondaryThumb", "peek": true}, { "from": "OculusTouch.A", "to": "Standard.A" }, { "from": "OculusTouch.B", "to": "Standard.B" }, From dc7bb5b0834764029e9856d0af05a5724773f294 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Dec 2016 00:17:03 +0000 Subject: [PATCH 02/30] grip button fix --- scripts/system/controllers/handControllerGrab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 341d8aec18..892dbda9c9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2346,6 +2346,7 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("releaseEquip"); } + // Make a small release haptic pulse if we really were holding something Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); From 1b24d373d291045c0d79e84b043c0cea840f4207 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Dec 2016 18:02:07 +0000 Subject: [PATCH 03/30] fixed near grab issue --- scripts/system/controllers/handControllerGrab.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 892dbda9c9..b8ac27a80c 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1009,7 +1009,10 @@ function MyController(hand) { this.secondaryPress = function(value) { _this.rawSecondaryValue = value; - if (value > 0) { + + // The value to check if we will allow the release function to be called + var allowReleaseValue = 0.1; + if (value > 0 && _this.rawTriggerValue <= allowReleaseValue) { _this.release(); } }; From 5cbf7588acb94dbb68eaca1fea42e71e49e8587f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 1 Dec 2016 11:11:42 -0800 Subject: [PATCH 04/30] Fixes for building on MSVC 2015 / Qt 5.8 --- libraries/entities/src/EntityItemProperties.cpp | 10 +++++----- libraries/shared/src/SharedUtil.cpp | 9 +++++++++ tests/render-perf/src/main.cpp | 3 ++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index e20bd81ab3..cb740a00f1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -147,15 +147,15 @@ QString getCollisionGroupAsString(uint8_t group) { } uint8_t getCollisionGroupAsBitMask(const QStringRef& name) { - if (0 == name.compare("dynamic")) { + if (0 == name.compare(QString("dynamic"))) { return USER_COLLISION_GROUP_DYNAMIC; - } else if (0 == name.compare("static")) { + } else if (0 == name.compare(QString("static"))) { return USER_COLLISION_GROUP_STATIC; - } else if (0 == name.compare("kinematic")) { + } else if (0 == name.compare(QString("kinematic"))) { return USER_COLLISION_GROUP_KINEMATIC; - } else if (0 == name.compare("myAvatar")) { + } else if (0 == name.compare(QString("myAvatar"))) { return USER_COLLISION_GROUP_MY_AVATAR; - } else if (0 == name.compare("otherAvatar")) { + } else if (0 == name.compare(QString("otherAvatar"))) { return USER_COLLISION_GROUP_OTHER_AVATAR; } return 0; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 287c15e3a2..26483388e5 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -26,6 +26,15 @@ #include #include "CPUIdent.h" #include + +#if _MSC_VER >= 1900 +#pragma comment(lib, "legacy_stdio_definitions.lib") +FILE _iob[] = {*stdin, *stdout, *stderr}; +extern "C" FILE * __cdecl __iob_func(void) { + return _iob; +} +#endif + #endif diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index e798969268..f52ba78724 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -1086,7 +1086,7 @@ private: QSize _size; QSettings _settings; - std::atomic _renderCount; + std::atomic _renderCount{ 0 }; gl::OffscreenContext _initContext; RenderThread _renderThread; QWindowCamera _camera; @@ -1152,3 +1152,4 @@ int main(int argc, char** argv) { } #include "main.moc" + From 643353a31e9adc99828d9aca78fb4e742bfb10e1 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 2 Dec 2016 15:46:20 -0800 Subject: [PATCH 05/30] Initial push Works on windows, have not tried it on mac. Lots more to do, but need to get it pushed to remote just in case. --- interface/src/Application.cpp | 8 +- libraries/networking/src/FingerprintUtils.cpp | 140 ++++++++++++++++++ libraries/networking/src/FingerprintUtils.h | 29 ++++ 3 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 libraries/networking/src/FingerprintUtils.cpp create mode 100644 libraries/networking/src/FingerprintUtils.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 76353104f4..9178a248ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -580,9 +581,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _window->setWindowTitle("Interface"); Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - + + // TODO: This is temporary, while developing + FingerprintUtils::getMachineFingerprint(); + // End TODO auto nodeList = DependencyManager::get(); - + // Set up a watchdog thread to intentionally crash the application on deadlocks _deadlockWatchdogThread = new DeadlockWatchdogThread(); _deadlockWatchdogThread->start(); diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp new file mode 100644 index 0000000000..6aa35dd2b6 --- /dev/null +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -0,0 +1,140 @@ +// +// FingerprintUtils.h +// libraries/networking/src +// +// Created by David Kelly on 2016-12-02. +// 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 +// + +#include "FingerprintUtils.h" +#include + +QString FingerprintUtils::getMachineFingerprint() { + QString retval; +#ifdef Q_OS_WIN + HRESULT hres; + IWbemLocator *pLoc = NULL; + + // initialize com + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *) &pLoc); + + if (FAILED(hres)) { + qDebug() << "Failed to initialize WbemLocator"; + return retval; + } + + // Connect to WMI through the IWbemLocator::ConnectServer method + IWbemServices *pSvc = NULL; + + // 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 + ); + + if (FAILED(hres)) { + pLoc->Release(); + qDebug() << "Failed to connect to WMI"; + return retval; + } + + // 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(); + qDebug() << "Failed to set security on proxy blanket"; + return retval; + } + + // 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(); + qDebug() << "query to get Win32_ComputerSystemProduct info"; + return retval; + } + + // 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: + retval = QString::fromWCharArray(vtProp.bstrVal); + break; + } + } + VariantClear(&vtProp); + + pclsObj->Release(); + } + pEnumerator->Release(); + + // Cleanup + pSvc->Release(); + pLoc->Release(); + + qDebug() << "Windows BIOS UUID: " << retval; +#endif //Q_OS_WIN + +#ifdef Q_OS_MAC + + io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); + CFStringRef uuidCf = (CFStringRef) IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); + IOObjectRelease(ioRegistryRoot); + retval = QString::fromCFString(uuidCf); + CFRelease(uuidCf); + qDebug() << "Mac serial number: " << retval; +#endif //Q_OS_MAC + + return retval; +} + diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h new file mode 100644 index 0000000000..84a94f0abc --- /dev/null +++ b/libraries/networking/src/FingerprintUtils.h @@ -0,0 +1,29 @@ +// +// FingerprintUtils.h +// libraries/networking/src +// +// Created by David Kelly on 2016-12-02. +// 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_FingerprintUtils_h +#define hifi_FingerprintUtils_h + +#include + +#ifdef Q_OS_WIN +#include +#include +#endif //Q_OS_WIN + +class FingerprintUtils { +public: + static QString getMachineFingerprint(); + +}; + +#endif // hifi_FingerprintUtils_h + From b888ce890c732f416362ea09185add9c8b468e69 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 3 Dec 2016 07:26:55 -0800 Subject: [PATCH 06/30] Updated the Qt audio plugins --- cmake/externals/wasapi/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 67f47d68fc..7cfca4f3ba 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi4.zip - URL_MD5 2abde5340a64d387848f12b9536a7e85 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi5.zip + URL_MD5 0530753e855ffc00232cc969bf1c84a8 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 3eecba77f20f4bdd2b3600421957d67a407232cb Mon Sep 17 00:00:00 2001 From: Ryan Jones Date: Sat, 3 Dec 2016 19:34:38 -0800 Subject: [PATCH 07/30] working menu item --- interface/src/Menu.cpp | 4 +++- interface/src/Menu.h | 1 + interface/src/ui/DialogsManager.cpp | 9 ++++++++ interface/src/ui/DialogsManager.h | 5 +++++ interface/src/ui/TestingDialog.cpp | 35 +++++++++++++++++++++++++++++ interface/src/ui/TestingDialog.h | 25 +++++++++++++++++++++ 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 interface/src/ui/TestingDialog.cpp create mode 100644 interface/src/ui/TestingDialog.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ea1b26dd80..53e3574092 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -589,7 +589,9 @@ Menu::Menu() { #endif - + // Developer >> Tests >>> + MenuWrapper* testMenu = developerMenu->addMenu("Tests"); + addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults())); // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ad11b91796..971e641f50 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -172,6 +172,7 @@ namespace MenuOption { const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; + const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 679fb7f59d..b8e74148f0 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -158,6 +158,15 @@ void DialogsManager::showScriptEditor() { _scriptEditor->raise(); } +void DialogsManager::showTestingResults() { + if (!_testingDialog) { + _testingDialog = new TestingDialog(qApp->getWindow()); + connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater())); + } + _testingDialog->show(); + _testingDialog->raise(); +} + void DialogsManager::showDomainConnectionDialog() { // if the dialog already exists we delete it so the connection data is refreshed if (_domainConnectionDialog) { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index e89bc43020..4bb914ead9 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -17,6 +17,7 @@ #include #include "HMDToolsDialog.h" +#include "TestingDialog.h" class AnimationsDialog; class AttachmentsDialog; @@ -26,6 +27,7 @@ class DiskCacheEditor; class LodToolsDialog; class OctreeStatsDialog; class ScriptEditorWindow; +class TestingDialog; class QMessageBox; class DomainConnectionDialog; @@ -38,6 +40,7 @@ public: QPointer getHMDToolsDialog() const { return _hmdToolsDialog; } QPointer getLodToolsDialog() const { return _lodToolsDialog; } QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } + QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } public slots: @@ -55,6 +58,7 @@ public slots: void hmdTools(bool showTools); void showScriptEditor(); void showDomainConnectionDialog(); + void showTestingResults(); // Application Update void showUpdateDialog(); @@ -83,6 +87,7 @@ private: QPointer _lodToolsDialog; QPointer _octreeStatsDialog; QPointer _scriptEditor; + QPointer _testingDialog; QPointer _domainConnectionDialog; }; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp new file mode 100644 index 0000000000..0a4435e13e --- /dev/null +++ b/interface/src/ui/TestingDialog.cpp @@ -0,0 +1,35 @@ +// +// TestingDialog.cpp +// interface/src/ui +// +// Created by Ryan Jones on 12/3/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 +// + +#include + +#include "ui/TestingDialog.h" +#include "Application.h" + +TestingDialog::TestingDialog(QWidget* parent) : + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) +{ + DependencyManager::get()->loadOneScript(qApp->applicationDirPath() + testRunnerRelativePath); + this->setWindowTitle(windowLabel); +} + +TestingDialog::~TestingDialog() { + // TODO: Clean up here? +} + +void TestingDialog::reject() { + this->QDialog::close(); +} + +void TestingDialog::closeEvent(QCloseEvent* event) { + this->QDialog::closeEvent(event); + emit closed(); +} diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h new file mode 100644 index 0000000000..97da4731f9 --- /dev/null +++ b/interface/src/ui/TestingDialog.h @@ -0,0 +1,25 @@ +#ifndef hifi_TestingDialog_h +#define hifi_TestingDialog_h + +#include + +const QString windowLabel = "Testing Dialog"; +const QString testRunnerRelativePath = "/scripts/developer/tests/bindUnitTest.js"; + +class TestingDialog : public QDialog { + Q_OBJECT +public: + TestingDialog(QWidget* parent); + ~TestingDialog(); + +signals: + void closed(); + +public slots: + void reject() override; + +protected: + void closeEvent(QCloseEvent*) override; +}; + +#endif \ No newline at end of file From 39e1a3fb4444d5cabc4d077bef8d39daf627dd42 Mon Sep 17 00:00:00 2001 From: Ryan Jones Date: Sun, 4 Dec 2016 20:53:06 -0800 Subject: [PATCH 08/30] add client-side test runner --- interface/src/ui/TestingDialog.cpp | 26 +++++---- interface/src/ui/TestingDialog.h | 29 ++++++---- .../developer/libraries/jasmine/hifi-boot.js | 48 ++++++++++++----- .../tests/{ => unit_tests}/avatarUnitTests.js | 6 +-- .../tests/{ => unit_tests}/bindUnitTest.js | 15 +++--- .../tests/unit_tests/entityUnitTests.js | 53 +++++++++++++++++++ .../developer/tests/unit_tests/testRunner.js | 13 +++++ 7 files changed, 145 insertions(+), 45 deletions(-) rename scripts/developer/tests/{ => unit_tests}/avatarUnitTests.js (93%) rename scripts/developer/tests/{ => unit_tests}/bindUnitTest.js (61%) create mode 100644 scripts/developer/tests/unit_tests/entityUnitTests.js create mode 100644 scripts/developer/tests/unit_tests/testRunner.js diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index 0a4435e13e..6f08bf87cf 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "ScriptEngines.h" #include "ui/TestingDialog.h" #include "Application.h" @@ -17,19 +17,23 @@ TestingDialog::TestingDialog(QWidget* parent) : QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) { - DependencyManager::get()->loadOneScript(qApp->applicationDirPath() + testRunnerRelativePath); - this->setWindowTitle(windowLabel); + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(windowLabel); + + _console = new JSConsole(this); + _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); + + auto _engines = DependencyManager::get(); + _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _console->setScriptEngine(_engine); + connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } TestingDialog::~TestingDialog() { - // TODO: Clean up here? + delete _console; } -void TestingDialog::reject() { - this->QDialog::close(); -} - -void TestingDialog::closeEvent(QCloseEvent* event) { - this->QDialog::closeEvent(event); - emit closed(); +void TestingDialog::onTestingFinished(const QString& scriptPath) { + _engine = NULL; + _console->setScriptEngine(NULL); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index 97da4731f9..1dc916a6f7 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -1,10 +1,24 @@ +// +// TestingDialog.h +// interface/src/ui +// +// Created by Ryan Jones on 12/3/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_TestingDialog_h #define hifi_TestingDialog_h #include +#include "ScriptEngine.h" +#include "JSConsole.h" -const QString windowLabel = "Testing Dialog"; -const QString testRunnerRelativePath = "/scripts/developer/tests/bindUnitTest.js"; +const QString windowLabel = "Client Script Tests"; +const QString testRunnerRelativePath = "/scripts/developer/tests/unit_tests/testRunner.js"; +const unsigned int TESTING_CONSOLE_HEIGHT = 400; class TestingDialog : public QDialog { Q_OBJECT @@ -12,14 +26,11 @@ public: TestingDialog(QWidget* parent); ~TestingDialog(); -signals: - void closed(); + void onTestingFinished(const QString& scriptPath); -public slots: - void reject() override; - -protected: - void closeEvent(QCloseEvent*) override; +private: + JSConsole* _console; + ScriptEngine* _engine; }; #endif \ No newline at end of file diff --git a/scripts/developer/libraries/jasmine/hifi-boot.js b/scripts/developer/libraries/jasmine/hifi-boot.js index 0c66a925af..f490a3618f 100644 --- a/scripts/developer/libraries/jasmine/hifi-boot.js +++ b/scripts/developer/libraries/jasmine/hifi-boot.js @@ -1,27 +1,50 @@ - (function() { + var BALLOT_X = '✗'; + var CHECKMARK = '✓'; + var DOWN_RIGHT_ARROW = '↳'; + var PASSED = 'passed'; + var lastSpecStartTime; function ConsoleReporter(options) { + var startTime = new Date().getTime(); + var errorCount = 0; this.jasmineStarted = function (obj) { - print("jasmineStarted: numSpecs = " + obj.totalSpecsDefined); + print('Jasmine started with ' + obj.totalSpecsDefined + ' tests.'); }; this.jasmineDone = function (obj) { - print("jasmineDone"); + var ERROR = errorCount === 1 ? 'error' : 'errors'; + var endTime = new Date().getTime(); + print('
'); + if (errorCount === 0) { + print ('All tests passed!'); + } else { + print('Tests completed with ' + + errorCount + ' ' + ERROR + '.'); + } + print('Tests completed in ' + (endTime - startTime) + 'ms.'); }; this.suiteStarted = function(obj) { - print("suiteStarted: \"" + obj.fullName + "\""); + print(obj.fullName); }; this.suiteDone = function(obj) { - print("suiteDone: \"" + obj.fullName + "\" " + obj.status); + print(''); }; this.specStarted = function(obj) { - print("specStarted: \"" + obj.fullName + "\""); + lastSpecStartTime = new Date().getTime(); }; this.specDone = function(obj) { - print("specDone: \"" + obj.fullName + "\" " + obj.status); + var specEndTime = new Date().getTime(); + var symbol = obj.status === PASSED ? + '' + CHECKMARK + '' : + '' + BALLOT_X + ''; + print('... ' + obj.fullName + ' ' + symbol + ' ' + '[' + + (specEndTime - lastSpecStartTime) + 'ms]'); - var i, l = obj.failedExpectations.length; - for (i = 0; i < l; i++) { - print(" " + obj.failedExpectations[i].message); + var specErrors = obj.failedExpectations.length; + errorCount += specErrors; + for (var i = 0; i < specErrors; i++) { + print('' + DOWN_RIGHT_ARROW + + ' ' + + obj.failedExpectations[i].message + ''); } }; return this; @@ -44,10 +67,11 @@ function extend(destination, source) { for (var property in source) { - destination[property] = source[property]; + if (source.hasOwnProperty(property)) { + destination[property] = source[property]; + } } return destination; } - }()); diff --git a/scripts/developer/tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js similarity index 93% rename from scripts/developer/tests/avatarUnitTests.js rename to scripts/developer/tests/unit_tests/avatarUnitTests.js index 29e3ad0588..2ff0f3b092 100644 --- a/scripts/developer/tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -1,7 +1,4 @@ -Script.include("../libraries/jasmine/jasmine.js"); -Script.include("../libraries/jasmine/hifi-boot.js"); - // Art3mis var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758"; @@ -17,6 +14,7 @@ describe("MyAvatar", function () { // wait until we are finished loading var id = Script.setInterval(function () { + print(MyAvatar.jointNames.length); if (MyAvatar.jointNames.length == 72) { // assume we are finished loading. Script.clearInterval(id); @@ -55,5 +53,3 @@ describe("MyAvatar", function () { }); -jasmine.getEnv().execute(); - diff --git a/scripts/developer/tests/bindUnitTest.js b/scripts/developer/tests/unit_tests/bindUnitTest.js similarity index 61% rename from scripts/developer/tests/bindUnitTest.js rename to scripts/developer/tests/unit_tests/bindUnitTest.js index 95fd497916..609487a30b 100644 --- a/scripts/developer/tests/bindUnitTest.js +++ b/scripts/developer/tests/unit_tests/bindUnitTest.js @@ -1,9 +1,12 @@ -Script.include('../libraries/jasmine/jasmine.js'); -Script.include('../libraries/jasmine/hifi-boot.js'); -Script.include('../../system/libraries/utils.js'); +Script.include('../../../system/libraries/utils.js'); describe('Bind', function() { - it('functions should have bind available', function() { + it('exists for functions', function() { + var FUNC = 'function'; + expect(typeof(function() {}.bind)).toEqual(FUNC); + }); + + it('should allow for setting context of this', function() { var foo = 'bar'; function callAnotherFn(anotherFn) { @@ -22,10 +25,6 @@ describe('Bind', function() { var instance = new TestConstructor(); - expect(typeof(instance.doSomething.bind) !== 'undefined'); expect(instance.doSomething()).toEqual(foo); }); }); - -jasmine.getEnv().execute(); -Script.stop(); \ No newline at end of file diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js new file mode 100644 index 0000000000..ad1606c4e9 --- /dev/null +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -0,0 +1,53 @@ +describe('Entity', function() { + var center = Vec3.sum( + MyAvatar.position, + Vec3.multiply(3, Quat.getFront(Camera.getOrientation())) + ); + var boxEntity; + var boxProps = { + type: 'Box', + color: { + red: 255, + green: 255, + blue: 255, + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1, + }, + }; + + beforeEach(function() { + boxEntity = Entities.addEntity(boxProps); + }); + + afterEach(function() { + Entities.deleteEntity(boxEntity); + boxEntity = null; + }); + + it('can be added programmatically', function() { + expect(typeof(boxEntity)).toEqual('string'); + }); + + it('instantiates itself correctly', function() { + var props = Entities.getEntityProperties(boxEntity); + expect(props.type).toEqual(boxProps.type); + }); + + it('can be modified after creation', function() { + var newPos = { + x: boxProps.position.x, + y: boxProps.position.y, + z: boxProps.position.z + 1.0, + }; + Entities.editEntity(boxEntity, { + position: newPos, + }); + + var props = Entities.getEntityProperties(boxEntity); + expect(Math.round(props.position.z)).toEqual(Math.round(newPos.z)); + }); +}); \ No newline at end of file diff --git a/scripts/developer/tests/unit_tests/testRunner.js b/scripts/developer/tests/unit_tests/testRunner.js new file mode 100644 index 0000000000..31d83cd986 --- /dev/null +++ b/scripts/developer/tests/unit_tests/testRunner.js @@ -0,0 +1,13 @@ +// Include testing library +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('bindUnitTest.js'); +Script.include('entityUnitTests.js'); + +// Run the tests +jasmine.getEnv().execute(); +Script.stop(); From 310e26011f7b05433f55af0a2cad73faf2ef645a Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 5 Dec 2016 00:47:50 -0800 Subject: [PATCH 09/30] Fix the lighting problem at the outline of the shapes, use the depth buffer instead of the Linear depth buffer --- libraries/render-utils/src/local_lights_shading.slf | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index b144ce13c7..d3542fcdfa 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -30,7 +30,6 @@ <@include LightClusterGrid.slh@> - in vec2 _texCoord0; out vec4 _fragColor; @@ -39,16 +38,14 @@ void main(void) { // Grab the fragment data from the uv vec2 texCoord = _texCoord0.st; - vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord); - DeferredFragment frag = unpackDeferredFragmentNoPosition(texCoord); + DeferredFrameTransform deferredTransform = getDeferredFrameTransform(); + DeferredFragment frag = unpackDeferredFragment(deferredTransform, texCoord); + vec4 fragPosition = frag.position; if (frag.mode == FRAG_MODE_UNLIT) { discard; } - frag.position = fragPosition; - - // Frag pos in world mat4 invViewMat = getViewInverse(); vec4 fragPos = invViewMat * fragPosition; @@ -57,7 +54,6 @@ void main(void) { vec4 clusterEyePos = frustumGrid_worldToEye(fragPos); ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); - ivec3 cluster = clusterGrid_getCluster(frustumGrid_clusterToIndex(clusterPos)); int numLights = cluster.x + cluster.y; if (numLights <= 0) { From 09d5dc382f7a228238b55c17d23c84899eca2d55 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 5 Dec 2016 18:08:36 +0000 Subject: [PATCH 10/30] better solution --- 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 b8ac27a80c..b9a735cf59 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1012,7 +1012,7 @@ function MyController(hand) { // The value to check if we will allow the release function to be called var allowReleaseValue = 0.1; - if (value > 0 && _this.rawTriggerValue <= allowReleaseValue) { + if (value > 0 && _this.state == STATE_HOLD) { _this.release(); } }; From 08301d488d3b41fff52001e50d48c7baec8855d3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 5 Dec 2016 18:10:54 +0000 Subject: [PATCH 11/30] clean up --- scripts/system/controllers/handControllerGrab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index b9a735cf59..ff1407cbf0 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2349,7 +2349,6 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("releaseEquip"); } - // Make a small release haptic pulse if we really were holding something Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); From a0f93dad2cd46c980273117d0a0ab52402e2892e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 10:14:03 -0800 Subject: [PATCH 12/30] Fix for driving/turning while flying in HMD mode The internal MyAvatar:_sensorToWorldMatrix was being updated properly, however the MyAvatar::_sensorToWorldMatrixCache was not. To fix this I've introduced a new "mode" to updateSensorToWorldMatrix that does not change the sensorToWorldMatrix at all, but properly copies the value to the cache and also updates hand controller poses etc. --- interface/src/avatar/MyAvatar.cpp | 6 +++++- interface/src/avatar/MyAvatar.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9a0b04aa6d..5b691a6dc6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -467,6 +467,8 @@ void MyAvatar::simulate(float deltaTime) { if (_characterController.getState() != CharacterController::State::Hover) { updateSensorToWorldMatrix(_enableVerticalComfortMode ? SensorToWorldUpdateMode::VerticalComfort : SensorToWorldUpdateMode::Vertical); + } else { + updateSensorToWorldMatrix(SensorToWorldUpdateMode::None); } { @@ -616,6 +618,8 @@ void MyAvatar::updateSensorToWorldMatrix(SensorToWorldUpdateMode mode) { } else if (mode == SensorToWorldUpdateMode::Vertical) { setSensorToWorldMatrix(sensorToWorldMat); } + } else if (mode == SensorToWorldUpdateMode::None) { + setSensorToWorldMatrix(_sensorToWorldMatrix); } } @@ -1897,7 +1901,7 @@ void MyAvatar::applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, flo // update the position column of matrix glm::mat4 newSensorToWorldMatrix = _sensorToWorldMatrix; newSensorToWorldMatrix[3] = glm::vec4(position, 1.0f); - setSensorToWorldMatrix(newSensorToWorldMatrix); + _sensorToWorldMatrix = newSensorToWorldMatrix; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index feef171a29..72936ec0ce 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -192,7 +192,8 @@ public: enum class SensorToWorldUpdateMode { Full = 0, Vertical, - VerticalComfort + VerticalComfort, + None }; void updateSensorToWorldMatrix(SensorToWorldUpdateMode mode = SensorToWorldUpdateMode::Full); From 349f0355aed9ba511480f18ba7ba577b47d6e771 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 10:42:20 -0800 Subject: [PATCH 13/30] Fix for reset sensors while in desktop mode. Previously hitting ' a.k.a. reset sensors, would teleport you to the last position you were in before you switched into desktop mode, or the spawn location. This fixes that behavior but not re-centering the avatar while in desktop mode. --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/avatar/MyAvatar.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 76353104f4..f9db5a956e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4745,13 +4745,13 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi renderArgs->_viewport = originalViewport; } -void Application::resetSensors(bool andReload) { +void Application::resetSensors(bool andRecenter) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); getActiveDisplayPlugin()->resetSensors(); _overlayConductor.centerUI(); - getMyAvatar()->reset(andReload); + getMyAvatar()->reset(andRecenter); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 5ab94465cc..a57fa1b02d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -317,7 +317,7 @@ public slots: void openUrl(const QUrl& url) const; - void resetSensors(bool andReload = false); + void resetSensors(bool andRecenter = false); void setActiveFaceTracker() const; #if (PR_BUILD || DEV_BUILD) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9a0b04aa6d..730a6d4a85 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -308,7 +308,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { } setThrust(glm::vec3(0.0f)); - if (andRecenter) { + if (andRecenter && qApp->isHMDMode()) { // derive the desired body orientation from the *old* hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. From 6d82410bc5f44d7804441bd17db6aaa01bcbc150 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 10:53:20 -0800 Subject: [PATCH 14/30] Fix for away.js teleporting user when in desktop mode away.js calls MyAvatar.centerBody, however centerBody is only meaningful in HMD mode. To guard against this, MyAvatar::centerBody is now a no-op if the application is not in HMD mode. --- interface/src/avatar/MyAvatar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 730a6d4a85..49a4f0f5da 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -254,6 +254,10 @@ void MyAvatar::centerBody() { return; } + if (!qApp->isHMDMode()) { + return; + } + // derive the desired body orientation from the current hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. From 54716f70e5194feeb84d6357fdecdccaa1baf7d8 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 5 Dec 2016 11:11:30 -0800 Subject: [PATCH 15/30] Now works on mac, small build change for that Plus does nothing for linux, but there's a comment. --- libraries/networking/CMakeLists.txt | 9 +++++ libraries/networking/src/FingerprintUtils.cpp | 40 ++++++++++++++----- libraries/networking/src/FingerprintUtils.h | 4 -- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/libraries/networking/CMakeLists.txt b/libraries/networking/CMakeLists.txt index 53ebb86b83..8ef67cb2a4 100644 --- a/libraries/networking/CMakeLists.txt +++ b/libraries/networking/CMakeLists.txt @@ -15,6 +15,10 @@ add_dependency_external_projects(tbb) find_package(OpenSSL REQUIRED) find_package(TBB REQUIRED) +if (APPLE) + find_library(FRAMEWORK_IOKIT IOKit) +endif () + if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") # this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." @@ -26,6 +30,11 @@ include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") # append OpenSSL to our list of libraries to link target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES} ${TBB_LIBRARIES}) +# IOKit is needed for getting machine fingerprint +if (APPLE) + target_link_libraries(${TARGET_NAME} ${FRAMEWORK_IOKIT}) +endif (APPLE) + # libcrypto uses dlopen in libdl if (UNIX) target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 6aa35dd2b6..7f18ccd276 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -12,8 +12,34 @@ #include "FingerprintUtils.h" #include +#ifdef Q_OS_WIN +#include +#include +#endif //Q_OS_WIN + +#ifdef Q_OS_MAC +#include +#include +#include +#endif //Q_OS_MAC + QString FingerprintUtils::getMachineFingerprint() { QString retval; + +#ifdef Q_OS_LINUX + // sadly need to be root to get smbios guid from linux, so + // for now lets do nothing. +#endif //Q_OS_LINUX + +#ifdef Q_OS_MAC + io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); + CFStringRef uuidCf = (CFStringRef) IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); + IOObjectRelease(ioRegistryRoot); + retval = QString::fromCFString(uuidCf); + CFRelease(uuidCf); + qDebug() << "Mac serial number: " << retval; +#endif //Q_OS_MAC + #ifdef Q_OS_WIN HRESULT hres; IWbemLocator *pLoc = NULL; @@ -125,16 +151,10 @@ QString FingerprintUtils::getMachineFingerprint() { qDebug() << "Windows BIOS UUID: " << retval; #endif //Q_OS_WIN -#ifdef Q_OS_MAC - - io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); - CFStringRef uuidCf = (CFStringRef) IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); - IOObjectRelease(ioRegistryRoot); - retval = QString::fromCFString(uuidCf); - CFRelease(uuidCf); - qDebug() << "Mac serial number: " << retval; -#endif //Q_OS_MAC - + // TODO: should we have a fallback for cases where this failed, + // leaving us with an empty string or something that isn't a + // guid? For now keeping this a string, but maybe best to return + // a QUuid? return retval; } diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index 84a94f0abc..cad198789c 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -14,10 +14,6 @@ #include -#ifdef Q_OS_WIN -#include -#include -#endif //Q_OS_WIN class FingerprintUtils { public: From 4d0f2c1ebb506ffa45820f6341e1d2ab2ae69349 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 5 Dec 2016 11:45:36 -0800 Subject: [PATCH 16/30] annoying whitespace --- libraries/networking/src/FingerprintUtils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index cad198789c..de7f0b87ff 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -18,7 +18,6 @@ class FingerprintUtils { public: static QString getMachineFingerprint(); - }; #endif // hifi_FingerprintUtils_h From 094857949d9d978a8916282c1c406c33991701f4 Mon Sep 17 00:00:00 2001 From: Ryan Jones Date: Mon, 5 Dec 2016 12:57:21 -0800 Subject: [PATCH 17/30] tabs -> spaces, remove raw ptr, remove unnecessary spec diff --- interface/src/Menu.cpp | 6 ++--- interface/src/Menu.h | 2 +- interface/src/ui/DialogsManager.cpp | 12 ++++----- interface/src/ui/DialogsManager.h | 6 ++--- interface/src/ui/TestingDialog.cpp | 26 ++++++++----------- interface/src/ui/TestingDialog.h | 13 +++++----- .../tests/unit_tests/avatarUnitTests.js | 1 - 7 files changed, 30 insertions(+), 36 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 53e3574092..16b213eacb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -589,9 +589,9 @@ Menu::Menu() { #endif - // Developer >> Tests >>> - MenuWrapper* testMenu = developerMenu->addMenu("Tests"); - addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults())); + // Developer >> Tests >>> + MenuWrapper* testMenu = developerMenu->addMenu("Tests"); + addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults())); // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 971e641f50..8998e616e8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -172,7 +172,7 @@ namespace MenuOption { const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; - const QString RunClientScriptTests = "Run Client Script Tests"; + const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index b8e74148f0..03c71d8573 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -159,12 +159,12 @@ void DialogsManager::showScriptEditor() { } void DialogsManager::showTestingResults() { - if (!_testingDialog) { - _testingDialog = new TestingDialog(qApp->getWindow()); - connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater())); - } - _testingDialog->show(); - _testingDialog->raise(); + if (!_testingDialog) { + _testingDialog = new TestingDialog(qApp->getWindow()); + connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater())); + } + _testingDialog->show(); + _testingDialog->raise(); } void DialogsManager::showDomainConnectionDialog() { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 4bb914ead9..c02c1fc2c3 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -40,7 +40,7 @@ public: QPointer getHMDToolsDialog() const { return _hmdToolsDialog; } QPointer getLodToolsDialog() const { return _lodToolsDialog; } QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } - QPointer getTestingDialog() const { return _testingDialog; } + QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } public slots: @@ -58,7 +58,7 @@ public slots: void hmdTools(bool showTools); void showScriptEditor(); void showDomainConnectionDialog(); - void showTestingResults(); + void showTestingResults(); // Application Update void showUpdateDialog(); @@ -87,7 +87,7 @@ private: QPointer _lodToolsDialog; QPointer _octreeStatsDialog; QPointer _scriptEditor; - QPointer _testingDialog; + QPointer _testingDialog; QPointer _domainConnectionDialog; }; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index 6f08bf87cf..f55eb63a5b 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -15,25 +15,21 @@ #include "Application.h" TestingDialog::TestingDialog(QWidget* parent) : - QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint), + _console(new JSConsole(this)) { - setAttribute(Qt::WA_DeleteOnClose); - setWindowTitle(windowLabel); + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(windowLabel); - _console = new JSConsole(this); - _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); + _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); - auto _engines = DependencyManager::get(); - _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); - _console->setScriptEngine(_engine); - connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); -} - -TestingDialog::~TestingDialog() { - delete _console; + auto _engines = DependencyManager::get(); + _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _console->setScriptEngine(_engine); + connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } void TestingDialog::onTestingFinished(const QString& scriptPath) { - _engine = NULL; - _console->setScriptEngine(NULL); + _engine = nullptr; + _console->setScriptEngine(nullptr); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index 1dc916a6f7..b90b8f2e99 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -21,16 +21,15 @@ const QString testRunnerRelativePath = "/scripts/developer/tests/unit_tests/test const unsigned int TESTING_CONSOLE_HEIGHT = 400; class TestingDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - TestingDialog(QWidget* parent); - ~TestingDialog(); + TestingDialog(QWidget* parent); - void onTestingFinished(const QString& scriptPath); + void onTestingFinished(const QString& scriptPath); private: - JSConsole* _console; - ScriptEngine* _engine; + std::unique_ptr _console; + ScriptEngine* _engine; }; -#endif \ No newline at end of file +#endif diff --git a/scripts/developer/tests/unit_tests/avatarUnitTests.js b/scripts/developer/tests/unit_tests/avatarUnitTests.js index 2ff0f3b092..7032b5f5e6 100644 --- a/scripts/developer/tests/unit_tests/avatarUnitTests.js +++ b/scripts/developer/tests/unit_tests/avatarUnitTests.js @@ -14,7 +14,6 @@ describe("MyAvatar", function () { // wait until we are finished loading var id = Script.setInterval(function () { - print(MyAvatar.jointNames.length); if (MyAvatar.jointNames.length == 72) { // assume we are finished loading. Script.clearInterval(id); From 2ca4d0ab29082de10ac023749c994a8a00b4ed51 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 5 Dec 2016 14:12:57 -0800 Subject: [PATCH 18/30] Fix drop in text rendering quality on texture memory constrained systems --- interface/src/Menu.cpp | 5 ++++- interface/src/Menu.h | 1 + .../src/display-plugins/OpenGLDisplayPlugin.cpp | 1 - libraries/render-utils/src/text/Font.cpp | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ea1b26dd80..c22d0ba32a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -363,6 +363,7 @@ Menu::Menu() { QActionGroup* textureGroup = new QActionGroup(textureMenu); textureGroup->setExclusive(true); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTextureAutomatic, 0, true)); + textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture4MB, 0, false)); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture64MB, 0, false)); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture256MB, 0, false)); textureGroup->addAction(addCheckableActionToQMenuAndActionHash(textureMenu, MenuOption::RenderMaxTexture512MB, 0, false)); @@ -372,7 +373,9 @@ Menu::Menu() { auto checked = textureGroup->checkedAction(); auto text = checked->text(); gpu::Context::Size newMaxTextureMemory { 0 }; - if (MenuOption::RenderMaxTexture64MB == text) { + if (MenuOption::RenderMaxTexture4MB == text) { + newMaxTextureMemory = MB_TO_BYTES(4); + } else if (MenuOption::RenderMaxTexture64MB == text) { newMaxTextureMemory = MB_TO_BYTES(64); } else if (MenuOption::RenderMaxTexture256MB == text) { newMaxTextureMemory = MB_TO_BYTES(256); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ad11b91796..287fec2609 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -156,6 +156,7 @@ namespace MenuOption { const QString RenderOtherLookAtVectors = "Show Other Eye Vectors"; const QString RenderMaxTextureMemory = "Maximum Texture Memory"; const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; + const QString RenderMaxTexture4MB = "4 MB"; const QString RenderMaxTexture64MB = "64 MB"; const QString RenderMaxTexture256MB = "256 MB"; const QString RenderMaxTexture512MB = "512 MB"; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 63c4692a5f..969c0a95a9 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -360,7 +360,6 @@ void OpenGLDisplayPlugin::customizeContext() { auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); - cursorData.texture->autoGenerateMips(-1); } } } diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 3607aa5803..4f4ee12622 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -210,7 +210,6 @@ void Font::read(QIODevice& in) { _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); _texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - _texture->autoGenerateMips(-1); } void Font::setupGPU() { From e8b5b56df75fa78bccca20414392b8a737722714 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:16:49 -0800 Subject: [PATCH 19/30] Revert "Merge pull request #9151 from hyperlogic/bug-fix/reset-sensor-in-desktop-mode" This reverts commit 119653bceb616479ed5499077cd119928a2a4b20, reversing changes made to a69d77ffbe4f31edbc720515f0f37af459e6f134. --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/avatar/MyAvatar.cpp | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a2eb58def4..900d1bbb63 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4733,13 +4733,13 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi renderArgs->_viewport = originalViewport; } -void Application::resetSensors(bool andRecenter) { +void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); getActiveDisplayPlugin()->resetSensors(); _overlayConductor.centerUI(); - getMyAvatar()->reset(andRecenter); + getMyAvatar()->reset(andReload); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } diff --git a/interface/src/Application.h b/interface/src/Application.h index a57fa1b02d..5ab94465cc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -317,7 +317,7 @@ public slots: void openUrl(const QUrl& url) const; - void resetSensors(bool andRecenter = false); + void resetSensors(bool andReload = false); void setActiveFaceTracker() const; #if (PR_BUILD || DEV_BUILD) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4296907863..5b691a6dc6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -254,10 +254,6 @@ void MyAvatar::centerBody() { return; } - if (!qApp->isHMDMode()) { - return; - } - // derive the desired body orientation from the current hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. @@ -312,7 +308,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { } setThrust(glm::vec3(0.0f)); - if (andRecenter && qApp->isHMDMode()) { + if (andRecenter) { // derive the desired body orientation from the *old* hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. From 49ce04e214db275aa203b3d6cc8da1cee068491d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:17:32 -0800 Subject: [PATCH 20/30] Revert "Merge pull request #9150 from hyperlogic/bug-fix/driving-while-flying-in-hmd" This reverts commit a69d77ffbe4f31edbc720515f0f37af459e6f134, reversing changes made to 996d3f58754f1203c157507cd4e5d5ade1ab4f54. --- interface/src/avatar/MyAvatar.cpp | 6 +----- interface/src/avatar/MyAvatar.h | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5b691a6dc6..9a0b04aa6d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -467,8 +467,6 @@ void MyAvatar::simulate(float deltaTime) { if (_characterController.getState() != CharacterController::State::Hover) { updateSensorToWorldMatrix(_enableVerticalComfortMode ? SensorToWorldUpdateMode::VerticalComfort : SensorToWorldUpdateMode::Vertical); - } else { - updateSensorToWorldMatrix(SensorToWorldUpdateMode::None); } { @@ -618,8 +616,6 @@ void MyAvatar::updateSensorToWorldMatrix(SensorToWorldUpdateMode mode) { } else if (mode == SensorToWorldUpdateMode::Vertical) { setSensorToWorldMatrix(sensorToWorldMat); } - } else if (mode == SensorToWorldUpdateMode::None) { - setSensorToWorldMatrix(_sensorToWorldMatrix); } } @@ -1901,7 +1897,7 @@ void MyAvatar::applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, flo // update the position column of matrix glm::mat4 newSensorToWorldMatrix = _sensorToWorldMatrix; newSensorToWorldMatrix[3] = glm::vec4(position, 1.0f); - _sensorToWorldMatrix = newSensorToWorldMatrix; + setSensorToWorldMatrix(newSensorToWorldMatrix); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 72936ec0ce..feef171a29 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -192,8 +192,7 @@ public: enum class SensorToWorldUpdateMode { Full = 0, Vertical, - VerticalComfort, - None + VerticalComfort }; void updateSensorToWorldMatrix(SensorToWorldUpdateMode mode = SensorToWorldUpdateMode::Full); From 0c8105b5519753bb177df062d5d1f509a56d7c33 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:17:53 -0800 Subject: [PATCH 21/30] Revert "Merge pull request #9141 from hyperlogic/feature/my-avatar-docs-part-1" This reverts commit 934148fea50b6564a5c8a10975e9d4abea25cda9, reversing changes made to d2b32c3084700d67f9edcdef19a60d386339be12. --- interface/src/avatar/MyAvatar.h | 157 +------------------ libraries/controllers/src/controllers/Pose.h | 10 -- tools/jsdoc/plugins/hifi.js | 9 +- 3 files changed, 8 insertions(+), 168 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index feef171a29..a5dc01b12b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -88,57 +88,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool avatarCollisionsEnabled READ getAvatarCollisionsEnabled WRITE setAvatarCollisionsEnabled) - /**jsdoc - * Your avatar is your in-world representation of you. The MyAvatar API is used to manipulate the avatar. - * For example, using the MyAvatar API you can customize the avatar's appearance, run custom avatar animations, - * change the avatar's position within the domain, or manage the avatar's collisions with other objects. - * NOTE: MyAvatar extends Avatar and AvatarData, see those namespace for more properties/methods. - * - * @namespace MyAvatar - * @augments Avatar - * @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface, - * and false if you would not like to see MyAvatar in your local interface. - * @property motorVelocity {Vec3} Can be used to move the avatar with this velocity. - * @property motorTimescale {float} Specifies how quickly the avatar should accelerate to meet the motorVelocity, - * smaller values will result in higher acceleration. - * @property motorReferenceFrame {string} Reference frame of the motorVelocity, must be one of the following: "avatar", "camera", "world" - * @property collisionSoundURL {string} Specifies the sound to play when the avatar experiences a collision. - * You can provide a mono or stereo 16-bit WAV file running at either 24 Khz or 48 Khz. - * The latter is downsampled by the audio mixer, so all audio effectively plays back at a 24 Khz sample rate. - * 48 Khz RAW files are also supported. - * @property audioListenerMode {number} When hearing spatialized audio this determines where the listener placed. - * Should be one of the following values: - * MyAvatar.audioListenerModeHead - the listener located at the avatar's head. - * MyAvatar.audioListenerModeCamera - the listener is relative to the camera. - * MyAvatar.audioListenerModeCustom - the listener is at a custom location specified by the MyAvatar.customListenPosition - * and MyAvatar.customListenOrientation properties. - * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position - * of audio spatialization listener. - * @property customListenOreintation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation - * of the audio spatialization listener. - * @property audioListenerModeHead {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the avatar's head. - * @property audioListenerModeCamera {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the camera. - * @property audioListenerModeCustom {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the value specified by MyAvatar.customListenPosition and MyAvatar.customListenOrientation. - * @property leftHandPosition {Vec3} READ-ONLY. The desired position of the left wrist in avatar space, determined by the hand controllers. - * Note: only valid if hand controllers are in use. - * @property rightHandPosition {Vec3} READ-ONLY. The desired position of the right wrist in avatar space, determined by the hand controllers. - * Note: only valid if hand controllers are in use. - * @property leftHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.leftHandPosition - * @property rightHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.rightHandPosition - * @property leftHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired - * wrist position, determined by the hand controllers. - * @property rightHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired - * wrist position, determined by the hand controllers. - * @property leftHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.leftHandPose - * @property rightHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.rightHandPose - * @property hmdLeanRecenterEnabled {bool} This can be used disable the hmd lean recenter behavior. This behavior is what causes your avatar - * to follow your HMD as you walk around the room, in room scale VR. Disabling this is useful if you desire to pin the avatar to a fixed location. - * @property avatarCollisionsEnabled {bool} This can be used to disable collisions between the avatar and the world. - */ - public: explicit MyAvatar(RigPointer rig); ~MyAvatar(); @@ -151,17 +100,7 @@ public: void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); - /**jsdoc - * Moves and orients the avatar, such that it is directly underneath the HMD, with toes pointed forward. - * @function MyAvatar.centerBody - */ Q_INVOKABLE void centerBody(); // thread-safe - - /**jsdoc - * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent - * contorted joints. - * @function MyAvatar.centerBody - */ Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe void update(float deltaTime); @@ -200,109 +139,23 @@ public: void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } - /**jsdoc - * The default position in world coordinates of the point directly between the avatar's eyes - * @function MyAvatar.getDefaultEyePosition - * @example This example gets the default eye position and prints it to the debug log. - * var defaultEyePosition = MyAvatar.getDefaultEyePosition(); - * print (JSON.stringify(defaultEyePosition)); - * @returns {Vec3} Position between the avatar's eyes. - */ Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). overrideAnimation() is used to completely - * override all motion from the default animation system (including inverse kinematics for hand and head controllers) and - * play a specified animation. To end this animation and restore the default animations, use MyAvatar.restoreAnimation. - * @function MyAvatar.overrideAnimation - * @example Play a clapping animation on your avatar for three seconds. - * // Clap your hands for 3 seconds then restore animation back to the avatar. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); - * Script.setTimeout(function () { - * MyAvatar.restoreAnimation(); - * }, 3000); - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. - * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. - * @param loop {bool} Set to true if the animation should loop. - * @param firstFrame {number} The frame the animation should start at. - * @param lastFrame {number} The frame the animation should end at. - */ + // Interrupt the current animation with a custom animation. Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will override the default animations. - * restoreAnimation() is used to restore all motion from the default animation system including inverse kinematics for hand and head - * controllers. If you aren't currently playing an override animation, this function will have no effect. - * @function MyAvatar.restoreAnimation - * @example Play a clapping animation on your avatar for three seconds. - * // Clap your hands for 3 seconds then restore animation back to the avatar. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); - * Script.setTimeout(function () { - * MyAvatar.restoreAnimation(); - * }, 3000); - */ + // Stop the animation that was started with overrideAnimation and go back to the standard animation. Q_INVOKABLE void restoreAnimation(); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data - * (such as look at vectors, hand sensors etc.). Each animation specified in the avatar-animation.json file is known as an animation role. - * Animation roles map to easily understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd." - * getAnimationRoles() is used get the list of animation roles defined in the avatar-animation.json. - * @function MyAvatar.getAnimatationRoles - * @example This example prints the list of animation roles defined in the avatar's avatar-animation.json file to the debug log. - * var roles = MyAvatar.getAnimationRoles(); - * print("Animation Roles:"); - * for (var i = 0; i < roles.length; i++) { - * print(roles[i]); - * } - * @returns {string[]} Array of role strings - */ + // Returns a list of all clips that are available Q_INVOKABLE QStringList getAnimationRoles(); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions - * that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). - * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). - * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation role. - * Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body. - * @function MyAvatar.overrideRoleAnimation - * @example The default avatar-animation.json defines an "idleStand" animation role. This role specifies that when the avatar is not moving, - * an animation clip of the avatar idling with hands hanging at its side will be used. It also specifies that when the avatar moves, the animation - * will smoothly blend to the walking animation used by the "walkFwd" animation role. - * In this example, the "idleStand" role animation clip has been replaced with a clapping animation clip. Now instead of standing with its arms - * hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar - * starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role. - * // An animation of the avatar clapping its hands while standing - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53); - * // To restore the default animation, use MyAvatar.restoreRoleAnimation(). - * @param role {string} The animation role to override - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. - * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. - * @param loop {bool} Set to true if the animation should loop - * @param firstFrame {number} The frame the animation should start at - * @param lastFrame {number} The frame the animation should end at - */ + // Replace an existing standard role animation with a custom one. Q_INVOKABLE void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions that - * the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). For each role, - * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). You can change the animation clip (.FBX) associated with a specified animation - * role using overrideRoleAnimation(). - * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have not specified an override animation - * for the specified role, this function will have no effect. - * @function MyAvatar.restoreRoleAnimation - * @param rule {string} The animation role clip to restore - */ + // remove an animation role override and return to the standard animation. Q_INVOKABLE void restoreRoleAnimation(const QString& role); // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index c16b3ae34b..47ba59279a 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -42,16 +42,6 @@ namespace controller { Pose transform(const glm::mat4& mat) const; - /**jsdoc - * Represents a hand controller pose typically received from Controller.getPoseValue - * Unless otherwise noted all properties are in avatar space. - * - * @typedef Pose - * @property translation {Vec3} position of controller - * @property rotation {Quat} orientation of controller - * @property velocity {Vec3} current velocity of controller (meters/sec) - * @property angularVelocity {Vec3} current angular velocity of controller (radians/sec) - */ static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event); }; diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c3fa14025f..8a6d2bf0f2 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -15,15 +15,12 @@ exports.handlers = { // directories to scan for jsdoc comments var dirList = [ '../../interface/src', - '../../interface/src/avatar', '../../interface/src/scripting', '../../interface/src/ui/overlays', - '../../libraries/animation/src', - '../../libraries/avatars/src', - '../../libraries/controllers/src/controllers/', - '../../libraries/entities/src', + '../../libraries/script-engine/src', '../../libraries/networking/src', - '../../libraries/script-engine/src' + '../../libraries/animation/src', + '../../libraries/entities/src', ]; var exts = ['.h', '.cpp']; From 89ae3b3d6efda0d47f2b6d55fcc530d1a076bea5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:18:03 -0800 Subject: [PATCH 22/30] Revert "Merge pull request #8691 from highfidelity/out-of-body-experience" This reverts commit efe9571ab82b3b70877b0140c70a37307c5a39dd, reversing changes made to 333e9ec7f4111a9fe4b0d6b7a6a80b56b8ca00fd. --- .../resources/avatar/avatar-animation.json | 45 +- interface/src/Application.cpp | 9 +- interface/src/Menu.cpp | 16 +- interface/src/Menu.h | 9 +- interface/src/avatar/MyAvatar.cpp | 465 ++++---------- interface/src/avatar/MyAvatar.h | 65 +- .../src/avatar/MyCharacterController.cpp | 443 +------------ interface/src/avatar/MyCharacterController.h | 29 +- interface/src/avatar/SkeletonModel.cpp | 67 +- .../src/scripting/HMDScriptingInterface.cpp | 20 - .../src/scripting/HMDScriptingInterface.h | 12 +- libraries/animation/src/AnimBlendLinear.cpp | 14 +- libraries/animation/src/AnimBlendLinear.h | 4 +- .../animation/src/AnimBlendLinearMove.cpp | 14 +- libraries/animation/src/AnimBlendLinearMove.h | 4 +- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimClip.h | 2 +- libraries/animation/src/AnimContext.cpp | 16 - libraries/animation/src/AnimContext.h | 30 - .../animation/src/AnimInverseKinematics.cpp | 79 +-- .../animation/src/AnimInverseKinematics.h | 9 +- libraries/animation/src/AnimManipulator.cpp | 6 +- libraries/animation/src/AnimManipulator.h | 4 +- libraries/animation/src/AnimNode.h | 8 +- libraries/animation/src/AnimOverlay.cpp | 6 +- libraries/animation/src/AnimOverlay.h | 2 +- libraries/animation/src/AnimStateMachine.cpp | 14 +- libraries/animation/src/AnimStateMachine.h | 4 +- libraries/animation/src/Rig.cpp | 85 +-- libraries/animation/src/Rig.h | 10 - libraries/physics/src/BulletUtil.h | 2 +- libraries/physics/src/CharacterController.cpp | 600 ++++++------------ libraries/physics/src/CharacterController.h | 79 +-- .../physics/src/CharacterGhostObject.cpp | 415 ------------ libraries/physics/src/CharacterGhostObject.h | 103 --- libraries/physics/src/CharacterGhostShape.cpp | 31 - libraries/physics/src/CharacterGhostShape.h | 25 - libraries/physics/src/CharacterRayResult.cpp | 31 - libraries/physics/src/CharacterRayResult.h | 44 -- .../physics/src/CharacterSweepResult.cpp | 42 -- libraries/physics/src/CharacterSweepResult.h | 45 -- .../physics/src/CollisionRenderMeshCache.cpp | 2 +- .../physics/src/CollisionRenderMeshCache.h | 2 +- libraries/physics/src/ContactInfo.cpp | 2 +- libraries/physics/src/ContactInfo.h | 2 +- libraries/physics/src/ObjectAction.cpp | 2 +- libraries/physics/src/ObjectAction.h | 2 +- libraries/physics/src/ObjectMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 2 +- .../physics/src/PhysicalEntitySimulation.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 2 +- libraries/physics/src/ShapeFactory.cpp | 2 +- libraries/physics/src/ShapeFactory.h | 2 +- libraries/physics/src/ShapeManager.cpp | 2 +- libraries/physics/src/ShapeManager.h | 2 +- libraries/shared/src/BackgroundMode.h | 2 +- libraries/shared/src/GeometryUtil.cpp | 39 -- libraries/shared/src/GeometryUtil.h | 5 - libraries/shared/src/ShapeInfo.cpp | 2 +- libraries/shared/src/ShapeInfo.h | 2 +- scripts/system/controllers/teleport.js | 80 +-- 63 files changed, 549 insertions(+), 2523 deletions(-) mode change 100755 => 100644 interface/src/avatar/MyAvatar.cpp mode change 100755 => 100644 interface/src/avatar/MyCharacterController.cpp delete mode 100644 libraries/animation/src/AnimContext.cpp delete mode 100644 libraries/animation/src/AnimContext.h mode change 100755 => 100644 libraries/physics/src/CharacterController.cpp delete mode 100755 libraries/physics/src/CharacterGhostObject.cpp delete mode 100755 libraries/physics/src/CharacterGhostObject.h delete mode 100644 libraries/physics/src/CharacterGhostShape.cpp delete mode 100644 libraries/physics/src/CharacterGhostShape.h delete mode 100755 libraries/physics/src/CharacterRayResult.cpp delete mode 100644 libraries/physics/src/CharacterRayResult.h delete mode 100755 libraries/physics/src/CharacterSweepResult.cpp delete mode 100644 libraries/physics/src/CharacterSweepResult.h diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index ba4c66f045..834a3fc277 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -255,10 +255,10 @@ }, { "id": "idleToWalkFwd", - "interpTarget": 4, - "interpDuration": 3.0, + "interpTarget": 3, + "interpDuration": 3, "transitions": [ - { "var": "idleToWalkFwdClipOnDone", "state": "walkFwd" }, + { "var": "idleToWalkFwdOnDone", "state": "walkFwd" }, { "var": "isNotMoving", "state": "idle" }, { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, @@ -292,8 +292,8 @@ }, { "id": "walkBwd", - "interpTarget": 4, - "interpDuration": 4, + "interpTarget": 6, + "interpDuration": 6, "transitions": [ { "var": "isNotMoving", "state": "idle" }, { "var": "isMovingForward", "state": "walkFwd" }, @@ -523,7 +523,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.5, 1.3, 4.5], + "characteristicSpeeds": [0.5, 1.4, 4.5], "alphaVar": "moveForwardAlpha", "desiredSpeedVar": "moveForwardSpeed" }, @@ -568,28 +568,15 @@ }, { "id": "idleToWalkFwd", - "type": "blendLinearMove", + "type": "clip", "data": { - "alpha": 0.0, - "desiredSpeed": 1.2, - "characteristicSpeeds": [1.2], - "alphaVar": 0.0, - "desiredSpeedVar": "moveForwardSpeed" + "url": "animations/idle_to_walk.fbx", + "startFrame": 1.0, + "endFrame": 13.0, + "timeScale": 1.0, + "loopFlag": false }, - "children": [ - { - "id": "idleToWalkFwdClip", - "type": "clip", - "data": { - "url": "animations/idle_to_walk.fbx", - "startFrame": 1.0, - "endFrame": 13.0, - "timeScale": 0.9, - "loopFlag": false - }, - "children": [] - } - ] + "children": [] }, { "id": "walkBwd", @@ -597,7 +584,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.6, 1.05], + "characteristicSpeeds": [0.6, 1.45], "alphaVar": "moveBackwardAlpha", "desiredSpeedVar": "moveBackwardSpeed" }, @@ -659,7 +646,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.5], + "characteristicSpeeds": [0.2, 0.65], "alphaVar": "moveLateralAlpha", "desiredSpeedVar": "moveLateralSpeed" }, @@ -696,7 +683,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.5], + "characteristicSpeeds": [0.2, 0.65], "alphaVar": "moveLateralAlpha", "desiredSpeedVar": "moveLateralSpeed" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 900d1bbb63..cff4d6a614 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3882,6 +3882,13 @@ void Application::update(float deltaTime) { if (nearbyEntitiesAreReadyForPhysics()) { _physicsEnabled = true; getMyAvatar()->updateMotionBehaviorFromMenu(); + } else { + auto characterController = getMyAvatar()->getCharacterController(); + if (characterController) { + // if we have a character controller, disable it here so the avatar doesn't get stuck due to + // a non-loading collision hull. + characterController->setEnabled(false); + } } } } @@ -4010,7 +4017,7 @@ void Application::update(float deltaTime) { avatarManager->getObjectsToChange(motionStates); _physicsEngine->changeObjects(motionStates); - myAvatar->prepareForPhysicsSimulation(deltaTime); + myAvatar->prepareForPhysicsSimulation(); _physicsEngine->forEachAction([&](EntityActionPointer action) { action->prepareForPhysicsSimulation(); }); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ea1b26dd80..dfa1a2f8b9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -498,10 +498,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisplayLeftFootTrace, 0, false, - avatar.get(), SLOT(setEnableDebugDrawLeftFootTrace(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisplayRightFootTrace, 0, false, - avatar.get(), SLOT(setEnableDebugDrawRightFootTrace(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, @@ -518,8 +514,6 @@ Menu::Menu() { avatar.get(), SLOT(setEnableInverseKinematics(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false, avatar.get(), SLOT(setEnableDebugDrawSensorToWorldMatrix(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKTargets, 0, false, - avatar.get(), SLOT(setEnableDebugDrawIKTargets(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl, Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()), @@ -529,18 +523,10 @@ Menu::Menu() { avatar.get(), SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarCollisions, 0, true, + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableCharacterController, 0, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); - // KINEMATIC_CONTROLLER_HACK - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MoveKinematically, 0, false, - avatar.get(), SLOT(updateMotionBehaviorFromMenu()), - UNSPECIFIED_POSITION, "Developer"); - - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableVerticalComfortMode, 0, false, - avatar.get(), SLOT(setEnableVerticalComfortMode(bool))); - // Developer > Hands >>> MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ad11b91796..e339da4d25 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -87,8 +87,6 @@ namespace MenuOption { const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHandTargets = "Show Hand Targets"; - const QString DisplayLeftFootTrace = "Show Left Foot Trace"; - const QString DisplayRightFootTrace = "Show Right Foot Trace"; const QString DisplayModelBounds = "Display Model Bounds"; const QString DisplayModelTriangles = "Display Model Triangles"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; @@ -98,11 +96,8 @@ namespace MenuOption { const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; - const QString EnableAvatarCollisions = "Enable Avatar Collisions"; - const QString EnableIncrementalTextureTransfer = "Enable Incremental Texture Transfer"; - const QString EnableDynamicTextureManagement = "Enable Dynamic Texture Management"; + const QString EnableCharacterController = "Enable avatar collisions"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; - const QString EnableVerticalComfortMode = "Enable Vertical Comfort Mode"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -168,7 +163,6 @@ namespace MenuOption { const QString RenderResolutionThird = "1/3"; const QString RenderResolutionQuarter = "1/4"; const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix"; - const QString RenderIKTargets = "Show IK Targets"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; @@ -197,7 +191,6 @@ namespace MenuOption { const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations"; - const QString MoveKinematically = "Move Kinematically"; // KINEMATIC_CONTROLLER_HACK const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp old mode 100755 new mode 100644 index 9a0b04aa6d..b8f02fd17e --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -61,7 +60,7 @@ using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; -const float MAX_WALKING_SPEED = 2.0f; // human walking speed +const float MAX_WALKING_SPEED = 2.6f; // human walking speed const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets additive boost below this speed const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this @@ -84,13 +83,6 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -// OUTOFBODY_HACK defined in SkeletonModel.cpp -extern glm::vec3 TRUNCATE_IK_CAPSULE_POSITION; -extern float TRUNCATE_IK_CAPSULE_LENGTH; -extern float TRUNCATE_IK_CAPSULE_RADIUS; -extern float MIN_OUT_OF_BODY_DISTANCE; -extern float MAX_OUT_OF_BODY_DISTANCE; - MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), _wasPushing(false), @@ -147,6 +139,8 @@ MyAvatar::MyAvatar(RigPointer rig) : // when we leave a domain we lift whatever restrictions that domain may have placed on our scale connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction); + _characterController.setEnabled(true); + _bodySensorMatrix = deriveBodyFromHMDSensor(); using namespace recording; @@ -376,27 +370,6 @@ void MyAvatar::update(float deltaTime) { simulate(deltaTime); - // Request to show the hand controllers if we're out-of-body for more then HAND_CONTROLLER_SHOW_TIME. - // Similarlly request to hide the controllers when we return to our bodies. - const float HAND_CONTROLLER_SHOW_TIME = 0.75f; - auto hmdInterface = DependencyManager::get(); - if (isOutOfBody() != _handControllerShow) { - _handControllerShowTimer += deltaTime; - if (_handControllerShowTimer > HAND_CONTROLLER_SHOW_TIME) { - if (isOutOfBody()) { - hmdInterface->requestShowHandControllers(); - _handControllerShow = true; - _handControllerShowTimer = 0.0f; - } else { - hmdInterface->requestHideHandControllers(); - _handControllerShow = false; - _handControllerShowTimer = 0.0f; - } - } - } else { - _handControllerShowTimer = 0.0f; - } - currentEnergy += energyChargeRate; currentEnergy -= getAccelerationEnergy(); currentEnergy -= getAudioEnergy(); @@ -464,18 +437,10 @@ void MyAvatar::simulate(float deltaTime) { // update sensorToWorldMatrix for camera and hand controllers // before we perform rig animations and IK. - - if (_characterController.getState() != CharacterController::State::Hover) { - updateSensorToWorldMatrix(_enableVerticalComfortMode ? SensorToWorldUpdateMode::VerticalComfort : SensorToWorldUpdateMode::Vertical); - } + updateSensorToWorldMatrix(); { PerformanceTimer perfTimer("skeleton"); - - if (_rig) { - _rig->setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets); - } - _skeletonModel->simulate(deltaTime); } @@ -556,8 +521,8 @@ void MyAvatar::simulate(float deltaTime) { } }); _characterController.setFlyingAllowed(flyingAllowed); - if (!ghostingAllowed && _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _characterController.setCollisionGroup(BULLET_COLLISION_GROUP_MY_AVATAR); + if (!_characterController.isEnabled() && !ghostingAllowed) { + _characterController.setEnabled(true); } } @@ -600,27 +565,11 @@ void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeV // best called at end of main loop, after physics. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. -void MyAvatar::updateSensorToWorldMatrix(SensorToWorldUpdateMode mode) { - if (mode == SensorToWorldUpdateMode::Full) { - glm::mat4 bodyToWorld = createMatFromQuatAndPos(getOrientation(), getPosition()); - setSensorToWorldMatrix(bodyToWorld * glm::inverse(_bodySensorMatrix)); - } else if (mode == SensorToWorldUpdateMode::Vertical || - mode == SensorToWorldUpdateMode::VerticalComfort) { - glm::mat4 bodyToWorld = createMatFromQuatAndPos(getOrientation(), getPosition()); - glm::mat4 newSensorToWorldMat = bodyToWorld * glm::inverse(_bodySensorMatrix); - glm::mat4 sensorToWorldMat = _sensorToWorldMatrix; - sensorToWorldMat[3][1] = newSensorToWorldMat[3][1]; - if (mode == SensorToWorldUpdateMode::VerticalComfort && - fabsf(_sensorToWorldMatrix[3][1] - newSensorToWorldMat[3][1]) > 0.1f) { - setSensorToWorldMatrix(sensorToWorldMat); - } else if (mode == SensorToWorldUpdateMode::Vertical) { - setSensorToWorldMatrix(sensorToWorldMat); - } - } -} - -void MyAvatar::setSensorToWorldMatrix(const glm::mat4& sensorToWorld) { - _sensorToWorldMatrix = sensorToWorld; +void MyAvatar::updateSensorToWorldMatrix() { + // update the sensor mat so that the body position will end up in the desired + // position when driven from the head. + glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); + _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); lateUpdatePalms(); @@ -862,14 +811,6 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) { return value; } -void MyAvatar::setEnableDebugDrawLeftFootTrace(bool isEnabled) { - _enableDebugDrawLeftFootTrace = isEnabled; -} - -void MyAvatar::setEnableDebugDrawRightFootTrace(bool isEnabled) { - _enableDebugDrawRightFootTrace = isEnabled; -} - void MyAvatar::setEnableDebugDrawDefaultPose(bool isEnabled) { _enableDebugDrawDefaultPose = isEnabled; @@ -912,11 +853,6 @@ void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) { } } -void MyAvatar::setEnableDebugDrawIKTargets(bool isEnabled) { - _enableDebugDrawIKTargets = isEnabled; -} - - void MyAvatar::setEnableMeshVisible(bool isEnabled) { render::ScenePointer scene = qApp->getMain3DScene(); _skeletonModel->setVisibleInScene(isEnabled, scene); @@ -931,10 +867,6 @@ void MyAvatar::setEnableInverseKinematics(bool isEnabled) { _rig->setEnableInverseKinematics(isEnabled); } -void MyAvatar::setEnableVerticalComfortMode(bool isEnabled) { - _enableVerticalComfortMode = isEnabled; -} - void MyAvatar::loadData() { Settings settings; settings.beginGroup("Avatar"); @@ -1282,8 +1214,6 @@ void MyAvatar::rebuildCollisionShape() { float scale = getUniformScale(); float radius = scale * _skeletonModel->getBoundingCapsuleRadius(); float height = scale * _skeletonModel->getBoundingCapsuleHeight() + 2.0f * radius; - const float CANONICAL_AVATAR_HEIGHT = 2.0f; - _canonicalScale = height / CANONICAL_AVATAR_HEIGHT; glm::vec3 corner(-radius, -0.5f * height, -radius); corner += scale * _skeletonModel->getBoundingCapsuleOffset(); glm::vec3 diagonal(2.0f * radius, height, 2.0f * radius); @@ -1341,14 +1271,10 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { } void MyAvatar::updateMotors() { - const float DEFAULT_MOTOR_TIMESCALE = 0.2f; - const float INVALID_MOTOR_TIMESCALE = 1.0e6f; - _characterController.clearMotors(); glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - if (_characterController.getState() == CharacterController::State::Hover || - _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + if (_characterController.getState() == CharacterController::State::Hover) { motorRotation = getHead()->getCameraOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift @@ -1356,18 +1282,14 @@ void MyAvatar::updateMotors() { glm::quat liftRotation; swingTwistDecomposition(getHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); } - - if (qApp->isHMDMode()) { - // OUTOFBODY_HACK: in HMDMode motors are applied differently: a "follow" motor is added - // during the CharacterController's substep + const float DEFAULT_MOTOR_TIMESCALE = 0.2f; + const float INVALID_MOTOR_TIMESCALE = 1.0e6f; + if (_isPushing || _isBraking || !_isBeingPushed) { + _characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE); } else { - if (_isPushing || _isBraking || !_isBeingPushed) { - _characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE); - } else { - // _isBeingPushed must be true --> disable action motor by giving it a long timescale, - // otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts - _characterController.addMotor(_actionMotorVelocity, motorRotation, INVALID_MOTOR_TIMESCALE); - } + // _isBeingPushed must be true --> disable action motor by giving it a long timescale, + // otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts + _characterController.addMotor(_actionMotorVelocity, motorRotation, INVALID_MOTOR_TIMESCALE); } } if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { @@ -1379,19 +1301,16 @@ void MyAvatar::updateMotors() { // world-frame motorRotation = glm::quat(); } - if (qApp->isHMDMode()) { - // OUTOFBODY_HACK: motors are applied differently in HMDMode - } else { - _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); - } + _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); } // legacy support for 'MyAvatar::applyThrust()', which has always been implemented as a // short-lived linearAcceleration - _characterController.setLinearAcceleration(glm::vec3(0.0f, _thrust.y, 0.0f)); + _characterController.setLinearAcceleration(_thrust); + _thrust = Vectors::ZERO; } -void MyAvatar::prepareForPhysicsSimulation(float deltaTime) { +void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); updateMotors(); @@ -1401,54 +1320,32 @@ void MyAvatar::prepareForPhysicsSimulation(float deltaTime) { qDebug() << "Warning: getParentVelocity failed" << getID(); parentVelocity = glm::vec3(); } - _characterController.handleChangedCollisionGroup(); _characterController.setParentVelocity(parentVelocity); - glm::vec3 position = getPosition(); - glm::quat orientation = getOrientation(); - - _characterController.setPositionAndOrientation(position, orientation); + _characterController.setPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { - // update the _bodySensorMatrix based on leaning behavior of the avatar. - _bodySensorMatrix = _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput(), deltaTime); - - // The avatar physics body always follows the _bodySensorMatrix. - glm::mat4 worldBodyMatrix = _sensorToWorldMatrix * _bodySensorMatrix; - getCharacterController()->setFollowParameters(worldBodyMatrix); + _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput()); } else { _follow.deactivate(); - getCharacterController()->disableFollow(); } _prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix); } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { - // figure out how far the hips can move before they hit something - int hipsJoint = getJointIndex("Hips"); - glm::vec3 hipsPosition; // rig-frame - // OUTOFBODY_HACK -- hardcoded maxHipsOffsetRadius (ultimately must exceed FollowHelper lateral/forward/back walk thresholds) - float maxHipsOffsetRadius = 3.0f * _characterController.getCapsuleRadius(); - if (_rig->getJointPosition(hipsJoint, hipsPosition)) { - // OUTOFBODY_HACK -- flip PI about yAxis - hipsPosition.x *= -1.0f; - hipsPosition.z *= -1.0f; - maxHipsOffsetRadius = _characterController.measureMaxHipsOffsetRadius(hipsPosition, maxHipsOffsetRadius); - } - _rig->updateMaxHipsOffsetLength(maxHipsOffsetRadius, deltaTime); - glm::vec3 position = getPosition(); glm::quat orientation = getOrientation(); if (_characterController.isEnabledAndReady()) { _characterController.getPositionAndOrientation(position, orientation); } nextAttitude(position, orientation); + _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); if (_characterController.isEnabledAndReady()) { - setVelocity(_characterController.getLinearVelocity()); + setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity()); + } else { + setVelocity(getVelocity() + _characterController.getFollowVelocity()); } - - _follow.postPhysicsUpdate(*this); } QString MyAvatar::getScriptedMotorFrame() const { @@ -1674,26 +1571,8 @@ void MyAvatar::postUpdate(float deltaTime) { DebugDraw::getInstance().updateMyAvatarPos(getPosition()); DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); - if (_enableDebugDrawLeftFootTrace || _enableDebugDrawRightFootTrace) { - int boneIndex = _enableDebugDrawLeftFootTrace ? getJointIndex("LeftFoot") : getJointIndex("RightFoot"); - const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f); - const glm::vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f); - const glm::vec4 TRANS(1.0f, 1.0f, 1.0f, 0.0f); - static bool colorBit = true; - colorBit = !colorBit; - glm::vec4 color = colorBit ? RED : WHITE; - - _debugLineLoop[_debugLineLoopIndex] = DebugDrawVertex(getJointPosition(boneIndex), color); - _debugLineLoopIndex = (_debugLineLoopIndex + 1) % DEBUG_LINE_LOOP_SIZE; - _debugLineLoop[_debugLineLoopIndex] = DebugDrawVertex(getJointPosition(boneIndex), TRANS); - for (size_t prev = DEBUG_LINE_LOOP_SIZE - 1, next = 0; next < DEBUG_LINE_LOOP_SIZE; prev = next, next++) { - if (_debugLineLoop[prev].color.w > 0.0f) { - DebugDraw::getInstance().drawRay(_debugLineLoop[prev].pos, _debugLineLoop[next].pos, _debugLineLoop[prev].color); - } - } - } - AnimPose postUpdateRoomPose(_sensorToWorldMatrix); + updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose); } @@ -1789,20 +1668,11 @@ void MyAvatar::updateOrientation(float deltaTime) { // update body orientation by movement inputs - glm::quat deltaRotation = glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))); - setOrientation(getOrientation() * deltaRotation); + setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime); if (qApp->isHMDMode()) { - - // rotate the sensorToWorldMatrix about the HMD! - glm::vec3 hmdOffset = extractTranslation(getHMDSensorMatrix()); - _sensorToWorldMatrix = (_sensorToWorldMatrix * - createMatFromQuatAndPos(glm::quat(), hmdOffset) * - createMatFromQuatAndPos(deltaRotation, glm::vec3()) * - createMatFromQuatAndPos(glm::quat(), -hmdOffset)); - glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); glm::quat bodyOrientation = getWorldBodyOrientation(); glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation; @@ -1819,7 +1689,6 @@ void MyAvatar::updateOrientation(float deltaTime) { } void MyAvatar::updateActionMotor(float deltaTime) { - bool thrustIsPushing = (glm::length2(_thrust) > EPSILON); bool scriptedMotorIsPushing = (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) && _scriptedMotorTimescale < MAX_CHARACTER_MOTOR_TIMESCALE; @@ -1859,11 +1728,11 @@ void MyAvatar::updateActionMotor(float deltaTime) { if (state == CharacterController::State::Hover) { // we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed - float motorSpeed = glm::length(glm::vec3(_actionMotorVelocity.x, _actionMotorVelocity.y, _actionMotorVelocity.z)); + float motorSpeed = glm::length(_actionMotorVelocity); float finalMaxMotorSpeed = getUniformScale() * MAX_ACTION_MOTOR_SPEED; float speedGrowthTimescale = 2.0f; float speedIncreaseFactor = 1.8f; - motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor; + motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED; if (_isPushing) { @@ -1886,21 +1755,6 @@ void MyAvatar::updateActionMotor(float deltaTime) { _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); } -void MyAvatar::applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, float deltaTime) { - glm::vec3 newVelocity = velocity; - if (_characterController.getState() != CharacterController::State::Hover) { - newVelocity -= glm::dot(newVelocity, _worldUpDirection); - } - float speed2 = glm::length2(newVelocity); - if (speed2 > MIN_AVATAR_SPEED_SQUARED) { - glm::vec3 position = extractTranslation(_sensorToWorldMatrix) + deltaTime * newVelocity; - // update the position column of matrix - glm::mat4 newSensorToWorldMatrix = _sensorToWorldMatrix; - newSensorToWorldMatrix[3] = glm::vec4(position, 1.0f); - setSensorToWorldMatrix(newSensorToWorldMatrix); - } -} - void MyAvatar::updatePosition(float deltaTime) { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { updateActionMotor(deltaTime); @@ -1921,61 +1775,11 @@ void MyAvatar::updatePosition(float deltaTime) { measureMotionDerivatives(deltaTime); _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; } else { + // physics physics simulation updated elsewhere float speed2 = glm::length2(velocity); _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; - - if (_moving) { - // scan for walkability - glm::vec3 position = getPosition(); - MyCharacterController::RayShotgunResult result; - glm::vec3 step = deltaTime * (getRotation() * _actionMotorVelocity); - _characterController.testRayShotgun(position, step, result); - _characterController.setStepUpEnabled(result.walkable); - } - - if (qApp->isHMDMode()) { - glm::quat motorRotation; - glm::vec3 worldVelocity = glm::vec3(0.0f); - if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - if (_characterController.getState() == CharacterController::State::Hover || - _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { - motorRotation = glmExtractRotation(_sensorToWorldMatrix * getHMDSensorMatrix()); - } else { - glm::quat liftRotation; - swingTwistDecomposition(glmExtractRotation(_sensorToWorldMatrix * getHMDSensorMatrix()), _worldUpDirection, liftRotation, motorRotation); - } - worldVelocity = motorRotation * _actionMotorVelocity; - } - - if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { - if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) { - motorRotation = getHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); - } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) { - motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); - } else { - // world-frame - motorRotation = glm::quat(); - } - worldVelocity += motorRotation * _scriptedMotorVelocity; - } - - // OUTOFBODY_HACK: apply scaling factor to _thrust, to get the same behavior as an periodically applied motor. - const float THRUST_DAMPING_FACTOR = 0.25f; - worldVelocity += THRUST_DAMPING_FACTOR * _thrust; - - // apply velocity directly to _sensorToWorldMatrix. - if (glm::length2(worldVelocity) > FLT_EPSILON) { - glm::mat4 worldBodyMatrix = _sensorToWorldMatrix * _bodySensorMatrix; - glm::vec3 position = extractTranslation(worldBodyMatrix); - glm::vec3 step = deltaTime * worldVelocity; - glm::vec3 newVelocity = _characterController.computeHMDStep(position, step) / deltaTime; - applyVelocityToSensorToWorldMatrix(newVelocity, deltaTime); - } - } } - _thrust = Vectors::ZERO; - // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly. if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) { _hoverReferenceCameraFacingIsCaptured = true; @@ -2028,10 +1832,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } -glm::vec3 MyAvatar::getPreActionVelocity() const { - return _characterController.getPreActionLinearVelocity(); -} - // There can be a separation between the _targetScale and the actual scale of the rendered avatar in a domain. // When the avatar enters a domain where their target scale is not allowed according to the min/max // we do not change their saved target scale. Instead, we use getDomainLimitedScale() to render the avatar @@ -2215,17 +2015,13 @@ void MyAvatar::updateMotionBehaviorFromMenu() { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } - // KINEMATIC_CONTROLLER_HACK - bool moveKinematically = menu->isOptionChecked(MenuOption::MoveKinematically); - _characterController.setMoveKinematically(moveKinematically); - - setAvatarCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions)); + setCharacterControllerEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); } -void MyAvatar::setAvatarCollisionsEnabled(bool enabled) { +void MyAvatar::setCharacterControllerEnabled(bool enabled) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setAvatarCollisionsEnabled", Q_ARG(bool, enabled)); + QMetaObject::invokeMethod(this, "setCharacterControllerEnabled", Q_ARG(bool, enabled)); return; } @@ -2237,12 +2033,11 @@ void MyAvatar::setAvatarCollisionsEnabled(bool enabled) { ghostingAllowed = zone->getGhostingAllowed(); } } - int16_t group = enabled || !ghostingAllowed ? BULLET_COLLISION_GROUP_MY_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS; - _characterController.setCollisionGroup(group); + _characterController.setEnabled(ghostingAllowed ? enabled : true); } -bool MyAvatar::getAvatarCollisionsEnabled() { - return _characterController.getCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS; +bool MyAvatar::getCharacterControllerEnabled() { + return _characterController.isEnabled(); } void MyAvatar::clearDriveKeys() { @@ -2330,10 +2125,6 @@ glm::quat MyAvatar::getOrientationForAudio() { return quat(); } -bool MyAvatar::isOutOfBody() const { - return _follow._isOutOfBody; -} - void MyAvatar::setAudioListenerMode(AudioListenerMode audioListenerMode) { if (_audioListenerMode != audioListenerMode) { _audioListenerMode = audioListenerMode; @@ -2354,52 +2145,72 @@ void MyAvatar::lateUpdatePalms() { Avatar::updatePalms(); } + +static const float FOLLOW_TIME = 0.5f; + MyAvatar::FollowHelper::FollowHelper() { deactivate(); } void MyAvatar::FollowHelper::deactivate() { - _activeBits = 0; + for (int i = 0; i < NumFollowTypes; i++) { + deactivate((FollowType)i); + } } void MyAvatar::FollowHelper::deactivate(FollowType type) { assert(type >= 0 && type < NumFollowTypes); - _activeBits &= ~(uint8_t)(0x01 << (int)type); + _timeRemaining[(int)type] = 0.0f; } void MyAvatar::FollowHelper::activate(FollowType type) { assert(type >= 0 && type < NumFollowTypes); - _activeBits |= (uint8_t)(0x01 << (int)type); + // TODO: Perhaps, the follow time should be proportional to the displacement. + _timeRemaining[(int)type] = FOLLOW_TIME; } bool MyAvatar::FollowHelper::isActive(FollowType type) const { assert(type >= 0 && type < NumFollowTypes); - return (bool)(_activeBits & (uint8_t)(0x01 << (int)type)); + return _timeRemaining[(int)type] > 0.0f; } bool MyAvatar::FollowHelper::isActive() const { - return (bool)_activeBits; + for (int i = 0; i < NumFollowTypes; i++) { + if (isActive((FollowType)i)) { + return true; + } + } + return false; } -void MyAvatar::FollowHelper::updateRotationActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { - auto cameraMode = qApp->getCamera()->getMode(); - if (cameraMode == CAMERA_MODE_THIRD_PERSON) { - deactivate(Rotation); - } else { - const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees - const float STOP_FOLLOW_ROTATION_THRESHOLD = cosf(PI / 180.0f); // 1 degree - glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); - if (isActive(Rotation)) { - if (glm::dot(myAvatar.getHMDSensorFacing(), bodyFacing) > STOP_FOLLOW_ROTATION_THRESHOLD) { - deactivate(Rotation); - } - } else if (glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD) { - activate(Rotation); +float MyAvatar::FollowHelper::getMaxTimeRemaining() const { + float max = 0.0f; + for (int i = 0; i < NumFollowTypes; i++) { + if (_timeRemaining[i] > max) { + max = _timeRemaining[i]; } } + return max; +} + +void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { + for (int i = 0; i < NumFollowTypes; i++) { + _timeRemaining[i] -= dt; + } } -void MyAvatar::FollowHelper::updateHorizontalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { +bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { + auto cameraMode = qApp->getCamera()->getMode(); + if (cameraMode == CAMERA_MODE_THIRD_PERSON) { + return false; + } else { + const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees + glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); + return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; + } +} + +bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { // -z axis of currentBodyMatrix in world space. glm::vec3 forward = glm::normalize(glm::vec3(-currentBodyMatrix[0][2], -currentBodyMatrix[1][2], -currentBodyMatrix[2][2])); @@ -2408,86 +2219,88 @@ void MyAvatar::FollowHelper::updateHorizontalActivation(const MyAvatar& myAvatar glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); float forwardLeanAmount = glm::dot(forward, offset); - float lateralLeanAmount = fabsf(glm::dot(right, offset)); + float lateralLeanAmount = glm::dot(right, offset); const float MAX_LATERAL_LEAN = 0.3f; const float MAX_FORWARD_LEAN = 0.15f; const float MAX_BACKWARD_LEAN = 0.1f; - const float MIN_LEAN = 0.02f; - if (isActive(Horizontal)) { - if (fabsf(forwardLeanAmount) < MIN_LEAN && lateralLeanAmount < MIN_LEAN) { - deactivate(Horizontal); - } - } else { - if (forwardLeanAmount > MAX_FORWARD_LEAN || - forwardLeanAmount < -MAX_BACKWARD_LEAN || - lateralLeanAmount > MAX_LATERAL_LEAN) { - activate(Horizontal); - } + if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { + return true; + } else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { + return true; } + + return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; } -void MyAvatar::FollowHelper::updateVerticalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { +bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { + const float CYLINDER_TOP = 0.1f; const float CYLINDER_BOTTOM = -1.5f; - const float MIN_VERTICAL_OFFSET = 0.02f; glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); - if (isActive(Vertical)) { - if (fabsf(offset.y) < MIN_VERTICAL_OFFSET) { - deactivate(Vertical); - } - } else if (offset.y > CYLINDER_TOP || offset.y < CYLINDER_BOTTOM) { - activate(Vertical); - } + return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); } -glm::mat4 MyAvatar::FollowHelper::prePhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput, float deltaTime) { +void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { + _desiredBodyMatrix = desiredBodyMatrix; + if (myAvatar.getHMDLeanRecenterEnabled()) { - updateRotationActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); - updateHorizontalActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); - updateVerticalActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); + if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { + activate(Rotation); + } + if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { + activate(Horizontal); + } + if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + activate(Vertical); + } + } - AnimPose currentBodyPose(currentBodyMatrix); - AnimPose desiredBodyPose(desiredBodyMatrix); - AnimPose followBodyPose(currentBodyMatrix); + glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix; + glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix; - if (isActive(Rotation) || hasDriveInput) { - followBodyPose.rot = desiredBodyPose.rot; - } - if (isActive(Horizontal) || hasDriveInput) { - followBodyPose.trans.x = desiredBodyPose.trans.x; - followBodyPose.trans.z = desiredBodyPose.trans.z; - } - if (isActive(Vertical) || hasDriveInput) { - followBodyPose.trans.y = desiredBodyPose.trans.y; - } + AnimPose followWorldPose(currentWorldMatrix); + if (isActive(Rotation)) { + followWorldPose.rot = glmExtractRotation(desiredWorldMatrix); + } + if (isActive(Horizontal)) { + glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix); + followWorldPose.trans.x = desiredTranslation.x; + followWorldPose.trans.z = desiredTranslation.z; + } + if (isActive(Vertical)) { + glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix); + followWorldPose.trans.y = desiredTranslation.y; + } + + myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining()); +} + +glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) { + if (isActive()) { + float dt = myAvatar.getCharacterController()->getFollowTime(); + decrementTimeRemaining(dt); + + // apply follow displacement to the body matrix. + glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement(); + glm::quat worldAngularDisplacement = myAvatar.getCharacterController()->getFollowAngularDisplacement(); + glm::quat sensorToWorld = glmExtractRotation(myAvatar.getSensorToWorldMatrix()); + glm::quat worldToSensor = glm::inverse(sensorToWorld); + + glm::vec3 sensorLinearDisplacement = worldToSensor * worldLinearDisplacement; + glm::quat sensorAngularDisplacement = worldToSensor * worldAngularDisplacement * sensorToWorld; + + glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), + sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); + return newBodyMat; - if (isActive() || hasDriveInput) { - const float TIMESCALE = 0.2f; - const float tau = glm::clamp(deltaTime / TIMESCALE, 0.0f, 1.0f); - AnimPose newBodyPose; - blend(1, ¤tBodyPose, &followBodyPose, tau, &newBodyPose); - return (glm::mat4)newBodyPose; - } else { - return currentBodyMatrix; - } } else { - deactivate(); return currentBodyMatrix; } } -void MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar) { - glm::mat4 worldHMDMat = myAvatar.getSensorToWorldMatrix() * myAvatar.getHMDSensorMatrix(); - glm::vec3 worldHMDPosition = extractTranslation(worldHMDMat); - glm::vec3 capsuleStart = myAvatar.getPosition() + Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = myAvatar.getPosition() - Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - _isOutOfBody = !pointIsInsideCapsule(worldHMDPosition, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - _outOfBodyDistance = distanceFromCapsule(worldHMDPosition, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); -} - float MyAvatar::getAccelerationEnergy() { glm::vec3 velocity = getVelocity(); int changeInVelocity = abs(velocity.length() - priorVelocity.length()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a5dc01b12b..f081ec533b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -56,8 +56,6 @@ enum AudioListenerMode { }; Q_DECLARE_METATYPE(AudioListenerMode); -const size_t DEBUG_LINE_LOOP_SIZE = 500; - class MyAvatar : public Avatar { Q_OBJECT Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) @@ -86,7 +84,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) - Q_PROPERTY(bool avatarCollisionsEnabled READ getAvatarCollisionsEnabled WRITE setAvatarCollisionsEnabled) + Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) public: explicit MyAvatar(RigPointer rig); @@ -110,7 +108,6 @@ public: const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; } const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; } const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } - const glm::vec2& getHMDSensorFacing() const { return _hmdSensorFacing; } const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; } Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); @@ -128,14 +125,7 @@ public: // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. - enum class SensorToWorldUpdateMode { - Full = 0, - Vertical, - VerticalComfort - }; - void updateSensorToWorldMatrix(SensorToWorldUpdateMode mode = SensorToWorldUpdateMode::Full); - - void setSensorToWorldMatrix(const glm::mat4& sensorToWorld); + void updateSensorToWorldMatrix(); void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } @@ -237,7 +227,7 @@ public: const MyCharacterController* getCharacterController() const { return &_characterController; } void updateMotors(); - void prepareForPhysicsSimulation(float deltaTime); + void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } @@ -282,14 +272,12 @@ public: bool hasDriveInput() const; - Q_INVOKABLE void setAvatarCollisionsEnabled(bool enabled); - Q_INVOKABLE bool getAvatarCollisionsEnabled(); + Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); + Q_INVOKABLE bool getCharacterControllerEnabled(); virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; - glm::vec3 getPreActionVelocity() const; - void addHoldAction(AvatarActionHold* holdAction); // thread-safe void removeHoldAction(AvatarActionHold* holdAction); // thread-safe void updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose); @@ -314,19 +302,15 @@ public slots: Q_INVOKABLE void updateMotionBehaviorFromMenu(); - void setEnableDebugDrawLeftFootTrace(bool isEnabled); - void setEnableDebugDrawRightFootTrace(bool isEnabled); void setEnableDebugDrawDefaultPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); void setEnableDebugDrawPosition(bool isEnabled); void setEnableDebugDrawHandControllers(bool isEnabled); void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); - void setEnableDebugDrawIKTargets(bool isEnabled); bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } void setEnableMeshVisible(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled); void setEnableInverseKinematics(bool isEnabled); - void setEnableVerticalComfortMode(bool isEnabled); QUrl getAnimGraphOverrideUrl() const; // thread-safe void setAnimGraphOverrideUrl(QUrl value); // thread-safe @@ -336,8 +320,6 @@ public slots: glm::vec3 getPositionForAudio(); glm::quat getOrientationForAudio(); - bool isOutOfBody() const; - signals: void audioListenerModeChanged(); void transformChanged(); @@ -390,8 +372,6 @@ private: virtual void updatePalms() override {} void lateUpdatePalms(); - void applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, float deltaTime); - void clampTargetScaleToDomainLimits(); void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; @@ -472,9 +452,8 @@ private: Vertical, NumFollowTypes }; - uint8_t _activeBits { 0 }; - bool _isOutOfBody { false }; - float _outOfBodyDistance { 0.0f }; + glm::mat4 _desiredBodyMatrix; + float _timeRemaining[NumFollowTypes]; void deactivate(); void deactivate(FollowType type); @@ -482,11 +461,13 @@ private: void activate(FollowType type); bool isActive() const; bool isActive(FollowType followType) const; - void updateRotationActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - void updateHorizontalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - void updateVerticalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - glm::mat4 prePhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput, float deltaTime); - void postPhysicsUpdate(MyAvatar& myAvatar); + float getMaxTimeRemaining() const; + void decrementTimeRemaining(float dt); + bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput); + glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); }; FollowHelper _follow; @@ -498,14 +479,10 @@ private: RigPointer _rig; bool _prevShouldDrawHead; - bool _enableDebugDrawLeftFootTrace { false }; - bool _enableDebugDrawRightFootTrace { false }; bool _enableDebugDrawDefaultPose { false }; bool _enableDebugDrawAnimPose { false }; bool _enableDebugDrawHandControllers { false }; bool _enableDebugDrawSensorToWorldMatrix { false }; - bool _enableDebugDrawIKTargets { false }; - bool _enableVerticalComfortMode { false }; AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; @@ -521,8 +498,6 @@ private: ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; bool _hmdLeanRecenterEnabled = true; - bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK - float _canonicalScale { 1.0f }; AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; @@ -540,18 +515,6 @@ private: float getEnergy(); void setEnergy(float value); bool didTeleport(); - - struct DebugDrawVertex { - DebugDrawVertex() : pos(), color() {} - DebugDrawVertex(const glm::vec3& posIn, const glm::vec4& colorIn) : pos(posIn), color(colorIn) {} - glm::vec3 pos; - glm::vec4 color; - }; - DebugDrawVertex _debugLineLoop[DEBUG_LINE_LOOP_SIZE]; - size_t _debugLineLoopIndex { 0 }; - - bool _handControllerShow { false }; - float _handControllerShowTimer { 0.0f }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp old mode 100755 new mode 100644 index 4161352e01..6e52f4a949 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -15,15 +15,11 @@ #include "MyAvatar.h" +// TODO: improve walking up steps +// TODO: make avatars able to walk up and down steps/slopes // TODO: make avatars stand on steep slope // TODO: make avatars not snag on low ceilings - -void MyCharacterController::RayShotgunResult::reset() { - hitFraction = 1.0f; - walkable = true; -} - MyCharacterController::MyCharacterController(MyAvatar* avatar) { assert(avatar); @@ -34,33 +30,37 @@ MyCharacterController::MyCharacterController(MyAvatar* avatar) { MyCharacterController::~MyCharacterController() { } -void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { - CharacterController::setDynamicsWorld(world); - if (world) { - initRayShotgun(world); - } -} - void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { _pendingFlags &= ~PENDING_FLAG_UPDATE_SHAPE; + // compute new dimensions from avatar's bounding box + float x = _boxScale.x; + float z = _boxScale.z; + _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + _halfHeight = 0.5f * _boxScale.y - _radius; + float MIN_HALF_HEIGHT = 0.1f; + if (_halfHeight < MIN_HALF_HEIGHT) { + _halfHeight = MIN_HALF_HEIGHT; + } + // NOTE: _shapeLocalOffset is already computed + if (_radius > 0.0f) { // create RigidBody if it doesn't exist if (!_rigidBody) { - btCollisionShape* shape = computeShape(); // HACK: use some simple mass property defaults for now - const btScalar DEFAULT_AVATAR_MASS = 100.0f; + const float DEFAULT_AVATAR_MASS = 100.0f; const btVector3 DEFAULT_AVATAR_INERTIA_TENSOR(30.0f, 8.0f, 30.0f); + btCollisionShape* shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _rigidBody = new btRigidBody(DEFAULT_AVATAR_MASS, nullptr, shape, DEFAULT_AVATAR_INERTIA_TENSOR); } else { btCollisionShape* shape = _rigidBody->getCollisionShape(); if (shape) { delete shape; } - shape = computeShape(); + shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _rigidBody->setCollisionShape(shape); } @@ -72,419 +72,12 @@ void MyCharacterController::updateShapeIfNecessary() { if (_state == State::Hover) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { - _rigidBody->setGravity(_gravity * _currentUp); - } - // KINEMATIC_CONTROLLER_HACK - if (_moveKinematically) { - _rigidBody->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); - } else { - _rigidBody->setCollisionFlags(_rigidBody->getCollisionFlags() & - ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)); + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } + //_rigidBody->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } else { // TODO: handle this failure case } } } -bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result) { - btVector3 rayDirection = glmToBullet(step); - btScalar stepLength = rayDirection.length(); - if (stepLength < FLT_EPSILON) { - return false; - } - rayDirection /= stepLength; - - // get _ghost ready for ray traces - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 newPosition = glmToBullet(position); - transform.setOrigin(newPosition); - _ghost.setWorldTransform(transform); - btMatrix3x3 rotation = transform.getBasis(); - _ghost.refreshOverlappingPairCache(); - - CharacterRayResult rayResult(&_ghost); - CharacterRayResult closestRayResult(&_ghost); - btVector3 rayStart; - btVector3 rayEnd; - - // compute rotation that will orient local ray start points to face step direction - btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f); - btVector3 adjustedDirection = rayDirection - rayDirection.dot(_currentUp) * _currentUp; - btVector3 axis = forward.cross(adjustedDirection); - btScalar lengthAxis = axis.length(); - if (lengthAxis > FLT_EPSILON) { - // we're walking sideways - btScalar angle = acosf(lengthAxis / adjustedDirection.length()); - if (rayDirection.dot(forward) < 0.0f) { - angle = PI - angle; - } - axis /= lengthAxis; - rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation; - } else if (rayDirection.dot(forward) < 0.0f) { - // we're walking backwards - rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation; - } - - // scan the top - // NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on. - // The approximate extra distance can be derived with trigonometry. - // - // minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ] - // - // where: theta = max angle between floor normal and vertical - // - // if stepLength is not long enough we can add the difference. - // - btScalar cosTheta = _minFloorNormalDotUp; - btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta); - const btScalar MIN_FORWARD_SLOP = 0.12f; // HACK: not sure why this is necessary to detect steepest walkable slope - btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP; - if (forwardSlop < 0.0f) { - // BIG step, no slop necessary - forwardSlop = 0.0f; - } - - const btScalar backSlop = 0.04f; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = newPosition + rotation * _topPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength + forwardSlop) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - if (result.walkable) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - result.walkable = false; - // the top scan wasn't walkable so don't bother scanning the bottom - // remove both forwardSlop and backSlop - result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength); - return result.hitFraction < 1.0f; - } - } - } - } - if (_state == State::Hover) { - // scan the bottom just like the top - for (int32_t i = 0; i < _bottomPoints.size(); ++i) { - rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength + forwardSlop) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - if (result.walkable) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - result.walkable = false; - // the bottom scan wasn't walkable - // remove both forwardSlop and backSlop - result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength); - return result.hitFraction < 1.0f; - } - } - } - } - } else { - // scan the bottom looking for nearest step point - // remove forwardSlop - result.hitFraction = (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop)) / (backSlop + stepLength); - - for (int32_t i = 0; i < _bottomPoints.size(); ++i) { - rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - } - } - // remove backSlop - // NOTE: backSlop removal can produce a NEGATIVE hitFraction! - // which means the shape is actually in interpenetration - result.hitFraction = ((closestRayResult.m_closestHitFraction * (backSlop + stepLength)) - backSlop) / stepLength; - } - return result.hitFraction < 1.0f; -} - -glm::vec3 MyCharacterController::computeHMDStep(const glm::vec3& position, const glm::vec3& step) { - btVector3 stepDirection = glmToBullet(step); - btScalar stepLength = stepDirection.length(); - if (stepLength < FLT_EPSILON) { - return glm::vec3(0.0f); - } - stepDirection /= stepLength; - - // get _ghost ready for ray traces - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 newPosition = glmToBullet(position); - transform.setOrigin(newPosition); - btMatrix3x3 rotation = transform.getBasis(); - _ghost.setWorldTransform(transform); - _ghost.refreshOverlappingPairCache(); - - // compute rotation that will orient local ray start points to face stepDirection - btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f); - btVector3 horizontalDirection = stepDirection - stepDirection.dot(_currentUp) * _currentUp; - btVector3 axis = forward.cross(horizontalDirection); - btScalar lengthAxis = axis.length(); - if (lengthAxis > FLT_EPSILON) { - // non-zero sideways component - btScalar angle = asinf(lengthAxis / horizontalDirection.length()); - if (stepDirection.dot(forward) < 0.0f) { - angle = PI - angle; - } - axis /= lengthAxis; - rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation; - } else if (stepDirection.dot(forward) < 0.0f) { - // backwards - rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation; - } - - CharacterRayResult rayResult(&_ghost); - btVector3 rayStart; - btVector3 rayEnd; - btVector3 penetration = btVector3(0.0f, 0.0f, 0.0f); - int32_t numPenetrations = 0; - - { // first we scan straight out from capsule center to see if we're stuck on anything - btScalar forwardRatio = 0.5f; - btScalar backRatio = 0.25f; - - btVector3 radial; - bool stuck = false; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = rotation * _topPoints[i]; - radial = rayStart - rayStart.dot(_currentUp) * _currentUp; - rayEnd = newPosition + rayStart + forwardRatio * radial; - rayStart += newPosition - backRatio * radial; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - btScalar totalRatio = backRatio + forwardRatio; - btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * totalRatio - backRatio) / forwardRatio; - if (adjustedHitFraction < 0.0f) { - penetration += adjustedHitFraction * radial; - ++numPenetrations; - } else { - stuck = true; - } - } - } - if (numPenetrations > 0) { - if (numPenetrations > 1) { - penetration /= (btScalar)numPenetrations; - } - return bulletToGLM(penetration); - } else if (stuck) { - return glm::vec3(0.0f); - } - } - - // if we get here then we're not stuck pushing into any surface - // so now we scan to see if the way before us is "walkable" - - // scan the top - // NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on. - // The approximate extra distance can be derived with trigonometry. - // - // minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ] - // - // where: theta = max angle between floor normal and vertical - // - // if stepLength is not long enough we can add the difference. - // - btScalar cosTheta = _minFloorNormalDotUp; - btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta); - const btScalar MIN_FORWARD_SLOP = 0.10f; // HACK: not sure why this is necessary to detect steepest walkable slope - btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP; - if (forwardSlop < 0.0f) { - // BIG step, no slop necessary - forwardSlop = 0.0f; - } - - // we push the step forward by stepMargin to help reduce accidental penetration - const btScalar MIN_STEP_MARGIN = 0.04f; - btScalar stepMargin = glm::max(_radius, MIN_STEP_MARGIN); - btScalar expandedStepLength = stepLength + forwardSlop + stepMargin; - - // loop over topPoints - bool walkable = true; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = newPosition + rotation * _topPoints[i]; - rayEnd = rayStart + expandedStepLength * stepDirection; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - walkable = false; - break; - } - } - } - - // scan the bottom - // TODO: implement sliding along sloped floors - bool steppingUp = false; - expandedStepLength = stepLength + MIN_FORWARD_SLOP + MIN_STEP_MARGIN; - for (int32_t i = _bottomPoints.size() - 1; i > -1; --i) { - rayStart = newPosition + rotation * _bottomPoints[i] - MIN_STEP_MARGIN * stepDirection; - rayEnd = rayStart + expandedStepLength * stepDirection; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * expandedStepLength - MIN_STEP_MARGIN) / (stepLength + MIN_FORWARD_SLOP); - if (adjustedHitFraction < 1.0f) { - steppingUp = true; - break; - } - } - } - - if (!walkable && steppingUp ) { - return glm::vec3(0.0f); - } - // else it might not be walkable, but we aren't steppingUp yet which means we can still move forward - - // TODO: slide up ramps and fall off edges (then we can remove the vertical follow of Avatar's RigidBody) - return step; -} - -btConvexHullShape* MyCharacterController::computeShape() const { - // HACK: the avatar collides using convex hull with a collision margin equal to - // the old capsule radius. Two points define a capsule and additional points are - // spread out at chest level to produce a slight taper toward the feet. This - // makes the avatar more likely to collide with vertical walls at a higher point - // and thus less likely to produce a single-point collision manifold below the - // _maxStepHeight when walking into against vertical surfaces --> fixes a bug - // where the "walk up steps" feature would allow the avatar to walk up vertical - // walls. - const int32_t NUM_POINTS = 6; - btVector3 points[NUM_POINTS]; - btVector3 xAxis = btVector3(1.0f, 0.0f, 0.0f); - btVector3 yAxis = btVector3(0.0f, 1.0f, 0.0f); - btVector3 zAxis = btVector3(0.0f, 0.0f, 1.0f); - points[0] = _halfHeight * yAxis; - points[1] = -_halfHeight * yAxis; - points[2] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * zAxis; - points[3] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * zAxis; - points[4] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * xAxis; - points[5] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * xAxis; - btConvexHullShape* shape = new btConvexHullShape(reinterpret_cast(points), NUM_POINTS); - shape->setMargin(_radius); - return shape; -} - -void MyCharacterController::initRayShotgun(const btCollisionWorld* world) { - // In order to trace rays out from the avatar's shape surface we need to know where the start points are in - // the local-frame. Since the avatar shape is somewhat irregular computing these points by hand is a hassle - // so instead we ray-trace backwards to the avatar to find them. - // - // We trace back a regular grid (see below) of points against the shape and keep any that hit. - // ___ - // + / + \ + - // |+ +| - // +| + | + - // |+ +| - // +| + | + - // |+ +| - // + \ + / + - // --- - // The shotgun will send rays out from these same points to see if the avatar's shape can proceed through space. - - // helper class for simple ray-traces against character - class MeOnlyResultCallback : public btCollisionWorld::ClosestRayResultCallback { - public: - MeOnlyResultCallback (btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) { - _me = me; - m_collisionFilterGroup = BULLET_COLLISION_GROUP_DYNAMIC; - m_collisionFilterMask = BULLET_COLLISION_MASK_DYNAMIC; - } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) override { - if (rayResult.m_collisionObject != _me) { - return 1.0f; - } - return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); - } - btRigidBody* _me; - }; - - const btScalar fullHalfHeight = _radius + _halfHeight; - const btScalar divisionLine = -fullHalfHeight + _maxStepHeight; // line between top and bottom - const btScalar topHeight = fullHalfHeight - divisionLine; - const btScalar slop = 0.02f; - - const int32_t NUM_ROWS = 5; // must be odd number > 1 - const int32_t NUM_COLUMNS = 5; // must be odd number > 1 - btVector3 reach = (2.0f * _radius) * btVector3(0.0f, 0.0f, 1.0f); - - { // top points - _topPoints.clear(); - _topPoints.reserve(NUM_ROWS * NUM_COLUMNS); - btScalar stepY = (topHeight - slop) / (btScalar)(NUM_ROWS - 1); - btScalar stepX = 2.0f * (_radius - slop) / (btScalar)(NUM_COLUMNS - 1); - - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 position = transform.getOrigin(); - btMatrix3x3 rotation = transform.getBasis(); - - for (int32_t i = 0; i < NUM_ROWS; ++i) { - int32_t maxJ = NUM_COLUMNS; - btScalar offsetX = -(btScalar)((NUM_COLUMNS - 1) / 2) * stepX; - if (i % 2 == 1) { - // odd rows have one less point and start a halfStep closer - maxJ -= 1; - offsetX += 0.5f * stepX; - } - for (int32_t j = 0; j < maxJ; ++j) { - btVector3 localRayEnd(offsetX + (btScalar)(j) * stepX, divisionLine + (btScalar)(i) * stepY, 0.0f); - btVector3 localRayStart = localRayEnd - reach; - MeOnlyResultCallback result(_rigidBody); - world->rayTest(position + rotation * localRayStart, position + rotation * localRayEnd, result); - if (result.m_closestHitFraction < 1.0f) { - _topPoints.push_back(localRayStart + result.m_closestHitFraction * reach); - } - } - } - } - - { // bottom points - _bottomPoints.clear(); - _bottomPoints.reserve(NUM_ROWS * NUM_COLUMNS); - - btScalar steepestStepHitHeight = (_radius + 0.04f) * (1.0f - DEFAULT_MIN_FLOOR_NORMAL_DOT_UP); - btScalar stepY = (_maxStepHeight - slop - steepestStepHitHeight) / (btScalar)(NUM_ROWS - 1); - btScalar stepX = 2.0f * (_radius - slop) / (btScalar)(NUM_COLUMNS - 1); - - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 position = transform.getOrigin(); - btMatrix3x3 rotation = transform.getBasis(); - - for (int32_t i = 0; i < NUM_ROWS; ++i) { - int32_t maxJ = NUM_COLUMNS; - btScalar offsetX = -(btScalar)((NUM_COLUMNS - 1) / 2) * stepX; - if (i % 2 == 1) { - // odd rows have one less point and start a halfStep closer - maxJ -= 1; - offsetX += 0.5f * stepX; - } - for (int32_t j = 0; j < maxJ; ++j) { - btVector3 localRayEnd(offsetX + (btScalar)(j) * stepX, (divisionLine - slop) - (btScalar)(i) * stepY, 0.0f); - btVector3 localRayStart = localRayEnd - reach; - MeOnlyResultCallback result(_rigidBody); - world->rayTest(position + rotation * localRayStart, position + rotation * localRayEnd, result); - if (result.m_closestHitFraction < 1.0f) { - _bottomPoints.push_back(localRayStart + result.m_closestHitFraction * reach); - } - } - } - } -} diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index 4c465185d6..265406bc6f 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -24,37 +24,10 @@ public: explicit MyCharacterController(MyAvatar* avatar); ~MyCharacterController (); - void setDynamicsWorld(btDynamicsWorld* world) override; - void updateShapeIfNecessary() override; - - // Sweeping a convex shape through the physics simulation can expensive when the obstacles are too complex - // (e.g. small 20k triangle static mesh) so instead as a fallback we cast several rays forward and if they - // don't hit anything we consider it a clean sweep. Hence the "Shotgun" code. - class RayShotgunResult { - public: - void reset(); - - float hitFraction { 1.0f }; - bool walkable { true }; - }; - - /// return true if RayShotgun hits anything - bool testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result); - - glm::vec3 computeHMDStep(const glm::vec3& position, const glm::vec3& step); - -protected: - void initRayShotgun(const btCollisionWorld* world); - -private: - btConvexHullShape* computeShape() const; + virtual void updateShapeIfNecessary() override; protected: MyAvatar* _avatar { nullptr }; - - // shotgun scan data - btAlignedObjectArray _topPoints; - btAlignedObjectArray _bottomPoints; }; #endif // hifi_MyCharacterController_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b1e0f31062..889f0ef36b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -23,12 +23,6 @@ #include "InterfaceLogging.h" #include "AnimDebugDraw.h" -glm::vec3 TRUNCATE_IK_CAPSULE_POSITION(0.0f, 0.0f, 0.0f); -float TRUNCATE_IK_CAPSULE_LENGTH = 1000.0f; -float TRUNCATE_IK_CAPSULE_RADIUS = 0.25f; -float MIN_OUT_OF_BODY_DISTANCE = TRUNCATE_IK_CAPSULE_RADIUS - 0.1f; -float MAX_OUT_OF_BODY_DISTANCE = TRUNCATE_IK_CAPSULE_RADIUS + 0.1f; - SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : Model(rig, parent), _owningAvatar(owningAvatar), @@ -92,6 +86,7 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle }; } + // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -112,9 +107,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::HeadParameters headParams; - glm::vec3 hmdPositionInRigSpace; - glm::vec3 truncatedHMDPositionInRigSpace; - if (qApp->isHMDMode()) { headParams.isInHMD = true; @@ -124,22 +116,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 worldToRig = glm::inverse(rigToWorld); glm::mat4 rigHMDMat = worldToRig * worldHMDMat; - hmdPositionInRigSpace = extractTranslation(rigHMDMat); - - glm::vec3 capsuleStart = Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = -Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - - // truncate head IK target if it's out of body - if (myAvatar->isOutOfBody()) { - truncatedHMDPositionInRigSpace = projectPointOntoCapsule(hmdPositionInRigSpace, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - } else { - truncatedHMDPositionInRigSpace = hmdPositionInRigSpace; - } - - headParams.rigHeadPosition = truncatedHMDPositionInRigSpace; + headParams.rigHeadPosition = extractTranslation(rigHMDMat); headParams.rigHeadOrientation = extractRotation(rigHMDMat); headParams.worldHeadOrientation = extractRotation(worldHMDMat); - } else { headParams.isInHMD = false; @@ -153,42 +132,13 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHeadParameters(headParams, deltaTime); - // OUTOFBODY_HACK: clamp horizontal component of head by maxHipsOffset. - // This is to prevent the hands from being incorrect relative to the head because - // the hips are being constrained by a small maxHipsOffset due to collision. - if (myAvatar->isOutOfBody()) { - float headOffsetLength2D = glm::length(glm::vec2(truncatedHMDPositionInRigSpace.x, truncatedHMDPositionInRigSpace.z)); - if (headOffsetLength2D > _rig->getMaxHipsOffsetLength()) { - truncatedHMDPositionInRigSpace.x *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; - truncatedHMDPositionInRigSpace.z *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; - } - } - Rig::HandParameters handParams; - // compute interp factor between in body and out of body hand positions. - glm::vec3 capsuleStart = Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = -Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - float outOfBodyAlpha = distanceFromCapsule(hmdPositionInRigSpace, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - outOfBodyAlpha = (glm::clamp(outOfBodyAlpha, MIN_OUT_OF_BODY_DISTANCE, MAX_OUT_OF_BODY_DISTANCE) - MIN_OUT_OF_BODY_DISTANCE) / - (MAX_OUT_OF_BODY_DISTANCE - MIN_OUT_OF_BODY_DISTANCE); - auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); if (leftPose.isValid()) { handParams.isLeftEnabled = true; handParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); handParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); - - // adjust hand position if head is out of body. - if (qApp->isHMDMode()) { - - // compute the out of body hand position. - glm::vec3 offset = handParams.leftPosition - hmdPositionInRigSpace; - glm::vec3 outOfBodyLeftPosition = truncatedHMDPositionInRigSpace + offset; - - // interpolate between in body and out of body hand position. - handParams.leftPosition = lerp(handParams.leftPosition, outOfBodyLeftPosition, outOfBodyAlpha); - } } else { handParams.isLeftEnabled = false; } @@ -198,17 +148,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { handParams.isRightEnabled = true; handParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); handParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); - - // adjust hand position if head is out of body. - if (qApp->isHMDMode()) { - - // compute the out of body hand position. - glm::vec3 offset = handParams.rightPosition - hmdPositionInRigSpace; - glm::vec3 outOfBodyRightPosition = truncatedHMDPositionInRigSpace + offset; - - // interpolate between in body and out of body hand position. - handParams.rightPosition = lerp(handParams.rightPosition, outOfBodyRightPosition, outOfBodyAlpha); - } } else { handParams.isRightEnabled = false; } @@ -221,7 +160,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - auto velocity = myAvatar->getPreActionVelocity(); + auto velocity = myAvatar->getLocalVelocity(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 243479428e..fb1440ebdf 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -114,21 +114,6 @@ glm::vec3 HMDScriptingInterface::getPosition() const { return glm::vec3(); } -void HMDScriptingInterface::setPosition(const glm::vec3& position) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setPosition", Qt::QueuedConnection, Q_ARG(const glm::vec3&, position)); - return; - } else { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::mat4 hmdToSensor = myAvatar->getHMDSensorMatrix(); - glm::mat4 sensorToWorld = myAvatar->getSensorToWorldMatrix(); - glm::mat4 hmdToWorld = sensorToWorld * hmdToSensor; - setTranslation(hmdToWorld, position); - sensorToWorld = hmdToWorld * glm::inverse(hmdToSensor); - myAvatar->setSensorToWorldMatrix(sensorToWorld); - } -} - glm::quat HMDScriptingInterface::getOrientation() const { if (qApp->getActiveDisplayPlugin()->isHmd()) { return glm::normalize(glm::quat_cast(getWorldHMDMatrix())); @@ -199,8 +184,3 @@ bool HMDScriptingInterface::isKeyboardVisible() { void HMDScriptingInterface::centerUI() { QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection); } - -void HMDScriptingInterface::snapToAvatar() { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->updateSensorToWorldMatrix(); -} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index f6b75ef625..c9ed7f0097 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -25,7 +25,7 @@ class QScriptEngine; class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 position READ getPosition) Q_PROPERTY(glm::quat orientation READ getOrientation) Q_PROPERTY(bool mounted READ isMounted) @@ -56,7 +56,7 @@ public: /// not be interrupted by a keyboard popup /// Returns false if there is already an active keyboard displayed. /// Clients should re-enable the keyboard when the operation is complete and ensure - /// that they balance any call to suppressKeyboard() that returns true with a corresponding + /// that they balance any call to suppressKeyboard() that returns true with a corresponding /// call to unsuppressKeyboard() within a reasonable amount of time Q_INVOKABLE bool suppressKeyboard(); @@ -69,9 +69,6 @@ public: // rotate the overlay UI sphere so that it is centered about the the current HMD position and orientation Q_INVOKABLE void centerUI(); - // snap HMD to align with Avatar's current position in world-frame - Q_INVOKABLE void snapToAvatar(); - signals: bool shouldShowHandControllersChanged(); @@ -85,10 +82,7 @@ public: private: // Get the position of the HMD glm::vec3 getPosition() const; - - // Set the position of the HMD - Q_INVOKABLE void setPosition(const glm::vec3& position); - + // Get the orientation of the HMD glm::quat getOrientation() const; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 936126bf52..52c440a14e 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -24,7 +24,7 @@ AnimBlendLinear::~AnimBlendLinear() { } -const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); @@ -33,7 +33,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con pose = AnimPose::identity; } } else if (_children.size() == 1) { - _poses = _children[0]->evaluate(animVars, context, dt, triggersOut); + _poses = _children[0]->evaluate(animVars, dt, triggersOut); } else { float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); @@ -41,7 +41,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; } @@ -51,15 +51,15 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } -void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, +void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, dt, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 0dae6aabdb..2478f9b473 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -30,7 +30,7 @@ public: AnimBlendLinear(const QString& id, float alpha); virtual ~AnimBlendLinear() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } @@ -38,7 +38,7 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; - void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt); AnimPoseVec _poses; diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 40fbb5a6f7..609b464512 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -26,7 +26,7 @@ AnimBlendLinearMove::~AnimBlendLinearMove() { } -const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { assert(_children.size() == _characteristicSpeeds.size()); @@ -43,7 +43,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const int nextPoseIndex = 0; float prevDeltaTime, nextDeltaTime; setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); } else { auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); @@ -52,7 +52,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, auto alpha = glm::fract(clampedAlpha); float prevDeltaTime, nextDeltaTime; setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); } return _poses; } @@ -62,16 +62,16 @@ const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const { return _poses; } -void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, +void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float prevDeltaTime, float nextDeltaTime) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, context, prevDeltaTime, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, prevDeltaTime, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, nextDeltaTime, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinearMove.h b/libraries/animation/src/AnimBlendLinearMove.h index 083858f873..4e04ce29cb 100644 --- a/libraries/animation/src/AnimBlendLinearMove.h +++ b/libraries/animation/src/AnimBlendLinearMove.h @@ -39,7 +39,7 @@ public: AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds); virtual ~AnimBlendLinearMove() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; } @@ -48,7 +48,7 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; - void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float prevDeltaTime, float nextDeltaTime); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 273b83743d..a5747e4f96 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -31,7 +31,7 @@ AnimClip::~AnimClip() { } -const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. _startFrame = animVars.lookup(_startFrameVar, _startFrame); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index c7e7ebf3ee..7989f6d172 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -30,7 +30,7 @@ public: AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); virtual ~AnimClip() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; } void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; } diff --git a/libraries/animation/src/AnimContext.cpp b/libraries/animation/src/AnimContext.cpp deleted file mode 100644 index c8d3e7bcda..0000000000 --- a/libraries/animation/src/AnimContext.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// AnimContext.cpp -// -// Created by Anthony J. Thibault on 9/19/16. -// Copyright (c) 2016 High Fidelity, Inc. All rights reserved. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AnimContext.h" - -AnimContext::AnimContext(bool enableDebugDrawIKTargets, const glm::mat4& geometryToRigMatrix) : - _enableDebugDrawIKTargets(enableDebugDrawIKTargets), - _geometryToRigMatrix(geometryToRigMatrix) { -} diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h deleted file mode 100644 index 3170911e14..0000000000 --- a/libraries/animation/src/AnimContext.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// AnimContext.h -// -// Created by Anthony J. Thibault on 9/19/16. -// Copyright (c) 2016 High Fidelity, Inc. All rights reserved. -// -// 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_AnimContext_h -#define hifi_AnimContext_h - -#include -#include - -class AnimContext { -public: - AnimContext(bool enableDebugDrawIKTargets, const glm::mat4& geometryToRigMatrix); - - bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; } - const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; } - -protected: - - bool _enableDebugDrawIKTargets { false }; - glm::mat4 _geometryToRigMatrix; -}; - -#endif // hifi_AnimContext_h diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 4dca0aa6a8..7985251002 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include "Rig.h" #include "ElbowConstraint.h" #include "SwingTwistConstraint.h" @@ -378,14 +376,14 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe } //virtual -const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) { +const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) { // don't call this function, call overlay() instead assert(false); return _relativePoses; } //virtual -const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay if (dt > MAX_OVERLAY_DT) { @@ -439,28 +437,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars computeTargets(animVars, targets, underPoses); } - // debug render ik targets - if (context.getEnableDebugDrawIKTargets()) { - const vec4 WHITE(1.0f); - glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - - for (auto& target : targets) { - glm::mat4 geomTargetMat = createMatFromQuatAndPos(target.getRotation(), target.getTranslation()); - glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat; - - std::string name = "ikTarget" + std::to_string(target.getIndex()); - DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), WHITE); - } - } else if (context.getEnableDebugDrawIKTargets() != _previousEnableDebugIKTargets) { - // remove markers if they were added last frame. - for (auto& target : targets) { - std::string name = "ikTarget" + std::to_string(target.getIndex()); - DebugDraw::getInstance().removeMyAvatarMarker(name); - } - } - - _previousEnableDebugIKTargets = context.getEnableDebugDrawIKTargets(); - if (targets.empty()) { // no IK targets but still need to enforce constraints std::map::iterator constraintItr = _constraints.begin(); @@ -510,13 +486,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // measure new _hipsOffset for next frame // by looking for discrepancies between where a targeted endEffector is // and where it wants to be (after IK solutions are done) - - // OUTOFBODY_HACK:use weighted average between HMD and other targets - float HMD_WEIGHT = 10.0f; - float OTHER_WEIGHT = 1.0f; - float totalWeight = 0.0f; - - glm::vec3 additionalHipsOffset = Vectors::ZERO; + glm::vec3 newHipsOffset = Vectors::ZERO; for (auto& target: targets) { int targetIndex = target.getIndex(); if (targetIndex == _headIndex && _headIndex != -1) { @@ -527,45 +497,26 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans; glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; - additionalHipsOffset += (OTHER_WEIGHT * HEAD_OFFSET_SLAVE_FACTOR) * (under- actual); - totalWeight += OTHER_WEIGHT; + newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under); } else if (target.getType() == IKTarget::Type::HmdHead) { + // we want to shift the hips to bring the head to its designated position glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; - glm::vec3 thisOffset = target.getTranslation() - actual; - glm::vec3 futureHipsOffset = _hipsOffset + thisOffset; - if (glm::length(glm::vec2(futureHipsOffset.x, futureHipsOffset.z)) < _maxHipsOffsetLength) { - // it is imperative to shift the hips and bring the head to its designated position - // so we slam newHipsOffset here and ignore all other targets - additionalHipsOffset = futureHipsOffset - _hipsOffset; - totalWeight = 0.0f; - break; - } else { - additionalHipsOffset += HMD_WEIGHT * (target.getTranslation() - actual); - totalWeight += HMD_WEIGHT; - } + _hipsOffset += target.getTranslation() - actual; + // and ignore all other targets + newHipsOffset = _hipsOffset; + break; } } else if (target.getType() == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans; glm::vec3 targetPosition = target.getTranslation(); - additionalHipsOffset += OTHER_WEIGHT * (targetPosition - actualPosition); - totalWeight += OTHER_WEIGHT; + newHipsOffset += targetPosition - actualPosition; } } - if (totalWeight > 1.0f) { - additionalHipsOffset /= totalWeight; - } // smooth transitions by relaxing _hipsOffset toward the new value - const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.10f; + const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f; - _hipsOffset += additionalHipsOffset * tau; - - // clamp the horizontal component of the hips offset - float hipsOffsetLength2D = glm::length(glm::vec2(_hipsOffset.x, _hipsOffset.z)); - if (hipsOffsetLength2D > _maxHipsOffsetLength) { - _hipsOffset.x *= _maxHipsOffsetLength / hipsOffsetLength2D; - _hipsOffset.z *= _maxHipsOffsetLength / hipsOffsetLength2D; - } + _hipsOffset += (newHipsOffset - _hipsOffset) * tau; } } } @@ -578,12 +529,6 @@ void AnimInverseKinematics::clearIKJointLimitHistory() { } } -void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) { - // OUTOFBODY_HACK: manually adjust scale here - const float METERS_TO_CENTIMETERS = 100.0f; - _maxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength; -} - RotationConstraint* AnimInverseKinematics::getConstraint(int index) { RotationConstraint* constraint = nullptr; std::map::iterator constraintItr = _constraints.find(index); diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index b61343a97b..c9560c7383 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -34,13 +34,11 @@ public: void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, const QString& typeVar); - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; void clearIKJointLimitHistory(); - void setMaxHipsOffsetLength(float maxLength); - protected: void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); @@ -85,7 +83,6 @@ protected: // experimental data for moving hips during IK glm::vec3 _hipsOffset { Vectors::ZERO }; - float _maxHipsOffsetLength { 1.0f }; int _headIndex { -1 }; int _hipsIndex { -1 }; int _hipsParentIndex { -1 }; @@ -93,8 +90,6 @@ protected: // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm int _maxTargetIndex { 0 }; - - bool _previousEnableDebugIKTargets { false }; }; #endif // hifi_AnimInverseKinematics_h diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 37e239f3e1..3eedec5dbd 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -22,11 +22,11 @@ AnimManipulator::~AnimManipulator() { } -const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { - return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeBindPoses()); +const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { + return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses()); } -const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { _alpha = animVars.lookup(_alphaVar, _alpha); _poses = underPoses; diff --git a/libraries/animation/src/AnimManipulator.h b/libraries/animation/src/AnimManipulator.h index 26f50a7dd9..8534b9c269 100644 --- a/libraries/animation/src/AnimManipulator.h +++ b/libraries/animation/src/AnimManipulator.h @@ -22,8 +22,8 @@ public: AnimManipulator(const QString& id, float alpha); virtual ~AnimManipulator() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 10db38f42e..23f2e1c7b3 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -20,7 +20,6 @@ #include "AnimSkeleton.h" #include "AnimVariant.h" -#include "AnimContext.h" class QJsonObject; @@ -73,10 +72,9 @@ public: AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) = 0; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, - const AnimPoseVec& underPoses) { - return evaluate(animVars, context, dt, triggersOut); + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) = 0; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { + return evaluate(animVars, dt, triggersOut); } void setCurrentFrame(float frame); diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index dbc635af66..8f60b972ce 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -39,7 +39,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) { } } -const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. // NOTE: switching bonesets can be an expensive operation, let's try to avoid it. @@ -51,8 +51,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() >= 2) { - auto& underPoses = _children[1]->evaluate(animVars, context, dt, triggersOut); - auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses); + auto& underPoses = _children[1]->evaluate(animVars, dt, triggersOut); + auto& overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses); if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 2f34c07309..eda8847d40 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -43,7 +43,7 @@ public: AnimOverlay(const QString& id, BoneSet boneSet, float alpha); virtual ~AnimOverlay() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; } void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 4e86b92c0b..41d8a94b0a 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -21,7 +21,7 @@ AnimStateMachine::~AnimStateMachine() { } -const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); if (_currentState->getID() != desiredStateID) { @@ -29,7 +29,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co bool foundState = false; for (auto& state : _states) { if (state->getID() == desiredStateID) { - switchState(animVars, context, state); + switchState(animVars, state); foundState = true; break; } @@ -42,7 +42,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co // evaluate currentState transitions auto desiredState = evaluateTransitions(animVars); if (desiredState != _currentState) { - switchState(animVars, context, desiredState); + switchState(animVars, desiredState); } assert(_currentState); @@ -62,7 +62,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co } else if (_interpType == InterpType::SnapshotPrev) { // interp between the prev snapshot and evaluated next target. // this is useful for interping into a blend - localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut); + localNextPoses = currentStateNode->evaluate(animVars, dt, triggersOut); prevPoses = &_prevPoses; nextPoses = &localNextPoses; } else { @@ -79,7 +79,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co } } if (!_duringInterp) { - _poses = currentStateNode->evaluate(animVars, context, dt, triggersOut); + _poses = currentStateNode->evaluate(animVars, dt, triggersOut); } return _poses; } @@ -92,7 +92,7 @@ void AnimStateMachine::addState(State::Pointer state) { _states.push_back(state); } -void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimContext& context, State::Pointer desiredState) { +void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) { const float FRAMES_PER_SECOND = 30.0f; @@ -114,7 +114,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon _prevPoses = _poses; // snapshot next pose at the target frame. nextStateNode->setCurrentFrame(desiredState->_interpTarget); - _nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers); + _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); } else if (_interpType == InterpType::SnapshotPrev) { // snapshot previoius pose _prevPoses = _poses; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 711326a9ae..d92b94d1b5 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -113,7 +113,7 @@ public: explicit AnimStateMachine(const QString& id); virtual ~AnimStateMachine() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; } @@ -123,7 +123,7 @@ protected: void addState(State::Pointer state); - void switchState(const AnimVariantMap& animVars, const AnimContext& context, State::Pointer desiredState); + void switchState(const AnimVariantMap& animVars, State::Pointer desiredState); State::Pointer evaluateTransitions(const AnimVariantMap& animVars) const; // for AnimDebugDraw rendering diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3276b6d6ba..45790524d1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -298,6 +298,7 @@ void Rig::clearJointAnimationPriority(int index) { void Rig::clearIKJointLimitHistory() { if (_animNode) { _animNode->traverse([&](AnimNode::Pointer node) { + // only report clip nodes as valid roles. auto ikNode = std::dynamic_pointer_cast(node); if (ikNode) { ikNode->clearIKJointLimitHistory(); @@ -307,30 +308,6 @@ void Rig::clearIKJointLimitHistory() { } } -void Rig::updateMaxHipsOffsetLength(float maxLength, float deltaTime) { - - _desiredMaxHipsOffsetLength = maxLength; - - // OUTOFBODY_HACK: smoothly update update _hipsOffsetLength, otherwise we risk introducing oscillation in the hips offset. - const float MAX_HIPS_OFFSET_TIMESCALE = 0.33f; - float tau = deltaTime / MAX_HIPS_OFFSET_TIMESCALE; - _maxHipsOffsetLength = (1.0f - tau) * _maxHipsOffsetLength + tau * _desiredMaxHipsOffsetLength; - - if (_animNode) { - _animNode->traverse([&](AnimNode::Pointer node) { - auto ikNode = std::dynamic_pointer_cast(node); - if (ikNode) { - ikNode->setMaxHipsOffsetLength(_maxHipsOffsetLength); - } - return true; - }); - } -} - -float Rig::getMaxHipsOffsetLength() const { - return _maxHipsOffsetLength; -} - int Rig::getJointParentIndex(int childIndex) const { if (_animSkeleton && isIndexValid(childIndex)) { return _animSkeleton->getParentIndex(childIndex); @@ -518,22 +495,15 @@ bool Rig::getRelativeDefaultJointTranslation(int index, glm::vec3& translationOu } // animation reference speeds. -static const std::vector FORWARD_SPEEDS = { 0.4f, 1.3f, 4.5f }; // m/s -static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.05f }; // m/s -static const std::vector LATERAL_SPEEDS = { 0.2f, 0.5f }; // m/s -static const float DEFAULT_AVATAR_EYE_HEIGHT = 1.65f; // movement speeds are for characters of this eye-height. ~170 cm tall. +static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s +static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s +static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { glm::vec3 front = worldRotation * IDENTITY_FRONT; glm::vec3 workingVelocity = worldVelocity; - // TODO: account for avatar scaling - int eyeJoint = indexOfJoint("LeftEye"); - int toeJoint = indexOfJoint("LeftToeBase"); - const float AVATAR_EYE_HEIGHT = (eyeJoint >= 0 && toeJoint >= 0) ? getAbsoluteDefaultPose(eyeJoint).trans.y - getAbsoluteDefaultPose(toeJoint).trans.y : DEFAULT_AVATAR_EYE_HEIGHT; - const float AVATAR_HEIGHT_RATIO = DEFAULT_AVATAR_EYE_HEIGHT / AVATAR_EYE_HEIGHT; - { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; @@ -553,22 +523,18 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float moveBackwardAlpha = 0.0f; float moveLateralAlpha = 0.0f; - float averageForwardSpeed = AVATAR_HEIGHT_RATIO * _averageForwardSpeed.getAverage(); - float averageBackwardSpeed = -averageForwardSpeed; - float averageLateralSpeed = AVATAR_HEIGHT_RATIO * fabsf(_averageLateralSpeed.getAverage()); - // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. - calcAnimAlpha(averageForwardSpeed, FORWARD_SPEEDS, &moveForwardAlpha); - calcAnimAlpha(averageBackwardSpeed, BACKWARD_SPEEDS, &moveBackwardAlpha); - calcAnimAlpha(averageLateralSpeed, LATERAL_SPEEDS, &moveLateralAlpha); + calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha); + calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha); + calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha); - _animVars.set("moveForwardSpeed", averageForwardSpeed); + _animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage()); _animVars.set("moveForwardAlpha", moveForwardAlpha); - _animVars.set("moveBackwardSpeed", averageBackwardSpeed); + _animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage()); _animVars.set("moveBackwardAlpha", moveBackwardAlpha); - _animVars.set("moveLateralSpeed", averageLateralSpeed); + _animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage())); _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec @@ -626,7 +592,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } } - const float STATE_CHANGE_HYSTERESIS_TIMER = 1.0f / 60.0f; + const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; // Skip hystersis timer for jump transitions. if (_desiredState == RigRole::Takeoff) { @@ -925,11 +891,9 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); - AnimContext context(_enableDebugDrawIKTargets, getGeometryToRigTransform()); - // evaluate the animation AnimNode::Triggers triggersOut; - _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); + _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -1058,6 +1022,20 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { 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("headType", (int)IKTarget::Type::HmdHead); @@ -1130,9 +1108,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isLeftEnabled) { - glm::vec3 handPosition = params.leftPosition; - // prevent the hand IK targets from intersecting the body capsule + glm::vec3 handPosition = params.leftPosition; glm::vec3 displacement; if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) { handPosition -= displacement; @@ -1149,9 +1126,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isRightEnabled) { - glm::vec3 handPosition = params.rightPosition; - // prevent the hand IK targets from intersecting the body capsule + glm::vec3 handPosition = params.rightPosition; glm::vec3 displacement; if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) { handPosition -= displacement; @@ -1403,10 +1379,9 @@ void Rig::computeAvatarBoundingCapsule( // call overlay twice: once to verify AnimPoseVec joints and again to do the IK AnimNode::Triggers triggersOut; - AnimContext context(false, glm::mat4()); float dt = 1.0f; // the value of this does not matter - ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); - AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + AnimPoseVec finalPoses = ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); // convert relative poses to absolute _animSkeleton->convertRelativePosesToAbsolute(finalPoses); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2c43d83876..151a7ae8e9 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -104,8 +104,6 @@ public: void clearJointAnimationPriority(int index); void clearIKJointLimitHistory(); - void updateMaxHipsOffsetLength(float maxLength, float deltaTime); - float getMaxHipsOffsetLength() const; int getJointParentIndex(int childIndex) const; @@ -215,8 +213,6 @@ public: const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } - void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; } - signals: void onLoadComplete(); @@ -319,12 +315,6 @@ protected: bool _enableInverseKinematics { true }; mutable uint32_t _jointNameWarningCount { 0 }; - glm::vec3 _desiredRigHeadPosition; - bool _truncateIKTargets { false }; - bool _enableDebugDrawIKTargets { false }; - - float _maxHipsOffsetLength { 1.0f }; - float _desiredMaxHipsOffsetLength { 1.0f }; private: QMap _stateHandlers; diff --git a/libraries/physics/src/BulletUtil.h b/libraries/physics/src/BulletUtil.h index c456ed8af8..b6fac74617 100644 --- a/libraries/physics/src/BulletUtil.h +++ b/libraries/physics/src/BulletUtil.h @@ -1,6 +1,6 @@ // // BulletUtil.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.02 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp old mode 100755 new mode 100644 index e15a1eeaf0..5c85f8fc50 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,6 +1,6 @@ // // CharacterControllerInterface.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.10.21 // Copyright 2015 High Fidelity, Inc. @@ -13,8 +13,8 @@ #include +#include "PhysicsCollisionGroups.h" #include "ObjectMotionState.h" -#include "PhysicsHelpers.h" #include "PhysicsLogging.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); @@ -62,10 +62,15 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const } CharacterController::CharacterController() { + _halfHeight = 1.0f; + + _enabled = false; + _floorDistance = MAX_FALL_HEIGHT; _targetVelocity.setValue(0.0f, 0.0f, 0.0f); _followDesiredBodyTransform.setIdentity(); + _followTimeRemaining = 0.0f; _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; @@ -73,6 +78,9 @@ CharacterController::CharacterController() { _takeoffToInAirStartTime = 0; _jumpButtonDownStartTime = 0; _jumpButtonDownCount = 0; + _followTime = 0.0f; + _followLinearDisplacement = btVector3(0, 0, 0); + _followAngularDisplacement = btQuaternion::getIdentity(); _hasSupport = false; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; @@ -99,7 +107,6 @@ bool CharacterController::needsAddition() const { void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { - // remove from old world if (_dynamicsWorld) { if (_rigidBody) { _dynamicsWorld->removeRigidBody(_rigidBody); @@ -108,26 +115,16 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld = nullptr; } if (world && _rigidBody) { - // add to new world _dynamicsWorld = world; _pendingFlags &= ~PENDING_FLAG_JUMP; - _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); + // Before adding the RigidBody to the world we must save its oldGravity to the side + // because adding an object to the world will overwrite it with the default gravity. + btVector3 oldGravity = _rigidBody->getGravity(); + _dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); - // restore gravity settings because adding an object to the world overwrites its gravity setting - _rigidBody->setGravity(_gravity * _currentUp); - btCollisionShape* shape = _rigidBody->getCollisionShape(); - assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); - _ghost.setCharacterShape(static_cast(shape)); + // restore gravity settings + _rigidBody->setGravity(oldGravity); } - // KINEMATIC_CONTROLLER_HACK - _ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup)); - _ghost.setCollisionWorld(_dynamicsWorld); - _ghost.setRadiusAndHalfHeight(_radius, _halfHeight); - _ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK - _ghost.setMinWallAngle(PI / 4.0f); // HACK - _ghost.setUpDirection(_currentUp); - _ghost.setMotorOnly(!_moveKinematically); - _ghost.setWorldTransform(_rigidBody->getWorldTransform()); } if (_dynamicsWorld) { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { @@ -141,84 +138,38 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } } -bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btScalar dt) { - if (_moveKinematically) { - // kinematic motion will move() the _ghost later - return _ghost.hasSupport(); - } - - bool pushing = _targetVelocity.length2() > FLT_EPSILON; - - btDispatcher* dispatcher = collisionWorld->getDispatcher(); - int numManifolds = dispatcher->getNumManifolds(); - bool hasFloor = false; - - btTransform rotation = _rigidBody->getWorldTransform(); - rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part +static const float COS_PI_OVER_THREE = cosf(PI / 3.0f); +bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const { + int numManifolds = collisionWorld->getDispatcher()->getNumManifolds(); for (int i = 0; i < numManifolds; i++) { - btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); - if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) { - bool characterIsFirst = _rigidBody == contactManifold->getBody0(); + btPersistentManifold* contactManifold = collisionWorld->getDispatcher()->getManifoldByIndexInternal(i); + const btCollisionObject* obA = static_cast(contactManifold->getBody0()); + const btCollisionObject* obB = static_cast(contactManifold->getBody1()); + if (obA == _rigidBody || obB == _rigidBody) { int numContacts = contactManifold->getNumContacts(); - int stepContactIndex = -1; - float highestStep = _minStepHeight; for (int j = 0; j < numContacts; j++) { - // check for "floor" - btManifoldPoint& contact = contactManifold->getContactPoint(j); - btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame - btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character - btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp); - if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) { - hasFloor = true; - if (!pushing) { - // we're not pushing against anything so we can early exit - // (all we need to know is that there is a floor) - break; - } + btManifoldPoint& pt = contactManifold->getContactPoint(j); + + // check to see if contact point is touching the bottom sphere of the capsule. + // and the contact normal is not slanted too much. + float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY(); + btVector3 normal = (obA == _rigidBody) ? pt.m_normalWorldOnB : -pt.m_normalWorldOnB; + if (contactPointY < -_halfHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) { + return true; } - if (pushing && _targetVelocity.dot(normal) < 0.0f) { - // remember highest step obstacle - if (!_stepUpEnabled || hitHeight > _maxStepHeight) { - // this manifold is invalidated by point that is too high - stepContactIndex = -1; - break; - } else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) { - highestStep = hitHeight; - stepContactIndex = j; - hasFloor = true; - } - } - } - if (stepContactIndex > -1 && highestStep > _stepHeight) { - // remember step info for later - btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex); - btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame - _stepNormal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character - _stepHeight = highestStep; - _stepPoint = rotation * pointOnCharacter; // rotate into world-frame - } - if (hasFloor && !(pushing && _stepUpEnabled)) { - // early exit since all we need to know is that we're on a floor - break; } } } - return hasFloor; -} - -void CharacterController::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) { - _preActionVelocity = getLinearVelocity(); - preStep(collisionWorld); - playerStep(collisionWorld, deltaTime); + return false; } void CharacterController::preStep(btCollisionWorld* collisionWorld) { // trace a ray straight down to see if we're standing on the ground - const btTransform& transform = _rigidBody->getWorldTransform(); + const btTransform& xform = _rigidBody->getWorldTransform(); // rayStart is at center of bottom sphere - btVector3 rayStart = transform.getOrigin() - _halfHeight * _currentUp; + btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp; // rayEnd is some short distance outside bottom sphere const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; @@ -232,117 +183,54 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; } + + _hasSupport = checkForSupport(collisionWorld); } const btScalar MIN_TARGET_SPEED = 0.001f; const btScalar MIN_TARGET_SPEED_SQUARED = MIN_TARGET_SPEED * MIN_TARGET_SPEED; -void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { - _stepHeight = _minStepHeight; // clears memory of last step obstacle +void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { btVector3 velocity = _rigidBody->getLinearVelocity() - _parentVelocity; - if (_following) { - _followTimeAccumulator += dt; - // linear part uses a motor - const float MAX_WALKING_SPEED = 30.5f; // TODO: scale this stuff with avatar size - const float MAX_WALKING_SPEED_DISTANCE = 1.0f; - const float NORMAL_WALKING_SPEED = 0.5f * MAX_WALKING_SPEED; - const float NORMAL_WALKING_SPEED_DISTANCE = 0.5f * MAX_WALKING_SPEED_DISTANCE; - const float FEW_SUBSTEPS = 4.0f * dt; + computeNewVelocity(dt, velocity); + _rigidBody->setLinearVelocity(velocity + _parentVelocity); + + // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. + // Rather than add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. + // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). + + const float MINIMUM_TIME_REMAINING = 0.005f; + const float MAX_DISPLACEMENT = 0.5f * _radius; + _followTimeRemaining -= dt; + if (_followTimeRemaining >= MINIMUM_TIME_REMAINING) { btTransform bodyTransform = _rigidBody->getWorldTransform(); + btVector3 startPos = bodyTransform.getOrigin(); btVector3 deltaPos = _followDesiredBodyTransform.getOrigin() - startPos; - btScalar deltaDistance = deltaPos.length(); - const float MIN_DELTA_DISTANCE = 0.01f; // TODO: scale by avatar size but cap at (NORMAL_WALKING_SPEED * FEW_SUBSTEPS) - if (deltaDistance > MIN_DELTA_DISTANCE) { - btVector3 vel = deltaPos; - if (_state == State::Hover) { - btScalar HOVER_FOLLOW_VELOCITY_TIMESCALE = 0.1f; - vel /= HOVER_FOLLOW_VELOCITY_TIMESCALE; - } else { - if (deltaDistance > MAX_WALKING_SPEED_DISTANCE) { - // cap max speed - vel *= MAX_WALKING_SPEED / deltaDistance; - } else if (deltaDistance > NORMAL_WALKING_SPEED_DISTANCE) { - // linearly interpolate to NORMAL_WALKING_SPEED - btScalar alpha = (deltaDistance - NORMAL_WALKING_SPEED_DISTANCE) / (MAX_WALKING_SPEED_DISTANCE - NORMAL_WALKING_SPEED_DISTANCE); - vel *= NORMAL_WALKING_SPEED * (1.0f - alpha) + MAX_WALKING_SPEED * alpha; - } else { - // use exponential decay but cap at NORMAL_WALKING_SPEED - vel /= FEW_SUBSTEPS; - btScalar speed = vel.length(); - if (speed > NORMAL_WALKING_SPEED) { - vel *= NORMAL_WALKING_SPEED / speed; - } - } - } - vel += _followVelocity; - const float HORIZONTAL_FOLLOW_TIMESCALE = 0.05f; - float verticalFollowTimescale = 20.0f; - if (_state == State::Hover) { - verticalFollowTimescale = HORIZONTAL_FOLLOW_TIMESCALE; - } else { - // remove vertical component - vel -= vel.dot(_currentUp) * _currentUp; - } - glm::quat worldFrameRotation; // identity - addMotor(bulletToGLM(vel), worldFrameRotation, HORIZONTAL_FOLLOW_TIMESCALE, verticalFollowTimescale); - } + btVector3 vel = deltaPos / _followTimeRemaining; + btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. + btVector3 endPos = startPos + linearDisplacement; - // angular part uses incremental teleports - const float ANGULAR_FOLLOW_TIMESCALE = 0.8f; - const float MAX_ANGULAR_SPEED = (PI / 2.0f) / ANGULAR_FOLLOW_TIMESCALE; btQuaternion startRot = bodyTransform.getRotation(); glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot)); - glm::vec2 currentRight(currentFacing.y, - currentFacing.x); + glm::vec2 currentRight(currentFacing.y, -currentFacing.x); glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation())); float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f)); - float angularSpeed = deltaAngle / FEW_SUBSTEPS; - if (angularSpeed > MAX_ANGULAR_SPEED) { - angularSpeed *= MAX_ANGULAR_SPEED / angularSpeed; - } + float angularSpeed = deltaAngle / _followTimeRemaining; float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight)); btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt); btQuaternion endRot = angularDisplacement * startRot; - _rigidBody->setWorldTransform(btTransform(endRot, startPos)); - } - - _hasSupport = checkForSupport(collisionWorld, dt); - computeNewVelocity(dt, velocity); - - if (_moveKinematically) { - // KINEMATIC_CONTROLLER_HACK - btTransform transform = _rigidBody->getWorldTransform(); - transform.setOrigin(_ghost.getWorldTransform().getOrigin()); - _ghost.setWorldTransform(transform); - _ghost.setMotorVelocity(_targetVelocity); - float overshoot = 1.0f * _radius; - _ghost.move(dt, overshoot, _gravity); - transform.setOrigin(_ghost.getWorldTransform().getOrigin()); - _rigidBody->setWorldTransform(transform); - _rigidBody->setLinearVelocity(_ghost.getLinearVelocity()); - } else { - float stepUpSpeed2 = _stepUpVelocity.length2(); - if (stepUpSpeed2 > FLT_EPSILON) { - // we step up with teleports rather than applying velocity - // use a speed that would ballistically reach _stepHeight under gravity - _stepUpVelocity /= sqrtf(stepUpSpeed2); - btScalar minStepUpSpeed = sqrtf(fabsf(2.0f * _gravity * _stepHeight)); - - btTransform transform = _rigidBody->getWorldTransform(); - transform.setOrigin(transform.getOrigin() + (dt * minStepUpSpeed) * _stepUpVelocity); - _rigidBody->setWorldTransform(transform); - - // make sure the upward velocity is large enough to clear the very top of the step - const btScalar MAGIC_STEP_OVERSHOOT_SPEED_COEFFICIENT = 0.5f; - minStepUpSpeed = MAGIC_STEP_OVERSHOOT_SPEED_COEFFICIENT * sqrtf(fabsf(2.0f * _gravity * _minStepHeight)); - btScalar vDotUp = velocity.dot(_currentUp); - if (vDotUp < minStepUpSpeed) { - velocity += (minStepUpSpeed - vDotUp) * _stepUpVelocity; - } - } - _rigidBody->setLinearVelocity(velocity + _parentVelocity); - _ghost.setWorldTransform(_rigidBody->getWorldTransform()); + + // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. + btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); + btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); + + _followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; + _followAngularDisplacement = angularDisplacement * _followAngularDisplacement; + + _rigidBody->setWorldTransform(btTransform(endRot, endPos)); } + _followTime += dt; } void CharacterController::jump() { @@ -384,96 +272,95 @@ void CharacterController::setState(State desiredState) { #ifdef DEBUG_STATE_CHANGE qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif - if (_rigidBody) { - if (desiredState == State::Hover && _state != State::Hover) { - // hover enter + if (desiredState == State::Hover && _state != State::Hover) { + // hover enter + if (_rigidBody) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else if (_state == State::Hover && desiredState != State::Hover) { - // hover exit - if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - _rigidBody->setGravity(_gravity * _currentUp); - } + } + } else if (_state == State::Hover && desiredState != State::Hover) { + // hover exit + if (_rigidBody) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } } _state = desiredState; } } -void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { - float x = scale.x; - float z = scale.z; +void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { + _boxScale = scale; + + float x = _boxScale.x; + float z = _boxScale.z; float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); - float halfHeight = 0.5f * scale.y - radius; + float halfHeight = 0.5f * _boxScale.y - radius; float MIN_HALF_HEIGHT = 0.1f; if (halfHeight < MIN_HALF_HEIGHT) { halfHeight = MIN_HALF_HEIGHT; } // compare dimensions - if (glm::abs(radius - _radius) > FLT_EPSILON || glm::abs(halfHeight - _halfHeight) > FLT_EPSILON) { - _radius = radius; - _halfHeight = halfHeight; - const btScalar DEFAULT_MIN_STEP_HEIGHT = 0.041f; // HACK: hardcoded now but should just larger than shape margin - const btScalar MAX_STEP_FRACTION_OF_HALF_HEIGHT = 0.56f; - _minStepHeight = DEFAULT_MIN_STEP_HEIGHT; - _maxStepHeight = MAX_STEP_FRACTION_OF_HALF_HEIGHT * (_halfHeight + _radius); - + float radiusDelta = glm::abs(radius - _radius); + float heightDelta = glm::abs(halfHeight - _halfHeight); + if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { + // shape hasn't changed --> nothing to do + } else { if (_dynamicsWorld) { // must REMOVE from world prior to shape update _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; } _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + // only need to ADD back when we happen to be enabled + if (_enabled) { + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } } // it's ok to change offset immediately -- there are no thread safety issues here - _shapeLocalOffset = minCorner + 0.5f * scale; + _shapeLocalOffset = corner + 0.5f * _boxScale; } -void CharacterController::setCollisionGroup(int16_t group) { - if (_collisionGroup != group) { - _collisionGroup = group; - _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; - _ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup)); - } -} - -void CharacterController::handleChangedCollisionGroup() { - if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_GROUP) { - // ATM the easiest way to update collision groups is to remove/re-add the RigidBody - if (_dynamicsWorld) { - _dynamicsWorld->removeRigidBody(_rigidBody); - _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); - } - _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; - - if (_state != State::Hover && _rigidBody) { - _gravity = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0.0f : DEFAULT_CHARACTER_GRAVITY; - _rigidBody->setGravity(_gravity * _currentUp); +void CharacterController::setEnabled(bool enabled) { + if (enabled != _enabled) { + if (enabled) { + // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. + // Setting the ADD bit here works for all cases so we don't even bother checking other bits. + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } else { + if (_dynamicsWorld) { + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; } + SET_STATE(State::Hover, "setEnabled"); + _enabled = enabled; } } void CharacterController::updateUpAxis(const glm::quat& rotation) { + btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - _ghost.setUpDirection(_currentUp); - if (_state != State::Hover && _rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + if (_state != State::Hover) { + const btScalar MIN_UP_ERROR = 0.01f; + if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } } } void CharacterController::setPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation) { + // TODO: update gravity if up has changed updateUpAxis(orientation); - _rotation = glmToBullet(orientation); - _position = glmToBullet(position + orientation * _shapeLocalOffset); + + btQuaternion bodyOrientation = glmToBullet(orientation); + btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset); + _characterBodyTransform = btTransform(bodyOrientation, bodyPosition); } void CharacterController::getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { - if (_rigidBody) { + if (_enabled && _rigidBody) { const btTransform& avatarTransform = _rigidBody->getWorldTransform(); rotation = bulletToGLM(avatarTransform.getRotation()); position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; @@ -484,43 +371,25 @@ void CharacterController::setParentVelocity(const glm::vec3& velocity) { _parentVelocity = glmToBullet(velocity); } -void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix) { +void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix, float timeRemaining) { + _followTimeRemaining = timeRemaining; _followDesiredBodyTransform = glmToBullet(desiredWorldBodyMatrix) * btTransform(btQuaternion::getIdentity(), glmToBullet(_shapeLocalOffset)); - if (!_following) { - _following = true; - _followVelocity = btVector3(0.0f, 0.0f, 0.0f); - } else if (_followTimeAccumulator > 0.0f) { - btVector3 newFollowVelocity = (_followDesiredBodyTransform.getOrigin() - _previousFollowPosition) / _followTimeAccumulator; - const float dontDivideByZero = 0.001f; - float newSpeed = newFollowVelocity.length() + dontDivideByZero; - float oldSpeed = _followVelocity.length(); +} - bool successfulSnap = false; - btVector3 offset = _followDesiredBodyTransform.getOrigin() - _position; - const btScalar MAX_FOLLOW_OFFSET = 10.0f * _radius; - if (offset.length() > MAX_FOLLOW_OFFSET) { - successfulSnap = queryPenetration(_followDesiredBodyTransform); - if (successfulSnap) { - _position = _followDesiredBodyTransform.getOrigin(); - } - } +glm::vec3 CharacterController::getFollowLinearDisplacement() const { + return bulletToGLM(_followLinearDisplacement); +} - const float VERY_SLOW_HOVER_SPEED = 0.25f; - const float FAST_CHANGE_SPEED_RATIO = 100.0f; - if (successfulSnap || (oldSpeed / newSpeed > FAST_CHANGE_SPEED_RATIO && newSpeed < VERY_SLOW_HOVER_SPEED)) { - // character is snapping to avatar position or avatar is stopping quickly - // HACK: slam _followVelocity and _rigidBody velocity immediately - _followVelocity = newFollowVelocity; - newFollowVelocity.setY(0.0f); - _rigidBody->setLinearVelocity(newFollowVelocity); - } else { - // use simple blending to filter noise from the velocity measurement - const float blend = 0.2f; - _followVelocity = (1.0f - blend) * _followVelocity + blend * newFollowVelocity; - } +glm::quat CharacterController::getFollowAngularDisplacement() const { + return bulletToGLM(_followAngularDisplacement); +} + +glm::vec3 CharacterController::getFollowVelocity() const { + if (_followTime > 0.0f) { + return bulletToGLM(_followLinearDisplacement) / _followTime; + } else { + return glm::vec3(); } - _previousFollowPosition = _followDesiredBodyTransform.getOrigin(); - _followTimeAccumulator = 0.0f; } glm::vec3 CharacterController::getLinearVelocity() const { @@ -531,10 +400,6 @@ glm::vec3 CharacterController::getLinearVelocity() const { return velocity; } -glm::vec3 CharacterController::getPreActionLinearVelocity() const { - return _preActionVelocity; -} - glm::vec3 CharacterController::getVelocityChange() const { if (_rigidBody) { return bulletToGLM(_velocityChange); @@ -563,18 +428,16 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel btScalar angle = motor.rotation.getAngle(); btVector3 velocity = worldVelocity.rotate(axis, -angle); - if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || - _state == State::Hover || motor.hTimescale == motor.vTimescale) { + if (_state == State::Hover || motor.hTimescale == motor.vTimescale) { // modify velocity btScalar tau = dt / motor.hTimescale; if (tau > 1.0f) { tau = 1.0f; } - velocity += tau * (motor.velocity - velocity); + velocity += (motor.velocity - velocity) * tau; // rotate back into world-frame velocity = velocity.rotate(axis, angle); - _targetVelocity += (tau * motor.velocity).rotate(axis, angle); // store the velocity and weight velocities.push_back(velocity); @@ -582,32 +445,12 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel } else { // compute local UP btVector3 up = _currentUp.rotate(axis, -angle); - btVector3 motorVelocity = motor.velocity; - - // save these non-adjusted components for later - btVector3 vTargetVelocity = motorVelocity.dot(up) * up; - btVector3 hTargetVelocity = motorVelocity - vTargetVelocity; - - if (_stepHeight > _minStepHeight) { - // there is a step --> compute velocity direction to go over step - btVector3 motorVelocityWF = motorVelocity.rotate(axis, angle); - if (motorVelocityWF.dot(_stepNormal) < 0.0f) { - // the motor pushes against step - motorVelocityWF = _stepNormal.cross(_stepPoint.cross(motorVelocityWF)); - btScalar doubleCrossLength2 = motorVelocityWF.length2(); - if (doubleCrossLength2 > FLT_EPSILON) { - // scale the motor in the correct direction and rotate back to motor-frame - motorVelocityWF *= (motorVelocity.length() / sqrtf(doubleCrossLength2)); - _stepUpVelocity += motorVelocityWF.rotate(axis, -angle); - } - } - } // split velocity into horizontal and vertical components btVector3 vVelocity = velocity.dot(up) * up; btVector3 hVelocity = velocity - vVelocity; - btVector3 vMotorVelocity = motorVelocity.dot(up) * up; - btVector3 hMotorVelocity = motorVelocity - vMotorVelocity; + btVector3 vTargetVelocity = motor.velocity.dot(up) * up; + btVector3 hTargetVelocity = motor.velocity - vTargetVelocity; // modify each component separately btScalar maxTau = 0.0f; @@ -617,7 +460,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel tau = 1.0f; } maxTau = tau; - hVelocity += (hMotorVelocity - hVelocity) * tau; + hVelocity += (hTargetVelocity - hVelocity) * tau; } if (motor.vTimescale < MAX_CHARACTER_MOTOR_TIMESCALE) { btScalar tau = dt / motor.vTimescale; @@ -627,12 +470,11 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel if (tau > maxTau) { maxTau = tau; } - vVelocity += (vMotorVelocity - vVelocity) * tau; + vVelocity += (vTargetVelocity - vVelocity) * tau; } // add components back together and rotate into world-frame velocity = (hVelocity + vVelocity).rotate(axis, angle); - _targetVelocity += maxTau * (hTargetVelocity + vTargetVelocity).rotate(axis, angle); // store velocity and weights velocities.push_back(velocity); @@ -650,8 +492,6 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) { velocities.reserve(_motors.size()); std::vector weights; weights.reserve(_motors.size()); - _targetVelocity = btVector3(0.0f, 0.0f, 0.0f); - _stepUpVelocity = btVector3(0.0f, 0.0f, 0.0f); for (int i = 0; i < (int)_motors.size(); ++i) { applyMotor(i, dt, velocity, velocities, weights); } @@ -667,18 +507,14 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) { for (size_t i = 0; i < velocities.size(); ++i) { velocity += (weights[i] / totalWeight) * velocities[i]; } - _targetVelocity /= totalWeight; } if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) { velocity = btVector3(0.0f, 0.0f, 0.0f); } // 'thrust' is applied at the very end - _targetVelocity += dt * _linearAcceleration; velocity += dt * _linearAcceleration; - // Note the differences between these two variables: - // _targetVelocity = ideal final velocity according to input - // velocity = real final velocity after motors are applied to current velocity + _targetVelocity = velocity; } void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { @@ -687,54 +523,57 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { velocity = bulletToGLM(btVelocity); } -void CharacterController::updateState() { - if (!_dynamicsWorld) { - return; - } - const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; - const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; - const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; - const btScalar MIN_HOVER_HEIGHT = 2.5f; - const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; +void CharacterController::preSimulation() { + if (_enabled && _dynamicsWorld && _rigidBody) { + quint64 now = usecTimestampNow(); - // scan for distant floor - // rayStart is at center of bottom sphere - btVector3 rayStart = _position; + // slam body to where it is supposed to be + _rigidBody->setWorldTransform(_characterBodyTransform); + btVector3 velocity = _rigidBody->getLinearVelocity(); + _preSimulationVelocity = velocity; - // rayEnd is straight down MAX_FALL_HEIGHT - btScalar rayLength = _radius + MAX_FALL_HEIGHT; - btVector3 rayEnd = rayStart - rayLength * _currentUp; + // scan for distant floor + // rayStart is at center of bottom sphere + btVector3 rayStart = _characterBodyTransform.getOrigin(); - ClosestNotMe rayCallback(_rigidBody); - rayCallback.m_closestHitFraction = 1.0f; - _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); - bool rayHasHit = rayCallback.hasHit(); - quint64 now = usecTimestampNow(); - if (rayHasHit) { - _rayHitStartTime = now; - _floorDistance = rayLength * rayCallback.m_closestHitFraction - (_radius + _halfHeight); - } else { + // rayEnd is straight down MAX_FALL_HEIGHT + btScalar rayLength = _radius + MAX_FALL_HEIGHT; + btVector3 rayEnd = rayStart - rayLength * _currentUp; + + const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; + const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; + const btScalar MIN_HOVER_HEIGHT = 2.5f; + const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; + const btScalar MAX_WALKING_SPEED = 2.5f; const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND; - if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { + + ClosestNotMe rayCallback(_rigidBody); + rayCallback.m_closestHitFraction = 1.0f; + _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); + bool rayHasHit = rayCallback.hasHit(); + if (rayHasHit) { + _rayHitStartTime = now; + _floorDistance = rayLength * rayCallback.m_closestHitFraction - (_radius + _halfHeight); + } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { rayHasHit = true; } else { _floorDistance = FLT_MAX; } - } - // record a time stamp when the jump button was first pressed. - bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; - if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { - if (_pendingFlags & PENDING_FLAG_JUMP) { - _jumpButtonDownStartTime = now; - _jumpButtonDownCount++; + // record a time stamp when the jump button was first pressed. + if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { + if (_pendingFlags & PENDING_FLAG_JUMP) { + _jumpButtonDownStartTime = now; + _jumpButtonDownCount++; + } } - } - btVector3 velocity = _preSimulationVelocity; + bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; + + btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; + bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); - // OUTOFBODY_HACK -- disable normal state transitions while collisionless - if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { switch (_state) { case State::Ground: if (!rayHasHit && !_hasSupport) { @@ -774,50 +613,32 @@ void CharacterController::updateState() { break; } case State::Hover: - btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; - const btScalar MAX_WALKING_SPEED = 2.5f; - bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); - - if ((_floorDistance < MIN_HOVER_HEIGHT) && - !(jumpButtonHeld || flyingFast || (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD)) { + if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { SET_STATE(State::Ground, "touching ground"); } break; } - if (_moveKinematically && _ghost.isHovering()) { - SET_STATE(State::Hover, "kinematic motion"); // HACK - } - } else { - // OUTOFBODY_HACK -- in collisionless state switch only between Ground and Hover states - if (rayHasHit) { - SET_STATE(State::Ground, "collisionless above ground"); - } else { - SET_STATE(State::Hover, "collisionless in air"); - } - } -} - -void CharacterController::preSimulation() { - if (_rigidBody) { - // slam body transform and remember velocity - _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position))); - _preSimulationVelocity = _rigidBody->getLinearVelocity(); - - updateState(); } _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; + + _followTime = 0.0f; + _followLinearDisplacement = btVector3(0, 0, 0); + _followAngularDisplacement = btQuaternion::getIdentity(); } void CharacterController::postSimulation() { - if (_rigidBody) { - _velocityChange = _rigidBody->getLinearVelocity() - _preSimulationVelocity; + // postSimulation() exists for symmetry and just in case we need to do something here later + if (_enabled && _dynamicsWorld && _rigidBody) { + btVector3 velocity = _rigidBody->getLinearVelocity(); + _velocityChange = velocity - _preSimulationVelocity; } } + bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { if (!_rigidBody) { return false; @@ -834,58 +655,7 @@ void CharacterController::setFlyingAllowed(bool value) { _flyingAllowed = value; if (!_flyingAllowed && _state == State::Hover) { - // OUTOFBODY_HACK -- disable normal state transitions while collisionless - if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { - SET_STATE(State::InAir, "flying not allowed"); - } + SET_STATE(State::InAir, "flying not allowed"); } } } - -float CharacterController::measureMaxHipsOffsetRadius(const glm::vec3& currentHipsOffset, float maxSweepDistance) { - btVector3 hipsOffset = glmToBullet(currentHipsOffset); // rig-frame - btScalar hipsOffsetLength = hipsOffset.length(); - if (hipsOffsetLength > FLT_EPSILON) { - const btTransform& transform = _rigidBody->getWorldTransform(); - - // rotate into world-frame - btTransform rotation = transform; - rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); - btVector3 startPos = transform.getOrigin() - rotation * glmToBullet(_shapeLocalOffset); - btTransform startTransform = transform; - startTransform.setOrigin(startPos); - btVector3 endPos = startPos + rotation * ((maxSweepDistance / hipsOffsetLength) * hipsOffset); - - // sweep test a sphere - btSphereShape sphere(_radius); - CharacterSweepResult result(&_ghost); - btTransform endTransform = startTransform; - endTransform.setOrigin(endPos); - _ghost.sweepTest(&sphere, startTransform, endTransform, result); - - // measure sweep success - if (result.hasHit()) { - maxSweepDistance *= result.m_closestHitFraction; - } - } - return maxSweepDistance; -} - -void CharacterController::setMoveKinematically(bool kinematic) { - if (kinematic != _moveKinematically) { - _moveKinematically = kinematic; - _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - _ghost.setMotorOnly(!_moveKinematically); - } -} - -bool CharacterController::queryPenetration(const btTransform& transform) { - btVector3 minBox; - btVector3 maxBox; - _ghost.setWorldTransform(transform); - _ghost.measurePenetration(minBox, maxBox); - btVector3 penetration = minBox; - penetration.setMax(maxBox.absolute()); - const btScalar MIN_PENETRATION_SQUARED = 0.0016f; // 0.04^2 - return penetration.length2() < MIN_PENETRATION_SQUARED; -} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 75cbc0730b..586ea175e6 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -1,6 +1,6 @@ // // CharacterControllerInterface.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.10.21 // Copyright 2015 High Fidelity, Inc. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CharacterController_h -#define hifi_CharacterController_h +#ifndef hifi_CharacterControllerInterface_h +#define hifi_CharacterControllerInterface_h #include #include @@ -19,18 +19,12 @@ #include #include -#include -#include - #include "BulletUtil.h" -#include "CharacterGhostObject.h" const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; -const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4; -const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); const float DEFAULT_CHARACTER_GRAVITY = -5.0f; @@ -50,7 +44,7 @@ public: bool needsRemoval() const; bool needsAddition() const; - virtual void setDynamicsWorld(btDynamicsWorld* world); + void setDynamicsWorld(btDynamicsWorld* world); btCollisionObject* getCollisionObject() { return _rigidBody; } virtual void updateShapeIfNecessary() = 0; @@ -62,7 +56,10 @@ public: virtual void warp(const btVector3& origin) override { } virtual void debugDraw(btIDebugDraw* debugDrawer) override { } virtual void setUpInterpolate(bool value) override { } - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override; + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override { + preStep(collisionWorld); + playerStep(collisionWorld, deltaTime); + } virtual void preStep(btCollisionWorld *collisionWorld) override; virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; virtual bool canJump() const override { assert(false); return false; } // never call this @@ -72,7 +69,6 @@ public: void clearMotors(); void addMotor(const glm::vec3& velocity, const glm::quat& rotation, float horizTimescale, float vertTimescale = -1.0f); void applyMotor(int index, btScalar dt, btVector3& worldVelocity, std::vector& velocities, std::vector& weights); - void setStepUpEnabled(bool enabled) { _stepUpEnabled = enabled; } void computeNewVelocity(btScalar dt, btVector3& velocity); void computeNewVelocity(btScalar dt, glm::vec3& velocity); @@ -86,11 +82,13 @@ public: void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; void setParentVelocity(const glm::vec3& parentVelocity); - void setFollowParameters(const glm::mat4& desiredWorldBodyMatrix); - void disableFollow() { _following = false; } + void setFollowParameters(const glm::mat4& desiredWorldMatrix, float timeRemaining); + float getFollowTime() const { return _followTime; } + glm::vec3 getFollowLinearDisplacement() const; + glm::quat getFollowAngularDisplacement() const; + glm::vec3 getFollowVelocity() const; glm::vec3 getLinearVelocity() const; - glm::vec3 getPreActionLinearVelocity() const; glm::vec3 getVelocityChange() const; float getCapsuleRadius() const { return _radius; } @@ -105,24 +103,17 @@ public: }; State getState() const { return _state; } - void updateState(); - void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale); + void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); - bool isEnabledAndReady() const { return _dynamicsWorld; } - - void setCollisionGroup(int16_t group); - int16_t getCollisionGroup() const { return _collisionGroup; } - void handleChangedCollisionGroup(); + bool isEnabled() const { return _enabled; } // thread-safe + void setEnabled(bool enabled); + bool isEnabledAndReady() const { return _enabled && _dynamicsWorld; } bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); void setFlyingAllowed(bool value); - float measureMaxHipsOffsetRadius(const glm::vec3& currentHipsOffset, float maxSweepDistance); - void setMoveKinematically(bool kinematic); // KINEMATIC_CONTROLLER_HACK - - bool queryPenetration(const btTransform& transform); protected: #ifdef DEBUG_STATE_CHANGE @@ -132,7 +123,7 @@ protected: #endif void updateUpAxis(const glm::quat& rotation); - bool checkForSupport(btCollisionWorld* collisionWorld, btScalar dt); + bool checkForSupport(btCollisionWorld* collisionWorld) const; protected: struct CharacterMotor { @@ -145,18 +136,14 @@ protected: }; std::vector _motors; - CharacterGhostObject _ghost; // KINEMATIC_CONTROLLER_HACK btVector3 _currentUp; btVector3 _targetVelocity; btVector3 _parentVelocity; btVector3 _preSimulationVelocity; - glm::vec3 _preActionVelocity; btVector3 _velocityChange; btTransform _followDesiredBodyTransform; - btVector3 _followVelocity { 0.0f, 0.0f, 0.0f }; - btVector3 _previousFollowPosition { 0.0f, 0.0f, 0.0f }; - btVector3 _position; - btQuaternion _rotation; + btScalar _followTimeRemaining; + btTransform _characterBodyTransform; glm::vec3 _shapeLocalOffset; @@ -168,29 +155,21 @@ protected: quint32 _jumpButtonDownCount; quint32 _takeoffJumpButtonID; - // data for walking up steps - btVector3 _stepPoint { 0.0f, 0.0f, 0.0f }; - btVector3 _stepNormal { 0.0f, 0.0f, 0.0f }; - btVector3 _stepUpVelocity { 0.0f, 0.0f, 0.0f }; - btScalar _stepHeight { 0.0f }; - btScalar _minStepHeight { 0.0f }; - btScalar _maxStepHeight { 0.0f }; - btScalar _minFloorNormalDotUp { DEFAULT_MIN_FLOOR_NORMAL_DOT_UP }; - - btScalar _halfHeight { 0.0f }; - btScalar _radius { 0.0f }; + btScalar _halfHeight; + btScalar _radius; btScalar _floorDistance; - bool _stepUpEnabled { true }; bool _hasSupport; - btScalar _gravity { DEFAULT_CHARACTER_GRAVITY }; - btScalar _followTimeAccumulator { 0.0f }; + btScalar _gravity; btScalar _jumpSpeed; + btScalar _followTime; + btVector3 _followLinearDisplacement; + btQuaternion _followAngularDisplacement; btVector3 _linearAcceleration; - bool _following { false }; + std::atomic_bool _enabled; State _state; bool _isPushingUp; @@ -200,8 +179,6 @@ protected: uint32_t _previousFlags { 0 }; bool _flyingAllowed { true }; - bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK - int16_t _collisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; }; -#endif // hifi_CharacterController_h +#endif // hifi_CharacterControllerInterface_h diff --git a/libraries/physics/src/CharacterGhostObject.cpp b/libraries/physics/src/CharacterGhostObject.cpp deleted file mode 100755 index 563605cd16..0000000000 --- a/libraries/physics/src/CharacterGhostObject.cpp +++ /dev/null @@ -1,415 +0,0 @@ -// -// CharacterGhostObject.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.08.26 -// 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 -// - -#include "CharacterGhostObject.h" - -#include -#include - -#include - -#include "CharacterRayResult.h" -#include "CharacterGhostShape.h" - - -CharacterGhostObject::~CharacterGhostObject() { - removeFromWorld(); - if (_ghostShape) { - delete _ghostShape; - _ghostShape = nullptr; - setCollisionShape(nullptr); - } -} - -void CharacterGhostObject::setCollisionGroupAndMask(int16_t group, int16_t mask) { - _collisionFilterGroup = group; - _collisionFilterMask = mask; - // TODO: if this probe is in the world reset ghostObject overlap cache -} - -void CharacterGhostObject::getCollisionGroupAndMask(int16_t& group, int16_t& mask) const { - group = _collisionFilterGroup; - mask = _collisionFilterMask; -} - -void CharacterGhostObject::setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight) { - _radius = radius; - _halfHeight = halfHeight; -} - -void CharacterGhostObject::setUpDirection(const btVector3& up) { - btScalar length = up.length(); - if (length > FLT_EPSILON) { - _upDirection /= length; - } else { - _upDirection = btVector3(0.0f, 1.0f, 0.0f); - } -} - -void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) { - _motorVelocity = velocity; - if (_motorOnly) { - _linearVelocity = _motorVelocity; - } -} - -// override of btCollisionObject::setCollisionShape() -void CharacterGhostObject::setCharacterShape(btConvexHullShape* shape) { - assert(shape); - // we create our own shape with an expanded Aabb for more reliable sweep tests - if (_ghostShape) { - delete _ghostShape; - } - - _ghostShape = new CharacterGhostShape(static_cast(shape)); - setCollisionShape(_ghostShape); -} - -void CharacterGhostObject::setCollisionWorld(btCollisionWorld* world) { - if (world != _world) { - removeFromWorld(); - _world = world; - addToWorld(); - } -} - -void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) { - bool oldOnFloor = _onFloor; - _onFloor = false; - _steppingUp = false; - assert(_world && _inWorld); - updateVelocity(dt, gravity); - - // resolve any penetrations before sweeping - int32_t MAX_LOOPS = 4; - int32_t numExtractions = 0; - btVector3 totalPosition(0.0f, 0.0f, 0.0f); - while (numExtractions < MAX_LOOPS) { - if (resolvePenetration(numExtractions)) { - numExtractions = 0; - break; - } - totalPosition += getWorldTransform().getOrigin(); - ++numExtractions; - } - if (numExtractions > 1) { - // penetration resolution was probably oscillating between opposing objects - // so we use the average of the solutions - totalPosition /= btScalar(numExtractions); - btTransform transform = getWorldTransform(); - transform.setOrigin(totalPosition); - setWorldTransform(transform); - - // TODO: figure out how to untrap character - } - btTransform startTransform = getWorldTransform(); - btVector3 startPosition = startTransform.getOrigin(); - if (_onFloor) { - // resolvePenetration() pushed the avatar out of a floor so - // we must updateTraction() before using _linearVelocity - updateTraction(startPosition); - } - - btScalar speed = _linearVelocity.length(); - btVector3 forwardSweep = dt * _linearVelocity; - btScalar stepDistance = dt * speed; - btScalar MIN_SWEEP_DISTANCE = 0.0001f; - if (stepDistance < MIN_SWEEP_DISTANCE) { - // not moving, no need to sweep - updateTraction(startPosition); - return; - } - - // augment forwardSweep to help slow moving sweeps get over steppable ledges - const btScalar MIN_OVERSHOOT = 0.04f; // default margin - if (overshoot < MIN_OVERSHOOT) { - overshoot = MIN_OVERSHOOT; - } - btScalar longSweepDistance = stepDistance + overshoot; - forwardSweep *= longSweepDistance / stepDistance; - - // step forward - CharacterSweepResult result(this); - btTransform nextTransform = startTransform; - nextTransform.setOrigin(startPosition + forwardSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); // forward - - if (!result.hasHit()) { - nextTransform.setOrigin(startPosition + (stepDistance / longSweepDistance) * forwardSweep); - setWorldTransform(nextTransform); - updateTraction(nextTransform.getOrigin()); - return; - } - bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < MIN_OVERSHOOT; - if (verticalOnly) { - // no need to step - nextTransform.setOrigin(startPosition + (result.m_closestHitFraction * stepDistance / longSweepDistance) * forwardSweep); - setWorldTransform(nextTransform); - - if (result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) { - _floorNormal = result.m_hitNormalWorld; - _floorContact = result.m_hitPointWorld; - _steppingUp = false; - _onFloor = true; - _hovering = false; - } - updateTraction(nextTransform.getOrigin()); - return; - } - - // check if this hit is obviously unsteppable - btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection)); - btScalar hitHeight = hitFromBase.dot(_upDirection); - if (hitHeight > _maxStepHeight) { - // shape can't step over the obstacle so move forward as much as possible before we bail - btVector3 forwardTranslation = result.m_closestHitFraction * forwardSweep; - btScalar forwardDistance = forwardTranslation.length(); - if (forwardDistance > stepDistance) { - forwardTranslation *= stepDistance / forwardDistance; - } - nextTransform.setOrigin(startPosition + forwardTranslation); - setWorldTransform(nextTransform); - _onFloor = _onFloor || oldOnFloor; - return; - } - // if we get here then we hit something that might be steppable - - // remember the forward sweep hit fraction for later - btScalar forwardSweepHitFraction = result.m_closestHitFraction; - - // figure out how high we can step up - btScalar availableStepHeight = measureAvailableStepHeight(); - - // raise by availableStepHeight before sweeping forward - result.resetHitHistory(); - startTransform.setOrigin(startPosition + availableStepHeight * _upDirection); - nextTransform.setOrigin(startTransform.getOrigin() + forwardSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); - if (result.hasHit()) { - startTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * forwardSweep); - } else { - startTransform = nextTransform; - } - - // sweep down in search of future landing spot - result.resetHitHistory(); - btVector3 downSweep = (- availableStepHeight) * _upDirection; - nextTransform.setOrigin(startTransform.getOrigin() + downSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); - if (result.hasHit() && result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) { - // can stand on future landing spot, so we interpolate toward it - _floorNormal = result.m_hitNormalWorld; - _floorContact = result.m_hitPointWorld; - _steppingUp = true; - _onFloor = true; - _hovering = false; - nextTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * downSweep); - btVector3 totalStep = nextTransform.getOrigin() - startPosition; - nextTransform.setOrigin(startPosition + (stepDistance / totalStep.length()) * totalStep); - updateTraction(nextTransform.getOrigin()); - } else { - // either there is no future landing spot, or there is but we can't stand on it - // in any case: we go forward as much as possible - nextTransform.setOrigin(startPosition + forwardSweepHitFraction * (stepDistance / longSweepDistance) * forwardSweep); - _onFloor = _onFloor || oldOnFloor; - updateTraction(nextTransform.getOrigin()); - } - setWorldTransform(nextTransform); -} - -bool CharacterGhostObject::sweepTest( - const btConvexShape* shape, - const btTransform& start, - const btTransform& end, - CharacterSweepResult& result) const { - if (_world && _inWorld) { - assert(shape); - btScalar allowedPenetration = _world->getDispatchInfo().m_allowedCcdPenetration; - convexSweepTest(shape, start, end, result, allowedPenetration); - return result.hasHit(); - } - return false; -} - -bool CharacterGhostObject::rayTest(const btVector3& start, - const btVector3& end, - CharacterRayResult& result) const { - if (_world && _inWorld) { - _world->rayTest(start, end, result); - } - return result.hasHit(); -} - -void CharacterGhostObject::measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut) { - // minBoxOut and maxBoxOut will be updated with penetration envelope. - // If one of the corner points is <0,0,0> then the penetration is resolvable in a single step, - // but if the space spanned by the two corners extends in both directions along at least one - // component then we the object is sandwiched between two opposing objects. - - // We assume this object has just been moved to its current location, or else objects have been - // moved around it since the last step so we must update the overlapping pairs. - refreshOverlappingPairCache(); - - // compute collision details - btHashedOverlappingPairCache* pairCache = getOverlappingPairCache(); - _world->getDispatcher()->dispatchAllCollisionPairs(pairCache, _world->getDispatchInfo(), _world->getDispatcher()); - - // loop over contact manifolds to compute the penetration box - minBoxOut = btVector3(0.0f, 0.0f, 0.0f); - maxBoxOut = btVector3(0.0f, 0.0f, 0.0f); - btManifoldArray manifoldArray; - - int numPairs = pairCache->getNumOverlappingPairs(); - for (int i = 0; i < numPairs; i++) { - manifoldArray.resize(0); - btBroadphasePair* collisionPair = &(pairCache->getOverlappingPairArray()[i]); - - btCollisionObject* obj0 = static_cast(collisionPair->m_pProxy0->m_clientObject); - btCollisionObject* obj1 = static_cast(collisionPair->m_pProxy1->m_clientObject); - - if ((obj0 && !obj0->hasContactResponse()) && (obj1 && !obj1->hasContactResponse())) { - // we know this probe has no contact response - // but neither does the other object so skip this manifold - continue; - } - - if (!collisionPair->m_algorithm) { - // null m_algorithm means the two shape types don't know how to collide! - // shouldn't fall in here but just in case - continue; - } - - btScalar mostFloorPenetration = 0.0f; - collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); - for (int j = 0; j < manifoldArray.size(); j++) { - btPersistentManifold* manifold = manifoldArray[j]; - btScalar directionSign = (manifold->getBody0() == this) ? btScalar(1.0) : btScalar(-1.0); - for (int p = 0; p < manifold->getNumContacts(); p++) { - const btManifoldPoint& pt = manifold->getContactPoint(p); - if (pt.getDistance() > 0.0f) { - continue; - } - - // normal always points from object to character - btVector3 normal = directionSign * pt.m_normalWorldOnB; - - btScalar penetrationDepth = pt.getDistance(); - if (penetrationDepth < mostFloorPenetration) { // remember penetrationDepth is negative - btScalar normalDotUp = normal.dot(_upDirection); - if (normalDotUp > _maxWallNormalUpComponent) { - mostFloorPenetration = penetrationDepth; - _floorNormal = normal; - if (directionSign > 0.0f) { - _floorContact = pt.m_positionWorldOnA; - } else { - _floorContact = pt.m_positionWorldOnB; - } - _onFloor = true; - } - } - - btVector3 penetration = (-penetrationDepth) * normal; - minBoxOut.setMin(penetration); - maxBoxOut.setMax(penetration); - } - } - } -} - -void CharacterGhostObject::refreshOverlappingPairCache() { - assert(_world && _inWorld); - btVector3 minAabb, maxAabb; - getCollisionShape()->getAabb(getWorldTransform(), minAabb, maxAabb); - // this updates both pairCaches: world broadphase and ghostobject - _world->getBroadphase()->setAabb(getBroadphaseHandle(), minAabb, maxAabb, _world->getDispatcher()); -} - -void CharacterGhostObject::removeFromWorld() { - if (_world && _inWorld) { - _world->removeCollisionObject(this); - _inWorld = false; - } -} - -void CharacterGhostObject::addToWorld() { - if (_world && !_inWorld) { - assert(getCollisionShape()); - setCollisionFlags(getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE); - _world->addCollisionObject(this, _collisionFilterGroup, _collisionFilterMask); - _inWorld = true; - } -} - -bool CharacterGhostObject::resolvePenetration(int numTries) { - btVector3 minBox, maxBox; - measurePenetration(minBox, maxBox); - btVector3 restore = maxBox + minBox; - if (restore.length2() > 0.0f) { - btTransform transform = getWorldTransform(); - transform.setOrigin(transform.getOrigin() + restore); - setWorldTransform(transform); - return false; - } - return true; -} - -void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) { - if (!_motorOnly) { - if (_hovering) { - _linearVelocity *= 0.999f; // HACK damping - } else { - _linearVelocity += (dt * gravity) * _upDirection; - } - } -} - -void CharacterGhostObject::updateHoverState(const btVector3& position) { - if (_onFloor) { - _hovering = false; - } else { - // cast a ray down looking for floor support - CharacterRayResult rayResult(this); - btScalar distanceToFeet = _radius + _halfHeight; - btScalar slop = 2.0f * getCollisionShape()->getMargin(); // slop to help ray start OUTSIDE the floor object - btVector3 startPos = position - ((distanceToFeet - slop) * _upDirection); - btVector3 endPos = startPos - (2.0f * distanceToFeet) * _upDirection; - rayTest(startPos, endPos, rayResult); - // we're hovering if the ray didn't hit anything or hit unstandable slope - _hovering = !rayResult.hasHit() || rayResult.m_hitNormalWorld.dot(_upDirection) < _maxWallNormalUpComponent; - } -} - -void CharacterGhostObject::updateTraction(const btVector3& position) { - updateHoverState(position); - if (_hovering || _motorOnly) { - _linearVelocity = _motorVelocity; - } else if (_onFloor) { - // compute a velocity that swings the shape around the _floorContact - btVector3 leverArm = _floorContact - position; - btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm)); - btScalar pathLength = pathDirection.length(); - if (pathLength > FLT_EPSILON) { - _linearVelocity = (_motorVelocity.length() / pathLength) * pathDirection; - } else { - _linearVelocity = btVector3(0.0f, 0.0f, 0.0f); - } - } -} - -btScalar CharacterGhostObject::measureAvailableStepHeight() const { - CharacterSweepResult result(this); - btTransform transform = getWorldTransform(); - btTransform nextTransform = transform; - nextTransform.setOrigin(transform.getOrigin() + _maxStepHeight * _upDirection); - sweepTest(_characterShape, transform, nextTransform, result); - return result.m_closestHitFraction * _maxStepHeight; -} - diff --git a/libraries/physics/src/CharacterGhostObject.h b/libraries/physics/src/CharacterGhostObject.h deleted file mode 100755 index feb132a53e..0000000000 --- a/libraries/physics/src/CharacterGhostObject.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// CharacterGhostObject.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.08.26 -// 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_CharacterGhostObject_h -#define hifi_CharacterGhostObject_h - -#include - -#include -#include -#include - -#include "CharacterSweepResult.h" -#include "CharacterRayResult.h" - -class CharacterGhostShape; - -class CharacterGhostObject : public btPairCachingGhostObject { -public: - CharacterGhostObject() { } - ~CharacterGhostObject(); - - void setCollisionGroupAndMask(int16_t group, int16_t mask); - void getCollisionGroupAndMask(int16_t& group, int16_t& mask) const; - - void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight); - void setUpDirection(const btVector3& up); - void setMotorVelocity(const btVector3& velocity); - void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); } - void setMaxStepHeight(btScalar height) { _maxStepHeight = height; } - - void setLinearVelocity(const btVector3& velocity) { _linearVelocity = velocity; } - const btVector3& getLinearVelocity() const { return _linearVelocity; } - - void setCharacterShape(btConvexHullShape* shape); - - void setCollisionWorld(btCollisionWorld* world); - - void move(btScalar dt, btScalar overshoot, btScalar gravity); - - bool sweepTest(const btConvexShape* shape, - const btTransform& start, - const btTransform& end, - CharacterSweepResult& result) const; - - bool rayTest(const btVector3& start, - const btVector3& end, - CharacterRayResult& result) const; - - bool isHovering() const { return _hovering; } - void setHovering(bool hovering) { _hovering = hovering; } - void setMotorOnly(bool motorOnly) { _motorOnly = motorOnly; } - - bool hasSupport() const { return _onFloor; } - bool isSteppingUp() const { return _steppingUp; } - const btVector3& getFloorNormal() const { return _floorNormal; } - - void measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut); - void refreshOverlappingPairCache(); - -protected: - void removeFromWorld(); - void addToWorld(); - - bool resolvePenetration(int numTries); - void updateVelocity(btScalar dt, btScalar gravity); - void updateTraction(const btVector3& position); - btScalar measureAvailableStepHeight() const; - void updateHoverState(const btVector3& position); - -protected: - btVector3 _upDirection { 0.0f, 1.0f, 0.0f }; // input, up in world-frame - btVector3 _motorVelocity { 0.0f, 0.0f, 0.0f }; // input, velocity character is trying to achieve - btVector3 _linearVelocity { 0.0f, 0.0f, 0.0f }; // internal, actual character velocity - btVector3 _floorNormal { 0.0f, 0.0f, 0.0f }; // internal, probable floor normal - btVector3 _floorContact { 0.0f, 0.0f, 0.0f }; // internal, last floor contact point - btCollisionWorld* _world { nullptr }; // input, pointer to world - //btScalar _distanceToFeet { 0.0f }; // input, distance from object center to lowest point on shape - btScalar _halfHeight { 0.0f }; - btScalar _radius { 0.0f }; - btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal - btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb - btConvexHullShape* _characterShape { nullptr }; // input, shape of character - CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache - int16_t _collisionFilterGroup { 0 }; - int16_t _collisionFilterMask { 0 }; - bool _inWorld { false }; // internal, was added to world - bool _hovering { false }; // internal, - bool _onFloor { false }; // output, is actually standing on floor - bool _steppingUp { false }; // output, future sweep hit a steppable ledge - bool _hasFloor { false }; // output, has floor underneath to fall on - bool _motorOnly { false }; // input, _linearVelocity slaves to _motorVelocity -}; - -#endif // hifi_CharacterGhostObject_h diff --git a/libraries/physics/src/CharacterGhostShape.cpp b/libraries/physics/src/CharacterGhostShape.cpp deleted file mode 100644 index 09f4f0b80f..0000000000 --- a/libraries/physics/src/CharacterGhostShape.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// CharacterGhostShape.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.14 -// 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 -// - -#include "CharacterGhostShape.h" - -#include - - -CharacterGhostShape::CharacterGhostShape(const btConvexHullShape* shape) : - btConvexHullShape(reinterpret_cast(shape->getUnscaledPoints()), shape->getNumPoints(), sizeof(btVector3)) { - assert(shape); - assert(shape->getUnscaledPoints()); - assert(shape->getNumPoints() > 0); - setMargin(shape->getMargin()); -} - -void CharacterGhostShape::getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const { - btConvexHullShape::getAabb(t, aabbMin, aabbMax); - // double the size of the Aabb by expanding both corners by half the extent - btVector3 expansion = 0.5f * (aabbMax - aabbMin); - aabbMin -= expansion; - aabbMax += expansion; -} diff --git a/libraries/physics/src/CharacterGhostShape.h b/libraries/physics/src/CharacterGhostShape.h deleted file mode 100644 index dc75c148d5..0000000000 --- a/libraries/physics/src/CharacterGhostShape.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// CharacterGhostShape.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.14 -// 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_CharacterGhostShape_h -#define hifi_CharacterGhostShape_h - -#include - -class CharacterGhostShape : public btConvexHullShape { - // Same as btConvexHullShape but reports an expanded Aabb for larger ghost overlap cache -public: - CharacterGhostShape(const btConvexHullShape* shape); - - virtual void getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const override; -}; - -#endif // hifi_CharacterGhostShape_h diff --git a/libraries/physics/src/CharacterRayResult.cpp b/libraries/physics/src/CharacterRayResult.cpp deleted file mode 100755 index 7a81e9cca6..0000000000 --- a/libraries/physics/src/CharacterRayResult.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// CharaterRayResult.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.05 -// 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 -// - -#include "CharacterRayResult.h" - -#include - -#include "CharacterGhostObject.h" - -CharacterRayResult::CharacterRayResult (const CharacterGhostObject* character) : - btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)), - _character(character) -{ - assert(_character); - _character->getCollisionGroupAndMask(m_collisionFilterGroup, m_collisionFilterMask); -} - -btScalar CharacterRayResult::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { - if (rayResult.m_collisionObject == _character) { - return 1.0f; - } - return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); -} diff --git a/libraries/physics/src/CharacterRayResult.h b/libraries/physics/src/CharacterRayResult.h deleted file mode 100644 index e8b0bb7f99..0000000000 --- a/libraries/physics/src/CharacterRayResult.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// CharaterRayResult.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.05 -// 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_CharacterRayResult_h -#define hifi_CharacterRayResult_h - -#include -#include - -class CharacterGhostObject; - -class CharacterRayResult : public btCollisionWorld::ClosestRayResultCallback { -public: - CharacterRayResult (const CharacterGhostObject* character); - - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override; - -protected: - const CharacterGhostObject* _character; - - // Note: Public data members inherited from ClosestRayResultCallback - // - // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction - // btVector3 m_rayToWorld; - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // - // Note: Public data members inherited from RayResultCallback - // - // btScalar m_closestHitFraction; - // const btCollisionObject* m_collisionObject; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; -}; - -#endif // hifi_CharacterRayResult_h diff --git a/libraries/physics/src/CharacterSweepResult.cpp b/libraries/physics/src/CharacterSweepResult.cpp deleted file mode 100755 index a5c4092b1d..0000000000 --- a/libraries/physics/src/CharacterSweepResult.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// -// CharaterSweepResult.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.01 -// 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 -// - -#include "CharacterSweepResult.h" - -#include - -#include "CharacterGhostObject.h" - -CharacterSweepResult::CharacterSweepResult(const CharacterGhostObject* character) - : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)), - _character(character) -{ - // set collision group and mask to match _character - assert(_character); - _character->getCollisionGroupAndMask(m_collisionFilterGroup, m_collisionFilterMask); -} - -btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) { - // skip objects that we shouldn't collide with - if (!convexResult.m_hitCollisionObject->hasContactResponse()) { - return btScalar(1.0); - } - if (convexResult.m_hitCollisionObject == _character) { - return btScalar(1.0); - } - - return ClosestConvexResultCallback::addSingleResult(convexResult, useWorldFrame); -} - -void CharacterSweepResult::resetHitHistory() { - m_hitCollisionObject = nullptr; - m_closestHitFraction = btScalar(1.0f); -} diff --git a/libraries/physics/src/CharacterSweepResult.h b/libraries/physics/src/CharacterSweepResult.h deleted file mode 100644 index 1e2898a3cf..0000000000 --- a/libraries/physics/src/CharacterSweepResult.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// CharaterSweepResult.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.01 -// 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_CharacterSweepResult_h -#define hifi_CharacterSweepResult_h - -#include -#include - - -class CharacterGhostObject; - -class CharacterSweepResult : public btCollisionWorld::ClosestConvexResultCallback { -public: - CharacterSweepResult(const CharacterGhostObject* character); - virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) override; - void resetHitHistory(); -protected: - const CharacterGhostObject* _character; - - // NOTE: Public data members inherited from ClosestConvexResultCallback: - // - // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback - // btVector3 m_convexToWorld; // unused except by btClosestNotMeConvexResultCallback - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // const btCollisionObject* m_hitCollisionObject; - // - // NOTE: Public data members inherited from ConvexResultCallback: - // - // btScalar m_closestHitFraction; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; - -}; - -#endif // hifi_CharacterSweepResult_h diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 40a8a4aff9..3a1c4d0ea4 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -1,6 +1,6 @@ // // CollisionRenderMeshCache.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 // Copyright 2016 High Fidelity, Inc. diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 6a6857a5ae..910b43996e 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -1,6 +1,6 @@ // // CollisionRenderMeshCache.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 // Copyright 2016 High Fidelity, Inc. diff --git a/libraries/physics/src/ContactInfo.cpp b/libraries/physics/src/ContactInfo.cpp index 9acf105d3a..c2ea6e8671 100644 --- a/libraries/physics/src/ContactInfo.cpp +++ b/libraries/physics/src/ContactInfo.cpp @@ -1,6 +1,6 @@ // // ContactEvent.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.01.20 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ContactInfo.h b/libraries/physics/src/ContactInfo.h index bbd2fdb460..11c908a414 100644 --- a/libraries/physics/src/ContactInfo.h +++ b/libraries/physics/src/ContactInfo.h @@ -1,6 +1,6 @@ // // ContactEvent.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.01.20 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 0d5948a793..ca64cabe5f 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -1,6 +1,6 @@ // // ObjectAction.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Seth Alves 2015-6-2 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 1327fd7c6f..43330269ac 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -1,6 +1,6 @@ // // ObjectAction.h -// libraries/physics/src +// libraries/physcis/src // // Created by Seth Alves 2015-6-2 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 503b39dc1c..38f079c1d4 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -1,6 +1,6 @@ // // ObjectMotionState.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.05 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 17d75c733b..a7894998a8 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -1,6 +1,6 @@ // // ObjectMotionState.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.05 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index dbef9b1f7d..903b160a5e 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -1,6 +1,6 @@ // // PhysicalEntitySimulation.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.04.27 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 79d16604c9..24b9ba95f0 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -1,6 +1,6 @@ // // PhysicalEntitySimulation.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.04.27 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index fa626d0c91..ba002d925c 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -1,6 +1,6 @@ // // PhysicsEngine.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index ae78802a64..bbafbb06b6 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -1,6 +1,6 @@ // // PhysicsEngine.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5268278be0..100dab0fd1 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -1,6 +1,6 @@ // // ShapeFactory.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..a1022104dd 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -1,6 +1,6 @@ // // ShapeFactory.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index fd3e35d28a..b61fb0037b 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -1,6 +1,6 @@ // // ShapeManager.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index ed81b5e8f8..261c06ddb9 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -1,6 +1,6 @@ // // ShapeManager.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/shared/src/BackgroundMode.h b/libraries/shared/src/BackgroundMode.h index 0e0d684e62..e6e585d9d8 100644 --- a/libraries/shared/src/BackgroundMode.h +++ b/libraries/shared/src/BackgroundMode.h @@ -1,6 +1,6 @@ // // BackgroundMode.h -// libraries/physics/src +// libraries/physcis/src // // Copyright 2015 High Fidelity, Inc. // diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 91623f1e83..92fe138021 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -578,42 +578,3 @@ float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirectio return glm::max(0.0f, theta - phi); } - -glm::vec3 nearestPointOnLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd) { - glm::vec3 n = segmentEnd - segmentStart; - float len = glm::length(n); - if (len < EPSILON) { - return segmentStart; - } else { - n /= len; - float projection = glm::dot(point - segmentStart, n); - projection = glm::clamp(projection, 0.0f, len); - return segmentStart + n * projection; - } -} - -float distanceFromLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd) { - return glm::length(point - nearestPointOnLineSegment(point, segmentStart, segmentEnd)); -} - -float distanceFromCapsule(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd, float capsuleRadius) { - return distanceFromLineSegment(point, segmentStart, segmentEnd) - capsuleRadius; -} - -bool pointIsInsideCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius) { - return distanceFromLineSegment(point, capsuleStart, capsuleEnd) < capsuleRadius; -} - -glm::vec3 projectPointOntoCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius) { - glm::vec3 nearestPoint = nearestPointOnLineSegment(point, capsuleStart, capsuleEnd); - glm::vec3 d = point - nearestPoint; - float dLen = glm::length(d); - if (dLen < EPSILON) { - return nearestPoint; // TODO: maybe we should pick a point actually on the surface... - } else { - return nearestPoint + d * (capsuleRadius / dLen); - } -} - - - diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 47f069931d..1c951ca09a 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -160,10 +160,5 @@ private: static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB); }; -glm::vec3 nearestPointOnLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd); -float distanceFromLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd); -float distanceFromCapsule(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd, float capsuleRadius); -bool pointIsInsideCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius); -glm::vec3 projectPointOntoCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius); #endif // hifi_GeometryUtil_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 583bceeaf2..b8ea3a4272 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -1,6 +1,6 @@ // // ShapeInfo.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 732b62ebbf..a6ff8d6d4a 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -1,6 +1,6 @@ // // ShapeInfo.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index ffead2eb6b..d3284352bf 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -13,7 +13,7 @@ var inTeleportMode = false; var SMOOTH_ARRIVAL_SPACING = 33; -var NUMBER_OF_STEPS_FOR_TELEPORT = 6; +var NUMBER_OF_STEPS = 6; var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); @@ -44,7 +44,6 @@ var COLORS_TELEPORT_TOO_CLOSE = { var TELEPORT_CANCEL_RANGE = 1; var USE_COOL_IN = true; var COOL_IN_DURATION = 500; -var MAX_HMD_AVATAR_SEPARATION = 10.0; function ThumbPad(hand) { this.hand = hand; @@ -89,7 +88,6 @@ function Teleporter() { this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; - this.teleportMode = "HMDAndAvatarTogether"; this.tooClose = false; this.inCoolIn = false; @@ -545,68 +543,43 @@ function Teleporter() { var offset = getAvatarFootOffset(); this.intersection.intersection.y += offset; this.exitTeleportMode(); - if (MyAvatar.hmdLeanRecenterEnabled) { - if (Vec3.distance(MyAvatar.position, _this.intersection.intersection) > MAX_HMD_AVATAR_SEPARATION) { - this.teleportMode = "HMDAndAvatarTogether"; - } else { - this.teleportMode = "HMDFirstAvatarWillFollow"; - } - } else { - this.teleportMode = "AvatarOnly"; - } - // Disable smooth arrival, possibly temporarily //this.smoothArrival(); - // Instead jump to the intersection directly. - var landingPoint = _this.intersection.intersection; - _this.teleportTo(landingPoint); - if (this.teleportMode === "HMDFirstAvatarWillFollow") { - MyAvatar.position = landingPoint; - } - - // cleanup UI + MyAvatar.position = _this.intersection.intersection; _this.hideTargetOverlay(); _this.hideCancelOverlay(); HMD.centerUI(); } }; - this.getWayPoints = function(startPoint, endPoint, numberOfSteps) { - var travel = Vec3.subtract(endPoint, startPoint); - var distance = Vec3.length(travel); - var wayPoints = []; - if (distance > 1.0) { - var base = Math.exp(Math.log(distance + 1.0) / numberOfSteps); - var i; - - // this fancy math generates regular points in logarithmic space - for (i = 0; i < numberOfSteps - 1; i++) { - var backFraction = (1.0 - Math.exp((numberOfSteps - 1 - i) * Math.log(base))) / distance; - wayPoints.push(Vec3.sum(endPoint, Vec3.multiply(backFraction, travel))); - } - } - wayPoints.push(endPoint); - return wayPoints; + this.findMidpoint = function(start, end) { + var xy = Vec3.sum(start, end); + var midpoint = Vec3.multiply(0.5, xy); + return midpoint }; - this.teleportTo = function(landingPoint) { - if (this.teleportMode === "AvatarOnly") { - MyAvatar.position = landingPoint; - } else if (this.teleportMode === "HMDAndAvatarTogether") { - var leanEnabled = MyAvatar.hmdLeanRecenterEnabled; - MyAvatar.setHMDLeanRecenterEnabled(false); - MyAvatar.position = landingPoint; - HMD.snapToAvatar(); - MyAvatar.hmdLeanRecenterEnabled = leanEnabled; - } else if (this.teleportMode === "HMDFirstAvatarWillFollow") { - var eyeOffset = Vec3.subtract(MyAvatar.getEyePosition(), MyAvatar.position); - landingPoint = Vec3.sum(landingPoint, eyeOffset); - HMD.position = landingPoint; + this.getArrivalPoints = function(startPoint, endPoint) { + var arrivalPoints = []; + var i; + var lastPoint; + + for (i = 0; i < NUMBER_OF_STEPS; i++) { + if (i === 0) { + lastPoint = startPoint; + } + var newPoint = _this.findMidpoint(lastPoint, endPoint); + lastPoint = newPoint; + arrivalPoints.push(newPoint); } - } + + arrivalPoints.push(endPoint); + + return arrivalPoints; + }; this.smoothArrival = function() { - _this.arrivalPoints = _this.getWayPoints(MyAvatar.position, _this.intersection.intersection, NUMBER_OF_STEPS_FOR_TELEPORT); + + _this.arrivalPoints = _this.getArrivalPoints(MyAvatar.position, _this.intersection.intersection); _this.smoothArrivalInterval = Script.setInterval(function() { if (_this.arrivalPoints.length === 0) { Script.clearInterval(_this.smoothArrivalInterval); @@ -614,7 +587,7 @@ function Teleporter() { return; } var landingPoint = _this.arrivalPoints.shift(); - _this.teleportTo(landingPoint); + MyAvatar.position = landingPoint; if (_this.arrivalPoints.length === 1 || _this.arrivalPoints.length === 0) { _this.hideTargetOverlay(); @@ -626,6 +599,7 @@ function Teleporter() { this.createTargetOverlay(false); this.createCancelOverlay(false); + } //related to repositioning the avatar after you teleport From 76216729645c560e07535ac861fcd3eb341f18a7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 5 Dec 2016 16:00:03 -0800 Subject: [PATCH 23/30] new mic unumute art --- scripts/system/assets/images/tools/mic.svg | 130 +++++++++++---------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/scripts/system/assets/images/tools/mic.svg b/scripts/system/assets/images/tools/mic.svg index 0424f3eb49..d029ba8b2e 100644 --- a/scripts/system/assets/images/tools/mic.svg +++ b/scripts/system/assets/images/tools/mic.svg @@ -1,13 +1,16 @@ - - + + + @@ -26,101 +29,100 @@ - - - - - - - + + + - - - + + + + c0,2.6,2.1,4.8,4.8,5.2v1.8h-2.2c-0.5,0-0.9,0.4-0.9,0.9s0.4,0.9,0.9,0.9H28c0.5,0,0.9-0.4,0.9-0.9s-0.4-0.9-0.9-0.9h-2.3v-1.8 + C28.5,129.1,30.6,126.9,30.6,124.3z"/> - - - + + + c0,2.6,2.1,4.8,4.8,5.2v1.8h-2.2c-0.5,0-0.9,0.4-0.9,0.9s0.4,0.9,0.9,0.9h6.4c0.5,0,0.9-0.4,0.9-0.9s-0.4-0.9-0.9-0.9h-2.3v-1.8 + C28.6,79,30.7,76.9,30.7,74.2z"/> - - - + + + + - + - - - - + + + + - - + + - + + c0,2.6,2.1,4.8,4.8,5.2v1.8h-2.2c-0.5,0-0.9,0.4-0.9,0.9c0,0.5,0.4,0.9,0.9,0.9H28c0.5,0,0.9-0.4,0.9-0.9s-0.4-0.9-0.9-0.9h-2.3 + v-1.8C28.5,29.2,30.6,27,30.6,24.4z"/> - - + + - + + c0,2.6,2.1,4.8,4.8,5.2v1.8h-2.2c-0.5,0-0.9,0.4-0.9,0.9s0.4,0.9,0.9,0.9H28c0.5,0,0.9-0.4,0.9-0.9s-0.4-0.9-0.9-0.9h-2.3v-1.8 + C28.5,179.1,30.6,176.9,30.6,174.3z"/> - + - - + + - + From 7880b5bfe00b31144b5d59ea8148aab795a1c35a Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 5 Dec 2016 17:05:39 -0800 Subject: [PATCH 24/30] Simple fallback now If the bios uuid isn't a valid UUID (on mac or windows it 'should be`) then we read the fallback from the settings and use that, or create and write one to the settings if it is missing. --- libraries/networking/src/FingerprintUtils.cpp | 46 ++++++++++++------- libraries/networking/src/FingerprintUtils.h | 8 ++-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 7f18ccd276..b8e519c05d 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -11,7 +11,7 @@ #include "FingerprintUtils.h" #include - +#include #ifdef Q_OS_WIN #include #include @@ -23,8 +23,11 @@ #include #endif //Q_OS_MAC -QString FingerprintUtils::getMachineFingerprint() { - QString retval; +static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; + +QUuid FingerprintUtils::getMachineFingerprint() { + + QString uuidString; #ifdef Q_OS_LINUX // sadly need to be root to get smbios guid from linux, so @@ -35,9 +38,9 @@ QString FingerprintUtils::getMachineFingerprint() { io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); CFStringRef uuidCf = (CFStringRef) IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); IOObjectRelease(ioRegistryRoot); - retval = QString::fromCFString(uuidCf); + uuidString = QString::fromCFString(uuidCf); CFRelease(uuidCf); - qDebug() << "Mac serial number: " << retval; + qDebug() << "Mac serial number: " << uuidString; #endif //Q_OS_MAC #ifdef Q_OS_WIN @@ -53,7 +56,7 @@ QString FingerprintUtils::getMachineFingerprint() { if (FAILED(hres)) { qDebug() << "Failed to initialize WbemLocator"; - return retval; + return uuidString; } // Connect to WMI through the IWbemLocator::ConnectServer method @@ -76,7 +79,7 @@ QString FingerprintUtils::getMachineFingerprint() { if (FAILED(hres)) { pLoc->Release(); qDebug() << "Failed to connect to WMI"; - return retval; + return uuidString; } // Set security levels on the proxy @@ -95,7 +98,7 @@ QString FingerprintUtils::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "Failed to set security on proxy blanket"; - return retval; + return uuidString; } // Use the IWbemServices pointer to grab the Win32_BIOS stuff @@ -111,7 +114,7 @@ QString FingerprintUtils::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "query to get Win32_ComputerSystemProduct info"; - return retval; + return uuidString; } // Get the SerialNumber from the Win32_BIOS data @@ -134,7 +137,7 @@ QString FingerprintUtils::getMachineFingerprint() { if (!FAILED(hres)) { switch (vtProp.vt) { case VT_BSTR: - retval = QString::fromWCharArray(vtProp.bstrVal); + uuidString = QString::fromWCharArray(vtProp.bstrVal); break; } } @@ -148,13 +151,24 @@ QString FingerprintUtils::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); - qDebug() << "Windows BIOS UUID: " << retval; + qDebug() << "Windows BIOS UUID: " << uuidString; #endif //Q_OS_WIN - // TODO: should we have a fallback for cases where this failed, - // leaving us with an empty string or something that isn't a - // guid? For now keeping this a string, but maybe best to return - // a QUuid? - return retval; + // now, turn into uuid. A malformed string will + // return QUuid() ("{00000...}") + QUuid uuid(uuidString); + if (uuid == QUuid()) { + // read fallback key (if any) + Settings settings; + uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString()); + qDebug() << "read fallback maching fingerprint: " << uuid.toString(); + if (uuid == QUuid()) { + // no fallback yet, set one + uuid = QUuid::createUuid(); + settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString()); + qDebug() << "no fallback machine fingerprint, setting it to: " << uuid.toString(); + } + } + return uuid; } diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index cad198789c..2e4c5db561 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -13,12 +13,10 @@ #define hifi_FingerprintUtils_h #include +#include - -class FingerprintUtils { -public: - static QString getMachineFingerprint(); - +namespace FingerprintUtils { + QUuid getMachineFingerprint(); }; #endif // hifi_FingerprintUtils_h From c40aadfea41015548f5cbbb4590c4b8c03c8c842 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 5 Dec 2016 17:13:12 -0800 Subject: [PATCH 25/30] whitespace --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9178a248ef..1bde51d6cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -586,7 +586,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo FingerprintUtils::getMachineFingerprint(); // End TODO auto nodeList = DependencyManager::get(); - + // Set up a watchdog thread to intentionally crash the application on deadlocks _deadlockWatchdogThread = new DeadlockWatchdogThread(); _deadlockWatchdogThread->start(); From f112d345e84e9df2393f97e7ab1f5a4b5fab4374 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 6 Dec 2016 09:37:54 -0800 Subject: [PATCH 26/30] Add dead zone to yaw controls --- interface/resources/controllers/xbox.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index 36065b87a1..51690dda30 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -25,7 +25,7 @@ ] }, - { "from": "GamePad.RX", "to": "Actions.Yaw" }, + { "from": "GamePad.RX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.Yaw" }, { "from": "GamePad.RY", "to": "Actions.VERTICAL_UP", From 3ba5e1ed4a84a6dc4ad0949788ef84ad60da15d7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 6 Dec 2016 10:52:40 -0800 Subject: [PATCH 27/30] new mic toolbar button art --- scripts/system/assets/images/tools/mic.svg | 225 ++++++++++----------- 1 file changed, 110 insertions(+), 115 deletions(-) diff --git a/scripts/system/assets/images/tools/mic.svg b/scripts/system/assets/images/tools/mic.svg index d029ba8b2e..3a7c183bc4 100644 --- a/scripts/system/assets/images/tools/mic.svg +++ b/scripts/system/assets/images/tools/mic.svg @@ -3,126 +3,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From dc47c5fc0c606a75939ee68007f8c4d786412af4 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 6 Dec 2016 11:12:50 -0800 Subject: [PATCH 28/30] CR feedback lame mistake - it is always the error branches that get you. --- interface/src/Application.cpp | 2 +- libraries/networking/src/FingerprintUtils.cpp | 6 +----- libraries/networking/src/FingerprintUtils.h | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1bde51d6cc..946bad2cc3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -583,7 +583,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us // TODO: This is temporary, while developing - FingerprintUtils::getMachineFingerprint(); + fingerprint::getMachineFingerprint(); // End TODO auto nodeList = DependencyManager::get(); diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index b8e519c05d..1dc17e0c5b 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -25,7 +25,7 @@ static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; -QUuid FingerprintUtils::getMachineFingerprint() { +QUuid fingerprint::getMachineFingerprint() { QString uuidString; @@ -56,7 +56,6 @@ QUuid FingerprintUtils::getMachineFingerprint() { if (FAILED(hres)) { qDebug() << "Failed to initialize WbemLocator"; - return uuidString; } // Connect to WMI through the IWbemLocator::ConnectServer method @@ -79,7 +78,6 @@ QUuid FingerprintUtils::getMachineFingerprint() { if (FAILED(hres)) { pLoc->Release(); qDebug() << "Failed to connect to WMI"; - return uuidString; } // Set security levels on the proxy @@ -98,7 +96,6 @@ QUuid FingerprintUtils::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "Failed to set security on proxy blanket"; - return uuidString; } // Use the IWbemServices pointer to grab the Win32_BIOS stuff @@ -114,7 +111,6 @@ QUuid FingerprintUtils::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "query to get Win32_ComputerSystemProduct info"; - return uuidString; } // Get the SerialNumber from the Win32_BIOS data diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index 2e4c5db561..1d59bd20a5 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -15,7 +15,7 @@ #include #include -namespace FingerprintUtils { +namespace fingerprint { QUuid getMachineFingerprint(); }; From 562cd12bca0d8efcde4551e96a9a947a2599c80c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 6 Dec 2016 15:17:47 -0800 Subject: [PATCH 29/30] More CR feedback Breaking into 2 functions - one public, one private, so I can avoid nested control structures and so on in dealing with errors on the windows side of things. So, no more namespace, back to a class with 2 static functions, one public one private --- interface/src/Application.cpp | 2 +- libraries/networking/src/FingerprintUtils.cpp | 20 ++++++++++++++----- libraries/networking/src/FingerprintUtils.h | 8 ++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 946bad2cc3..1bde51d6cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -583,7 +583,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us // TODO: This is temporary, while developing - fingerprint::getMachineFingerprint(); + FingerprintUtils::getMachineFingerprint(); // End TODO auto nodeList = DependencyManager::get(); diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index 1dc17e0c5b..42a617e93d 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -24,11 +24,8 @@ #endif //Q_OS_MAC static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; - -QUuid fingerprint::getMachineFingerprint() { - +QString FingerprintUtils::getMachineFingerprintString() { QString uuidString; - #ifdef Q_OS_LINUX // sadly need to be root to get smbios guid from linux, so // for now lets do nothing. @@ -56,6 +53,7 @@ QUuid fingerprint::getMachineFingerprint() { if (FAILED(hres)) { qDebug() << "Failed to initialize WbemLocator"; + return uuidString; } // Connect to WMI through the IWbemLocator::ConnectServer method @@ -78,6 +76,7 @@ QUuid fingerprint::getMachineFingerprint() { if (FAILED(hres)) { pLoc->Release(); qDebug() << "Failed to connect to WMI"; + return uuidString; } // Set security levels on the proxy @@ -96,6 +95,7 @@ QUuid fingerprint::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "Failed to set security on proxy blanket"; + return uuidString; } // Use the IWbemServices pointer to grab the Win32_BIOS stuff @@ -111,6 +111,7 @@ QUuid fingerprint::getMachineFingerprint() { pSvc->Release(); pLoc->Release(); qDebug() << "query to get Win32_ComputerSystemProduct info"; + return uuidString; } // Get the SerialNumber from the Win32_BIOS data @@ -150,8 +151,17 @@ QUuid fingerprint::getMachineFingerprint() { qDebug() << "Windows BIOS UUID: " << uuidString; #endif //Q_OS_WIN + return uuidString; + +} + +QUuid FingerprintUtils::getMachineFingerprint() { + + QString uuidString = getMachineFingerprintString(); + // now, turn into uuid. A malformed string will - // return QUuid() ("{00000...}") + // return QUuid() ("{00000...}"), which handles + // any errors in getting the string QUuid uuid(uuidString); if (uuid == QUuid()) { // read fallback key (if any) diff --git a/libraries/networking/src/FingerprintUtils.h b/libraries/networking/src/FingerprintUtils.h index 1d59bd20a5..572b150ec4 100644 --- a/libraries/networking/src/FingerprintUtils.h +++ b/libraries/networking/src/FingerprintUtils.h @@ -15,8 +15,12 @@ #include #include -namespace fingerprint { - QUuid getMachineFingerprint(); +class FingerprintUtils { +public: + static QUuid getMachineFingerprint(); + +private: + static QString getMachineFingerprintString(); }; #endif // hifi_FingerprintUtils_h From 5c3480e2a22ab2c345f441f2ef38e7ac30e1324d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 6 Dec 2016 16:09:20 -0800 Subject: [PATCH 30/30] Preserve and restore the GL context when resizing QML surfaces --- libraries/gl/src/gl/GLHelpers.cpp | 12 +++++ libraries/gl/src/gl/GLHelpers.h | 5 ++ libraries/gl/src/gl/OffscreenQmlSurface.cpp | 57 +++++++++++---------- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index dca1214b32..ab91ca0902 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -69,3 +69,15 @@ QThread* RENDER_THREAD = nullptr; bool isRenderThread() { return QThread::currentThread() == RENDER_THREAD; } + +namespace gl { + void withSavedContext(const std::function& f) { + // Save the original GL context, because creating a QML surface will create a new context + QOpenGLContext * savedContext = QOpenGLContext::currentContext(); + QSurface * savedSurface = savedContext ? savedContext->surface() : nullptr; + f(); + if (savedContext) { + savedContext->makeCurrent(savedSurface); + } + } +} diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index daa181467d..84229b97d2 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -10,6 +10,7 @@ #ifndef hifi_GLHelpers_h #define hifi_GLHelpers_h +#include #include // 16 bits of depth precision @@ -34,4 +35,8 @@ int glVersionToInteger(QString glVersion); bool isRenderThread(); +namespace gl { + void withSavedContext(const std::function& f); +} + #endif diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 06f755c1dd..cde779d101 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -467,40 +467,41 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { } qCDebug(glLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height(); + gl::withSavedContext([&] { + _canvas->makeCurrent(); - _canvas->makeCurrent(); - - // Release hold on the textures of the old size - if (uvec2() != _size) { - // If the most recent texture was unused, we can directly recycle it - if (_latestTextureAndFence.first) { - offscreenTextures.releaseTexture(_latestTextureAndFence); - _latestTextureAndFence = { 0, 0 }; + // Release hold on the textures of the old size + if (uvec2() != _size) { + // If the most recent texture was unused, we can directly recycle it + if (_latestTextureAndFence.first) { + offscreenTextures.releaseTexture(_latestTextureAndFence); + _latestTextureAndFence = { 0, 0 }; + } + offscreenTextures.releaseSize(_size); } - offscreenTextures.releaseSize(_size); - } - _size = newOffscreenSize; + _size = newOffscreenSize; - // Acquire the new texture size - if (uvec2() != _size) { - offscreenTextures.acquireSize(_size); - if (_depthStencil) { - glDeleteRenderbuffers(1, &_depthStencil); - _depthStencil = 0; + // Acquire the new texture size + if (uvec2() != _size) { + offscreenTextures.acquireSize(_size); + if (_depthStencil) { + glDeleteRenderbuffers(1, &_depthStencil); + _depthStencil = 0; + } + glGenRenderbuffers(1, &_depthStencil); + glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _size.x, _size.y); + if (!_fbo) { + glGenFramebuffers(1, &_fbo); + } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } - glGenRenderbuffers(1, &_depthStencil); - glBindRenderbuffer(GL_RENDERBUFFER, _depthStencil); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _size.x, _size.y); - if (!_fbo) { - glGenFramebuffers(1, &_fbo); - } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - } - _canvas->doneCurrent(); + _canvas->doneCurrent(); + }); } QQuickItem* OffscreenQmlSurface::getRootItem() {