From 7022c4009fafa72fffa0fcf45aac9919557a280f Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 15:18:06 -0700 Subject: [PATCH 001/280] midi in and out --- includes/BuildInfo.h | 29 +++ interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 14 +- libraries/midi/CMakeLists.txt | 3 + libraries/midi/src/Midi.cpp | 223 ++++++++++++++++++ libraries/midi/src/Midi.h | 68 ++++++ libraries/script-engine/src/ScriptEngine.cpp | 3 + libraries/shared/src/USBEventListener.cpp | 48 ++++ libraries/shared/src/USBEventListener.h | 28 +++ scripts/tutorials/createMidiSphere.js | 49 ++++ .../tutorials/entity_scripts/midiSphere.js | 52 ++++ 11 files changed, 514 insertions(+), 5 deletions(-) create mode 100644 includes/BuildInfo.h create mode 100644 libraries/midi/CMakeLists.txt create mode 100644 libraries/midi/src/Midi.cpp create mode 100644 libraries/midi/src/Midi.h create mode 100644 libraries/shared/src/USBEventListener.cpp create mode 100644 libraries/shared/src/USBEventListener.h create mode 100644 scripts/tutorials/createMidiSphere.js create mode 100644 scripts/tutorials/entity_scripts/midiSphere.js diff --git a/includes/BuildInfo.h b/includes/BuildInfo.h new file mode 100644 index 0000000000..76faf1fe84 --- /dev/null +++ b/includes/BuildInfo.h @@ -0,0 +1,29 @@ +// +// BuildInfo.h.in +// cmake/templates +// +// Created by Stephen Birarda on 1/14/16. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#define USE_STABLE_GLOBAL_SERVICES 0 + +#include + +namespace BuildInfo { + // WARNING: This file has been auto-generated. + // Check cmake/templates/BuildInfo.h.in if you want to modify it. + + const QString INTERFACE_NAME = "Interface"; + const QString ASSIGNMENT_CLIENT_NAME = "assignment-client"; + const QString DOMAIN_SERVER_NAME = "domain-server"; + const QString AC_CLIENT_SERVER_NAME = "ac-client"; + const QString MODIFIED_ORGANIZATION = "High Fidelity - dev"; + const QString ORGANIZATION_DOMAIN = "highfidelity.io"; + const QString VERSION = "dev"; + const QString BUILD_BRANCH = ""; + const QString BUILD_GLOBAL_SERVICES = "DEVELOPMENT"; +} diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 71341f3f11..79758c625b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -193,7 +193,7 @@ link_hifi_libraries( shared octree ktx gpu gl gpu-gl procedural model render recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics - render-utils entities-renderer avatars-renderer ui auto-updater + render-utils entities-renderer avatars-renderer ui auto-updater midi controllers plugins image trackers ui-plugins display-plugins input-plugins ${NON_ANDROID_LIBRARIES} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 890b5cb455..d54c0da371 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -395,7 +396,11 @@ public: return true; } } - } + + if (message->message == WM_DEVICECHANGE) { + Midi::USBchanged(); // re-scan the MIDI bus + } + } return false; } }; @@ -518,8 +523,9 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -640,7 +646,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _entityClipboard->createRootElement(); #ifdef Q_OS_WIN - installNativeEventFilter(&MyNativeEventFilter::getInstance()); + installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif _logger = new FileLogger(this); // After setting organization name in order to get correct directory diff --git a/libraries/midi/CMakeLists.txt b/libraries/midi/CMakeLists.txt new file mode 100644 index 0000000000..dc54819c2b --- /dev/null +++ b/libraries/midi/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME midi) +setup_hifi_library(Network) +link_hifi_libraries(shared networking) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp new file mode 100644 index 0000000000..5f5b4fa2f3 --- /dev/null +++ b/libraries/midi/src/Midi.cpp @@ -0,0 +1,223 @@ +// +// Midi.cpp +// libraries/midi/src +// +// Created by Burt Sloane +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Midi.h" + + +#include + + +#if defined Q_OS_WIN32 +#include "Windows.h" +#endif + + +static Midi* instance = NULL; // communicate this to non-class callbacks + + +#if defined Q_OS_WIN32 + +#pragma comment(lib, "Winmm.lib") + +// +std::vector midihin; +std::vector midihout; + +std::vector Midi::midiinexclude; +std::vector Midi::midioutexclude; + + +void CALLBACK MidiInProc( + HMIDIIN hMidiIn, + UINT wMsg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 +) { + if (wMsg == MIM_OPEN) { + } + else if (wMsg == MIM_CLOSE) { + for (int i = 0; i < midihin.size(); i++) if (midihin[i] == hMidiIn) { + midihin[i] = NULL; + instance->sendNote(0xb0, 0x7b, 0); // all notes off + } + } + else if (wMsg == MIM_DATA) { + int status = 0x0ff & dwParam1; + int note = 0x0ff & (dwParam1 >> 8); + int vel = 0x0ff & (dwParam1 >> 16); +//sendNote(status, note, vel); // NOTE: relay the note on to all other midi devices + instance->Midi::noteReceived(status, note, vel); + } +} + + +void CALLBACK MidiOutProc( + HMIDIOUT hmo, + UINT wMsg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 + ) { + if (wMsg == MOM_CLOSE) { + for (int i = 0; i < midihout.size(); i++) if (midihout[i] == hmo) { + midihout[i] = NULL; + instance->sendNote(0xb0, 0x7b, 0); // all notes off + } + } +} + + +void Midi::sendNote(int status, int note, int vel) { + for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); + } +} + +void Midi::noteReceived(int status, int note, int velocity) { + if (status >= 0x0a0) return; // NOTE: only sending note-on and note-off to Javascript + + QVariantMap eventData; + eventData["status"] = status; + eventData["note"] = note; + eventData["velocity"] = velocity; + emit midiNote(eventData); +} + + +void Midi::MidiSetup() { + midihin.clear(); + midihout.clear(); + + MIDIINCAPS incaps; + for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { + midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); + + bool found = false; + for (int j = 0; j < midiinexclude.size(); j++) { + if (lstrcmp(midiinexclude[j].toStdString().c_str(), incaps.szPname) == 0) found = true; + } + if (!found) // EXCLUDE AN INPUT BY NAME + { + HMIDIIN tmphin; + midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION); + midiInStart(tmphin); + midihin.push_back(tmphin); + } + + } + + MIDIOUTCAPS outcaps; + for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { + midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); + + bool found = false; + for (int j = 0; j < midioutexclude.size(); j++) { + if (lstrcmp(midioutexclude[j].toStdString().c_str(), outcaps.szPname) == 0) found = true; + } + if (!found) // EXCLUDE AN OUTPUT BY NAME + { + HMIDIOUT tmphout; + midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION); + midihout.push_back(tmphout); + } + } + + sendNote(0xb0, 0x7b, 0); // all notes off +} + +void Midi::MidiCleanup() { + sendNote(0xb0, 0x7b, 0); // all notes off + + for (int i = 0; i < midihin.size(); i++) if (midihin[i] != NULL) { + midiInStop(midihin[i]); + midiInClose(midihin[i]); + } + for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { + midiOutClose(midihout[i]); + } + midihin.clear(); + midihout.clear(); +} +#endif + +// + +Midi::Midi() { + instance = this; + midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing + MidiSetup(); +} + +void Midi::playMidiNote(int status, int note, int velocity) { + sendNote(status, note, velocity); +} + +void Midi::allNotesOff() { + sendNote(0xb0, 0x7b, 0); // all notes off +} + +void Midi::resetDevices() { + MidiCleanup(); + MidiSetup(); +} + +void Midi::USBchanged() { + instance->MidiCleanup(); + instance->MidiSetup(); +} + +// + +QStringList Midi::listMidiDevices(bool output) { + QStringList rv; + if (output) { + MIDIOUTCAPS outcaps; + for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { + midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); + rv.append(outcaps.szPname); + } + } + else { + MIDIINCAPS incaps; + for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { + midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); + rv.append(incaps.szPname); + } + } + return rv; +} + +void Midi::unblockMidiDevice(QString name, bool output) { + if (output) { + for (int i = 0; i < midioutexclude.size(); i++) if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { + midioutexclude.erase(midioutexclude.begin() + i); + break; + } + } + else { + for (int i = 0; i < midiinexclude.size(); i++) if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { + midiinexclude.erase(midiinexclude.begin() + i); + break; + } + } +} + +void Midi::blockMidiDevice(QString name, bool output) { + unblockMidiDevice(name, output); // make sure it's only in there once + if (output) { + midioutexclude.push_back(name); + } + else { + midiinexclude.push_back(name); + } +} + diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h new file mode 100644 index 0000000000..66e45f0a53 --- /dev/null +++ b/libraries/midi/src/Midi.h @@ -0,0 +1,68 @@ +// +// Midi.h +// libraries/midi/src +// +// Created by Burt Sloane +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Midi_h +#define hifi_Midi_h + +#include +#include +#include + +#include +#include + +class Midi : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + Midi(); + +public: + void noteReceived(int status, int note, int velocity); // relay a note to Javascript + void sendNote(int status, int note, int vel); // relay a note to MIDI outputs + static void USBchanged(); + +private: + static std::vector midiinexclude; + static std::vector midioutexclude; + +private: + void MidiSetup(); + void MidiCleanup(); + +signals: + void midiNote(QVariantMap eventData); + +public slots: +/// play a note on all connected devices +/// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc +/// @param {int} note: midi note number +/// @param {int} velocity: note velocity (0 means noteoff) +Q_INVOKABLE void playMidiNote(int status, int note, int velocity); + +/// turn off all notes on all connected devices +Q_INVOKABLE void allNotesOff(); + +/// clean up and re-discover attached devices +Q_INVOKABLE void resetDevices(); + +/// ask for a list of inputs/outputs +Q_INVOKABLE QStringList listMidiDevices(bool output); + +/// block an input/output by name +Q_INVOKABLE void blockMidiDevice(QString name, bool output); + +/// unblock an input/output by name +Q_INVOKABLE void unblockMidiDevice(QString name, bool output); +}; + +#endif // hifi_Midi_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 61bf601019..ba162ba502 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -76,6 +76,7 @@ #include +#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work? #include "MIDIEvent.h" const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { @@ -662,6 +663,8 @@ void ScriptEngine::init() { registerGlobalObject("Audio", DependencyManager::get().data()); + registerGlobalObject("Midi", DependencyManager::get().data()); + registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); diff --git a/libraries/shared/src/USBEventListener.cpp b/libraries/shared/src/USBEventListener.cpp new file mode 100644 index 0000000000..c71277dfed --- /dev/null +++ b/libraries/shared/src/USBEventListener.cpp @@ -0,0 +1,48 @@ +// +// USBEventListener.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 09/03/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "USBEventListener.h" + +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +#include +#include + +USBEventListener& USBEventListener::getInstance() { + static USBEventListener staticInstance; + return staticInstance; +} + +void signalHandler(int param) { + // tell the qApp it should quit + QMetaObject::invokeMethod(qApp, "quit"); +} + +USBEventListener::USBEventListener(QObject* parent) : QObject(parent) { +#ifndef Q_OS_WIN +#endif +} + + +bool USBEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) { +#ifdef Q_OS_WIN + if (eventType == "windows_generic_MSG") { + MSG* message = (MSG*)msg; + if (message->message == WM_DEVICECHANGE) { + } + } +#endif + return false; +} diff --git a/libraries/shared/src/USBEventListener.h b/libraries/shared/src/USBEventListener.h new file mode 100644 index 0000000000..9ab008256e --- /dev/null +++ b/libraries/shared/src/USBEventListener.h @@ -0,0 +1,28 @@ +// +// USBEventListener.h +// libraries/midi +// +// Created by Burt Sloane +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_USBEventListener_h +#define hifi_USBEventListener_h + +#include +#include + +class USBEventListener : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT +public: + static USBEventListener& getInstance(); + + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; +private: + USBEventListener(QObject* parent = 0); +}; + +#endif // hifi_USBEventListener_h diff --git a/scripts/tutorials/createMidiSphere.js b/scripts/tutorials/createMidiSphere.js new file mode 100644 index 0000000000..705acac9de --- /dev/null +++ b/scripts/tutorials/createMidiSphere.js @@ -0,0 +1,49 @@ +// +// Created by James B. Pollack @imgntn on April 18, 2016. +// Adapted by Burt +// 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 +// + +var SCRIPT_URL = "file:///e:/hifi/scripts/tutorials/entity_scripts/midiSphere.js"; +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); + +var BALL_GRAVITY = { + x: 0, + y: 0, + z: 0 +}; + +var BALL_DIMENSIONS = { + x: 0.4, + y: 0.4, + z: 0.4 +}; + + +var BALL_COLOR = { + red: 255, + green: 0, + blue: 0 +}; + +var midiSphereProperties = { + name: 'MIDI Sphere', + shapeType: 'sphere', + type: 'Sphere', + script: SCRIPT_URL, + color: BALL_COLOR, + dimensions: BALL_DIMENSIONS, + gravity: BALL_GRAVITY, + dynamic: false, + position: center, + collisionless: false, + ignoreForCollisions: true +}; + +var midiSphere = Entities.addEntity(midiSphereProperties); + +Script.stop(); diff --git a/scripts/tutorials/entity_scripts/midiSphere.js b/scripts/tutorials/entity_scripts/midiSphere.js new file mode 100644 index 0000000000..5ed29a330f --- /dev/null +++ b/scripts/tutorials/entity_scripts/midiSphere.js @@ -0,0 +1,52 @@ +// midiSphere.js +// +// Script Type: Entity +// Created by James B. Pollack @imgntn on 9/21/2015 +// Adapted by Burt +// Copyright 2015 High Fidelity, Inc. +// +// This script listens to MIDI and makes the ball change color. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var _this; + + function MidiSphere() { + _this = this; + this.clicked = false; + return; + } + + MidiSphere.prototype = { + preload: function(entityID) { + this.entityID = entityID; + Midi.midiNote.connect(function(eventData) { + print("MidiSphere.noteReceived: "+JSON.stringify(eventData)); + Entities.editEntity(entityID, { color: { red: 2*eventData.note, green: 2*eventData.note, blue: 2*eventData.note} }); + }); + print("MidiSphere.preload"); + }, + unload: function(entityID) { + print("MidiSphere.unload"); + }, + + clickDownOnEntity: function(entityID, mouseEvent) { + print("MidiSphere.clickDownOnEntity"); + if (this.clicked) { + Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); + this.clicked = false; + Midi.playMidiNote(144, 64, 0); + } else { + Entities.editEntity(entityID, { color: { red: 255, green: 255, blue: 0} }); + this.clicked = true; + Midi.playMidiNote(144, 64, 100); + } + } + + }; + + // entity scripts should return a newly constructed object of our type + return new MidiSphere(); +}); \ No newline at end of file From ade017fab043898108866262cedb798a8a30e192 Mon Sep 17 00:00:00 2001 From: burtsloane Date: Thu, 15 Jun 2017 15:21:36 -0700 Subject: [PATCH 002/280] Delete BuildInfo.h --- includes/BuildInfo.h | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 includes/BuildInfo.h diff --git a/includes/BuildInfo.h b/includes/BuildInfo.h deleted file mode 100644 index 76faf1fe84..0000000000 --- a/includes/BuildInfo.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// BuildInfo.h.in -// cmake/templates -// -// Created by Stephen Birarda on 1/14/16. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#define USE_STABLE_GLOBAL_SERVICES 0 - -#include - -namespace BuildInfo { - // WARNING: This file has been auto-generated. - // Check cmake/templates/BuildInfo.h.in if you want to modify it. - - const QString INTERFACE_NAME = "Interface"; - const QString ASSIGNMENT_CLIENT_NAME = "assignment-client"; - const QString DOMAIN_SERVER_NAME = "domain-server"; - const QString AC_CLIENT_SERVER_NAME = "ac-client"; - const QString MODIFIED_ORGANIZATION = "High Fidelity - dev"; - const QString ORGANIZATION_DOMAIN = "highfidelity.io"; - const QString VERSION = "dev"; - const QString BUILD_BRANCH = ""; - const QString BUILD_GLOBAL_SERVICES = "DEVELOPMENT"; -} From 19e35e1cd005590571a4c13d39abd5f036bae388 Mon Sep 17 00:00:00 2001 From: burtsloane Date: Thu, 15 Jun 2017 15:22:40 -0700 Subject: [PATCH 003/280] Delete USBEventListener.h --- libraries/shared/src/USBEventListener.h | 28 ------------------------- 1 file changed, 28 deletions(-) delete mode 100644 libraries/shared/src/USBEventListener.h diff --git a/libraries/shared/src/USBEventListener.h b/libraries/shared/src/USBEventListener.h deleted file mode 100644 index 9ab008256e..0000000000 --- a/libraries/shared/src/USBEventListener.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// USBEventListener.h -// libraries/midi -// -// Created by Burt Sloane -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_USBEventListener_h -#define hifi_USBEventListener_h - -#include -#include - -class USBEventListener : public QObject, public QAbstractNativeEventFilter { - Q_OBJECT -public: - static USBEventListener& getInstance(); - - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; -private: - USBEventListener(QObject* parent = 0); -}; - -#endif // hifi_USBEventListener_h From 2358d51ef14bd94dfe41356e194f9945b9da4bb4 Mon Sep 17 00:00:00 2001 From: burtsloane Date: Thu, 15 Jun 2017 15:22:51 -0700 Subject: [PATCH 004/280] Delete USBEventListener.cpp --- libraries/shared/src/USBEventListener.cpp | 48 ----------------------- 1 file changed, 48 deletions(-) delete mode 100644 libraries/shared/src/USBEventListener.cpp diff --git a/libraries/shared/src/USBEventListener.cpp b/libraries/shared/src/USBEventListener.cpp deleted file mode 100644 index c71277dfed..0000000000 --- a/libraries/shared/src/USBEventListener.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// USBEventListener.cpp -// libraries/shared/src -// -// Created by Ryan Huffman on 09/03/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "USBEventListener.h" - -#ifdef Q_OS_WIN -#include -#else -#include -#endif - -#include -#include - -USBEventListener& USBEventListener::getInstance() { - static USBEventListener staticInstance; - return staticInstance; -} - -void signalHandler(int param) { - // tell the qApp it should quit - QMetaObject::invokeMethod(qApp, "quit"); -} - -USBEventListener::USBEventListener(QObject* parent) : QObject(parent) { -#ifndef Q_OS_WIN -#endif -} - - -bool USBEventListener::nativeEventFilter(const QByteArray &eventType, void* msg, long* result) { -#ifdef Q_OS_WIN - if (eventType == "windows_generic_MSG") { - MSG* message = (MSG*)msg; - if (message->message == WM_DEVICECHANGE) { - } - } -#endif - return false; -} From af5e812f00affc5d59cb01140895612e937d5452 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 15:56:49 -0700 Subject: [PATCH 005/280] neutralized for non-windows compile --- libraries/midi/src/Midi.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 5f5b4fa2f3..8ae6c085bb 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -22,6 +22,9 @@ static Midi* instance = NULL; // communicate this to non-class callbacks +std::vector Midi::midiinexclude; +std::vector Midi::midioutexclude; + #if defined Q_OS_WIN32 @@ -31,9 +34,6 @@ static Midi* instance = NULL; // communicate this to non-class callbacks std::vector midihin; std::vector midihout; -std::vector Midi::midiinexclude; -std::vector Midi::midioutexclude; - void CALLBACK MidiInProc( HMIDIIN hMidiIn, @@ -147,6 +147,17 @@ void Midi::MidiCleanup() { midihin.clear(); midihout.clear(); } +#else +void Midi::sendNote(int status, int note, int vel) { +} + +void Midi::MidiSetup() { + sendNote(0xb0, 0x7b, 0); // all notes off +} + +void Midi::MidiCleanup() { + sendNote(0xb0, 0x7b, 0); // all notes off +} #endif // @@ -179,6 +190,7 @@ void Midi::USBchanged() { QStringList Midi::listMidiDevices(bool output) { QStringList rv; +#if defined Q_OS_WIN32 if (output) { MIDIOUTCAPS outcaps; for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { @@ -193,6 +205,7 @@ QStringList Midi::listMidiDevices(bool output) { rv.append(incaps.szPname); } } +#endif return rv; } From a73281b827e1aec988ab05cc2b540e10779bd793 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 16:10:08 -0700 Subject: [PATCH 006/280] fix PR issues --- interface/src/Application.cpp | 2 +- libraries/midi/src/Midi.cpp | 50 +++++++++++++++-------------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d54c0da371..9d11c6f2be 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -646,7 +646,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _entityClipboard->createRootElement(); #ifdef Q_OS_WIN - installNativeEventFilter(&MyNativeEventFilter::getInstance()); + installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif _logger = new FileLogger(this); // After setting organization name in order to get correct directory diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 8ae6c085bb..6e76565bed 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -20,6 +20,13 @@ #endif +const int MIDI_STATUS_MASK = 0x0F0; +const int MIDI_NOTE_OFF = 0x080; +const int MIDI_NOTE_ON = 0x090; +const int MIDI_CONTROL_CHANGE = 0x0b0; +const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b; + + static Midi* instance = NULL; // communicate this to non-class callbacks std::vector Midi::midiinexclude; @@ -35,22 +42,14 @@ std::vector midihin; std::vector midihout; -void CALLBACK MidiInProc( - HMIDIIN hMidiIn, - UINT wMsg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 -) { +void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { if (wMsg == MIM_OPEN) { - } - else if (wMsg == MIM_CLOSE) { + } else if (wMsg == MIM_CLOSE) { for (int i = 0; i < midihin.size(); i++) if (midihin[i] == hMidiIn) { midihin[i] = NULL; - instance->sendNote(0xb0, 0x7b, 0); // all notes off + instance->allNotesOff(); } - } - else if (wMsg == MIM_DATA) { + } else if (wMsg == MIM_DATA) { int status = 0x0ff & dwParam1; int note = 0x0ff & (dwParam1 >> 8); int vel = 0x0ff & (dwParam1 >> 16); @@ -60,17 +59,11 @@ void CALLBACK MidiInProc( } -void CALLBACK MidiOutProc( - HMIDIOUT hmo, - UINT wMsg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 - ) { +void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { if (wMsg == MOM_CLOSE) { for (int i = 0; i < midihout.size(); i++) if (midihout[i] == hmo) { midihout[i] = NULL; - instance->sendNote(0xb0, 0x7b, 0); // all notes off + instance->allNotesOff(); } } } @@ -83,7 +76,8 @@ void Midi::sendNote(int status, int note, int vel) { } void Midi::noteReceived(int status, int note, int velocity) { - if (status >= 0x0a0) return; // NOTE: only sending note-on and note-off to Javascript + if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && + ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) return; // NOTE: only sending note-on and note-off to Javascript QVariantMap eventData; eventData["status"] = status; @@ -103,7 +97,7 @@ void Midi::MidiSetup() { bool found = false; for (int j = 0; j < midiinexclude.size(); j++) { - if (lstrcmp(midiinexclude[j].toStdString().c_str(), incaps.szPname) == 0) found = true; + if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) found = true; } if (!found) // EXCLUDE AN INPUT BY NAME { @@ -121,7 +115,7 @@ void Midi::MidiSetup() { bool found = false; for (int j = 0; j < midioutexclude.size(); j++) { - if (lstrcmp(midioutexclude[j].toStdString().c_str(), outcaps.szPname) == 0) found = true; + if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) found = true; } if (!found) // EXCLUDE AN OUTPUT BY NAME { @@ -131,11 +125,11 @@ void Midi::MidiSetup() { } } - sendNote(0xb0, 0x7b, 0); // all notes off + allNotesOff(); } void Midi::MidiCleanup() { - sendNote(0xb0, 0x7b, 0); // all notes off + allNotesOff(); for (int i = 0; i < midihin.size(); i++) if (midihin[i] != NULL) { midiInStop(midihin[i]); @@ -152,11 +146,11 @@ void Midi::sendNote(int status, int note, int vel) { } void Midi::MidiSetup() { - sendNote(0xb0, 0x7b, 0); // all notes off + allNotesOff(); } void Midi::MidiCleanup() { - sendNote(0xb0, 0x7b, 0); // all notes off + allNotesOff(); } #endif @@ -173,7 +167,7 @@ void Midi::playMidiNote(int status, int note, int velocity) { } void Midi::allNotesOff() { - sendNote(0xb0, 0x7b, 0); // all notes off + sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off } void Midi::resetDevices() { From 7d92ef9f2589cd48d1e35e50340d5db5064d4391 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 16:12:16 -0700 Subject: [PATCH 007/280] more PR fixes --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9d11c6f2be..4f645eb1d2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -523,9 +523,9 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); From fbc28c0e2fd70ce7de726f7726ed8852fb4bc927 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 16:16:29 -0700 Subject: [PATCH 008/280] pedantic PR --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ba162ba502..3a846d3026 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -663,7 +663,7 @@ void ScriptEngine::init() { registerGlobalObject("Audio", DependencyManager::get().data()); - registerGlobalObject("Midi", DependencyManager::get().data()); + registerGlobalObject("Midi", DependencyManager::get().data()); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); From c35020eb5a4552f31956a3a5a18e7846c20e6bfd Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 16:24:12 -0700 Subject: [PATCH 009/280] last of the PR changes --- libraries/midi/src/Midi.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 6e76565bed..60e4b61cbd 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -99,8 +99,7 @@ void Midi::MidiSetup() { for (int j = 0; j < midiinexclude.size(); j++) { if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) found = true; } - if (!found) // EXCLUDE AN INPUT BY NAME - { + if (!found) { // EXCLUDE AN INPUT BY NAME HMIDIIN tmphin; midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION); midiInStart(tmphin); @@ -117,8 +116,7 @@ void Midi::MidiSetup() { for (int j = 0; j < midioutexclude.size(); j++) { if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) found = true; } - if (!found) // EXCLUDE AN OUTPUT BY NAME - { + if (!found) { // EXCLUDE AN OUTPUT BY NAME HMIDIOUT tmphout; midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION); midihout.push_back(tmphout); From 4f7a1a6e2ef932ff14dff794a32cd458fa92c64a Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 16:35:53 -0700 Subject: [PATCH 010/280] got rid of tabs --- interface/src/Application.cpp | 8 +- libraries/midi/src/Midi.cpp | 245 +++++++++--------- libraries/midi/src/Midi.h | 18 +- libraries/script-engine/src/ScriptEngine.cpp | 2 +- scripts/tutorials/createMidiSphere.js | 8 +- .../tutorials/entity_scripts/midiSphere.js | 42 +-- 6 files changed, 168 insertions(+), 155 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4f645eb1d2..ffd37bb813 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -397,10 +397,10 @@ public: } } - if (message->message == WM_DEVICECHANGE) { - Midi::USBchanged(); // re-scan the MIDI bus - } - } + if (message->message == WM_DEVICECHANGE) { + Midi::USBchanged(); // re-scan the MIDI bus + } + } return false; } }; diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 60e4b61cbd..a3d4db0c88 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -27,7 +27,7 @@ const int MIDI_CONTROL_CHANGE = 0x0b0; const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b; -static Midi* instance = NULL; // communicate this to non-class callbacks +static Midi* instance = NULL; // communicate this to non-class callbacks std::vector Midi::midiinexclude; std::vector Midi::midioutexclude; @@ -43,186 +43,199 @@ std::vector midihout; void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { - if (wMsg == MIM_OPEN) { - } else if (wMsg == MIM_CLOSE) { - for (int i = 0; i < midihin.size(); i++) if (midihin[i] == hMidiIn) { - midihin[i] = NULL; - instance->allNotesOff(); - } - } else if (wMsg == MIM_DATA) { - int status = 0x0ff & dwParam1; - int note = 0x0ff & (dwParam1 >> 8); - int vel = 0x0ff & (dwParam1 >> 16); -//sendNote(status, note, vel); // NOTE: relay the note on to all other midi devices - instance->Midi::noteReceived(status, note, vel); - } + if (wMsg == MIM_OPEN) { + } else if (wMsg == MIM_CLOSE) { + for (int i = 0; i < midihin.size(); i++) { + if (midihin[i] == hMidiIn) { + midihin[i] = NULL; + instance->allNotesOff(); + } + } + } else if (wMsg == MIM_DATA) { + int status = 0x0ff & dwParam1; + int note = 0x0ff & (dwParam1 >> 8); + int vel = 0x0ff & (dwParam1 >> 16); +//sendNote(status, note, vel); // NOTE: relay the note on to all other midi devices + instance->Midi::noteReceived(status, note, vel); + } } void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { - if (wMsg == MOM_CLOSE) { - for (int i = 0; i < midihout.size(); i++) if (midihout[i] == hmo) { - midihout[i] = NULL; - instance->allNotesOff(); - } - } + if (wMsg == MOM_CLOSE) { + for (int i = 0; i < midihout.size(); i++) { + if (midihout[i] == hmo) { + midihout[i] = NULL; + instance->allNotesOff(); + } + } + } } void Midi::sendNote(int status, int note, int vel) { - for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); - } + for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); + } } void Midi::noteReceived(int status, int note, int velocity) { - if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && - ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) return; // NOTE: only sending note-on and note-off to Javascript + if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && + ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) return; // NOTE: only sending note-on and note-off to Javascript - QVariantMap eventData; - eventData["status"] = status; - eventData["note"] = note; - eventData["velocity"] = velocity; - emit midiNote(eventData); + QVariantMap eventData; + eventData["status"] = status; + eventData["note"] = note; + eventData["velocity"] = velocity; + emit midiNote(eventData); } void Midi::MidiSetup() { - midihin.clear(); - midihout.clear(); + midihin.clear(); + midihout.clear(); - MIDIINCAPS incaps; - for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { - midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); + MIDIINCAPS incaps; + for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { + midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); - bool found = false; - for (int j = 0; j < midiinexclude.size(); j++) { - if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) found = true; - } - if (!found) { // EXCLUDE AN INPUT BY NAME - HMIDIIN tmphin; - midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION); - midiInStart(tmphin); - midihin.push_back(tmphin); - } + bool found = false; + for (int j = 0; j < midiinexclude.size(); j++) { + if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) { + found = true; + } + } + if (!found) { // EXCLUDE AN INPUT BY NAME + HMIDIIN tmphin; + midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION); + midiInStart(tmphin); + midihin.push_back(tmphin); + } - } + } - MIDIOUTCAPS outcaps; - for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { - midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); + MIDIOUTCAPS outcaps; + for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { + midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); - bool found = false; - for (int j = 0; j < midioutexclude.size(); j++) { - if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) found = true; - } - if (!found) { // EXCLUDE AN OUTPUT BY NAME - HMIDIOUT tmphout; - midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION); - midihout.push_back(tmphout); - } - } + bool found = false; + for (int j = 0; j < midioutexclude.size(); j++) { + if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) { + found = true; + } + } + if (!found) { // EXCLUDE AN OUTPUT BY NAME + HMIDIOUT tmphout; + midiOutOpen(&tmphout, i, (DWORD_PTR)MidiOutProc, NULL, CALLBACK_FUNCTION); + midihout.push_back(tmphout); + } + } - allNotesOff(); + allNotesOff(); } void Midi::MidiCleanup() { - allNotesOff(); + allNotesOff(); - for (int i = 0; i < midihin.size(); i++) if (midihin[i] != NULL) { - midiInStop(midihin[i]); - midiInClose(midihin[i]); - } - for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { - midiOutClose(midihout[i]); - } - midihin.clear(); - midihout.clear(); + for (int i = 0; i < midihin.size(); i++) { + if (midihin[i] != NULL) { + midiInStop(midihin[i]); + midiInClose(midihin[i]); + } + } + for (int i = 0; i < midihout.size(); i++) { + if (midihout[i] != NULL) { + midiOutClose(midihout[i]); + } + } + midihin.clear(); + midihout.clear(); } #else void Midi::sendNote(int status, int note, int vel) { } void Midi::MidiSetup() { - allNotesOff(); + allNotesOff(); } void Midi::MidiCleanup() { - allNotesOff(); + allNotesOff(); } #endif // Midi::Midi() { - instance = this; - midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing - MidiSetup(); + instance = this; + midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing + MidiSetup(); } void Midi::playMidiNote(int status, int note, int velocity) { - sendNote(status, note, velocity); + sendNote(status, note, velocity); } void Midi::allNotesOff() { - sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off + sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off } void Midi::resetDevices() { - MidiCleanup(); - MidiSetup(); + MidiCleanup(); + MidiSetup(); } void Midi::USBchanged() { - instance->MidiCleanup(); - instance->MidiSetup(); + instance->MidiCleanup(); + instance->MidiSetup(); } // QStringList Midi::listMidiDevices(bool output) { - QStringList rv; + QStringList rv; #if defined Q_OS_WIN32 - if (output) { - MIDIOUTCAPS outcaps; - for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { - midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); - rv.append(outcaps.szPname); - } - } - else { - MIDIINCAPS incaps; - for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { - midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); - rv.append(incaps.szPname); - } - } + if (output) { + MIDIOUTCAPS outcaps; + for (unsigned int i = 0; i < midiOutGetNumDevs(); i++) { + midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); + rv.append(outcaps.szPname); + } + } else { + MIDIINCAPS incaps; + for (unsigned int i = 0; i < midiInGetNumDevs(); i++) { + midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); + rv.append(incaps.szPname); + } + } #endif - return rv; + return rv; } void Midi::unblockMidiDevice(QString name, bool output) { - if (output) { - for (int i = 0; i < midioutexclude.size(); i++) if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { - midioutexclude.erase(midioutexclude.begin() + i); - break; - } - } - else { - for (int i = 0; i < midiinexclude.size(); i++) if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { - midiinexclude.erase(midiinexclude.begin() + i); - break; - } - } + if (output) { + for (int i = 0; i < midioutexclude.size(); i++) { + if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { + midioutexclude.erase(midioutexclude.begin() + i); + break; + } + } + } else { + for (int i = 0; i < midiinexclude.size(); i++) { + if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { + midiinexclude.erase(midiinexclude.begin() + i); + break; + } + } + } } void Midi::blockMidiDevice(QString name, bool output) { - unblockMidiDevice(name, output); // make sure it's only in there once - if (output) { - midioutexclude.push_back(name); - } - else { - midiinexclude.push_back(name); - } + unblockMidiDevice(name, output); // make sure it's only in there once + if (output) { + midioutexclude.push_back(name); + } else { + midiinexclude.push_back(name); + } } diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 66e45f0a53..90ce9f8a37 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -24,23 +24,23 @@ class Midi : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - Midi(); + Midi(); public: - void noteReceived(int status, int note, int velocity); // relay a note to Javascript - void sendNote(int status, int note, int vel); // relay a note to MIDI outputs - static void USBchanged(); + void noteReceived(int status, int note, int velocity); // relay a note to Javascript + void sendNote(int status, int note, int vel); // relay a note to MIDI outputs + static void USBchanged(); private: - static std::vector midiinexclude; - static std::vector midioutexclude; + static std::vector midiinexclude; + static std::vector midioutexclude; private: - void MidiSetup(); - void MidiCleanup(); + void MidiSetup(); + void MidiCleanup(); signals: - void midiNote(QVariantMap eventData); + void midiNote(QVariantMap eventData); public slots: /// play a note on all connected devices diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3a846d3026..568260d6e3 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -76,7 +76,7 @@ #include -#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work? +#include "../../midi/src/Midi.h" // FIXME why won't a simpler include work? #include "MIDIEvent.h" const QString ScriptEngine::_SETTINGS_ENABLE_EXTENDED_EXCEPTIONS { diff --git a/scripts/tutorials/createMidiSphere.js b/scripts/tutorials/createMidiSphere.js index 705acac9de..a36d4bf608 100644 --- a/scripts/tutorials/createMidiSphere.js +++ b/scripts/tutorials/createMidiSphere.js @@ -16,7 +16,7 @@ var BALL_GRAVITY = { y: 0, z: 0 }; - + var BALL_DIMENSIONS = { x: 0.4, y: 0.4, @@ -34,14 +34,14 @@ var midiSphereProperties = { name: 'MIDI Sphere', shapeType: 'sphere', type: 'Sphere', - script: SCRIPT_URL, + script: SCRIPT_URL, color: BALL_COLOR, dimensions: BALL_DIMENSIONS, gravity: BALL_GRAVITY, dynamic: false, position: center, - collisionless: false, - ignoreForCollisions: true + collisionless: false, + ignoreForCollisions: true }; var midiSphere = Entities.addEntity(midiSphereProperties); diff --git a/scripts/tutorials/entity_scripts/midiSphere.js b/scripts/tutorials/entity_scripts/midiSphere.js index 5ed29a330f..980f8f834e 100644 --- a/scripts/tutorials/entity_scripts/midiSphere.js +++ b/scripts/tutorials/entity_scripts/midiSphere.js @@ -15,35 +15,35 @@ function MidiSphere() { _this = this; - this.clicked = false; - return; + this.clicked = false; + return; } - + MidiSphere.prototype = { preload: function(entityID) { this.entityID = entityID; - Midi.midiNote.connect(function(eventData) { - print("MidiSphere.noteReceived: "+JSON.stringify(eventData)); - Entities.editEntity(entityID, { color: { red: 2*eventData.note, green: 2*eventData.note, blue: 2*eventData.note} }); - }); - print("MidiSphere.preload"); + Midi.midiNote.connect(function(eventData) { + print("MidiSphere.noteReceived: "+JSON.stringify(eventData)); + Entities.editEntity(entityID, { color: { red: 2*eventData.note, green: 2*eventData.note, blue: 2*eventData.note} }); + }); + print("MidiSphere.preload"); }, unload: function(entityID) { - print("MidiSphere.unload"); + print("MidiSphere.unload"); }, - clickDownOnEntity: function(entityID, mouseEvent) { - print("MidiSphere.clickDownOnEntity"); - if (this.clicked) { - Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); - this.clicked = false; - Midi.playMidiNote(144, 64, 0); - } else { - Entities.editEntity(entityID, { color: { red: 255, green: 255, blue: 0} }); - this.clicked = true; - Midi.playMidiNote(144, 64, 100); - } - } + clickDownOnEntity: function(entityID, mouseEvent) { + print("MidiSphere.clickDownOnEntity"); + if (this.clicked) { + Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); + this.clicked = false; + Midi.playMidiNote(144, 64, 0); + } else { + Entities.editEntity(entityID, { color: { red: 255, green: 255, blue: 0} }); + this.clicked = true; + Midi.playMidiNote(144, 64, 100); + } + } }; From a29f07aef50f96ebe9278f3e2cf0eebe6d80dcdc Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 17:32:59 -0700 Subject: [PATCH 011/280] one more --- libraries/midi/src/Midi.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index a3d4db0c88..2d2d08440e 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -74,8 +74,10 @@ void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_P void Midi::sendNote(int status, int note, int vel) { - for (int i = 0; i < midihout.size(); i++) if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); + for (int i = 0; i < midihout.size(); i++) { + if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); + } } } From 6f842dc204aa1abdf5e4aa2240018c8b63650771 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Fri, 16 Jun 2017 14:30:20 -0700 Subject: [PATCH 012/280] requested changes --- libraries/midi/src/Midi.cpp | 67 +++++++++++++++++++++++++------------ libraries/midi/src/Midi.h | 3 ++ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 2d2d08440e..5b5376c6d3 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -20,6 +20,9 @@ #endif +const int MIDI_BYTE_MASK = 0x0FF; +const int MIDI_SHIFT_NOTE = 8; +const int MIDI_SHIFT_VELOCITY = 16; const int MIDI_STATUS_MASK = 0x0F0; const int MIDI_NOTE_OFF = 0x080; const int MIDI_NOTE_ON = 0x090; @@ -28,6 +31,7 @@ const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b; static Midi* instance = NULL; // communicate this to non-class callbacks +static bool thruModeEnabled = false; std::vector Midi::midiinexclude; std::vector Midi::midioutexclude; @@ -43,32 +47,45 @@ std::vector midihout; void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { - if (wMsg == MIM_OPEN) { - } else if (wMsg == MIM_CLOSE) { - for (int i = 0; i < midihin.size(); i++) { - if (midihin[i] == hMidiIn) { - midihin[i] = NULL; - instance->allNotesOff(); + switch (wMsg) { + case MIM_OPEN: + // message not used + break; + case MIM_CLOSE: + for (int i = 0; i < midihin.size(); i++) { + if (midihin[i] == hMidiIn) { + midihin[i] = NULL; + instance->allNotesOff(); + } } + break; + case MIM_DATA: { + int status = MIDI_BYTE_MASK & dwParam1; + int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE); + int vel = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY); + if (thruModeEnabled) { + instance->sendNote(status, note, vel); // relay the note on to all other midi devices + } + instance->noteReceived(status, note, vel); // notify the javascript + break; } - } else if (wMsg == MIM_DATA) { - int status = 0x0ff & dwParam1; - int note = 0x0ff & (dwParam1 >> 8); - int vel = 0x0ff & (dwParam1 >> 16); -//sendNote(status, note, vel); // NOTE: relay the note on to all other midi devices - instance->Midi::noteReceived(status, note, vel); } } void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { - if (wMsg == MOM_CLOSE) { - for (int i = 0; i < midihout.size(); i++) { - if (midihout[i] == hmo) { - midihout[i] = NULL; - instance->allNotesOff(); + switch (wMsg) { + case MOM_OPEN: + // message not used + break; + case MOM_CLOSE: + for (int i = 0; i < midihout.size(); i++) { + if (midihout[i] == hmo) { + midihout[i] = NULL; + instance->allNotesOff(); + } } - } + break; } } @@ -76,14 +93,16 @@ void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_P void Midi::sendNote(int status, int note, int vel) { for (int i = 0; i < midihout.size(); i++) { if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << 8) + (vel << 16)); + midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (vel << MIDI_SHIFT_VELOCITY)); } } } void Midi::noteReceived(int status, int note, int velocity) { if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && - ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) return; // NOTE: only sending note-on and note-off to Javascript + ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) { + return; // NOTE: only sending note-on and note-off to Javascript + } QVariantMap eventData; eventData["status"] = status; @@ -105,9 +124,10 @@ void Midi::MidiSetup() { for (int j = 0; j < midiinexclude.size(); j++) { if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) { found = true; + break; } } - if (!found) { // EXCLUDE AN INPUT BY NAME + if (!found) { // EXCLUDE AN INPUT BY NAME HMIDIIN tmphin; midiInOpen(&tmphin, i, (DWORD_PTR)MidiInProc, NULL, CALLBACK_FUNCTION); midiInStart(tmphin); @@ -124,6 +144,7 @@ void Midi::MidiSetup() { for (int j = 0; j < midioutexclude.size(); j++) { if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) { found = true; + break; } } if (!found) { // EXCLUDE AN OUTPUT BY NAME @@ -241,3 +262,7 @@ void Midi::blockMidiDevice(QString name, bool output) { } } +void Midi::thruModeEnable(bool enable) { + thruModeEnabled = enable; +} + diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 90ce9f8a37..e8ba803ad4 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -63,6 +63,9 @@ Q_INVOKABLE void blockMidiDevice(QString name, bool output); /// unblock an input/output by name Q_INVOKABLE void unblockMidiDevice(QString name, bool output); + +/// repeat all incoming notes to all outputs (default disabled) +Q_INVOKABLE void thruModeEnable(bool enable); }; #endif // hifi_Midi_h From f1485afccc80585499dd0d6b9ab4691f74f85853 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 22 Jun 2017 13:01:11 -0700 Subject: [PATCH 013/280] public script --- scripts/tutorials/createMidiSphere.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/tutorials/createMidiSphere.js b/scripts/tutorials/createMidiSphere.js index a36d4bf608..cfec8b59dc 100644 --- a/scripts/tutorials/createMidiSphere.js +++ b/scripts/tutorials/createMidiSphere.js @@ -8,7 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var SCRIPT_URL = "file:///e:/hifi/scripts/tutorials/entity_scripts/midiSphere.js"; +//var SCRIPT_URL = "file:///e:/hifi/scripts/tutorials/entity_scripts/midiSphere.js"; +var SCRIPT_URL = "http://hifi-files.s3-website-us-west-2.amazonaws.com/midiSphere.js";" var center = Vec3.sum(MyAvatar.position, Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); var BALL_GRAVITY = { From c10475186c32bef0331a686cc3d5eb0c8603c457 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Jun 2017 18:48:29 -0400 Subject: [PATCH 014/280] add support for switching cursor from arrow to reticle --- interface/src/Application.cpp | 42 +++++++++++++++++--------- interface/src/Application.h | 9 ++++-- interface/src/ui/PreferencesDialog.cpp | 6 ++++ libraries/ui/src/CursorManager.cpp | 28 +++++++++++++++-- libraries/ui/src/CursorManager.h | 9 +++++- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 540622e522..433185b40b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -612,6 +612,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; +const QString DEFAULT_CURSOR_NAME = "DEFAULT"; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runningMarkerExisted) : QApplication(argc, argv), @@ -631,6 +632,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), + _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), @@ -928,14 +930,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); -#ifdef Q_OS_MAC - auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget -#else - // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the - // window menu, which is a pain, so only hide it for the GL surface - auto cursorTarget = _glWidget; -#endif - cursorTarget->setCursor(Qt::BlankCursor); + showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get())); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); @@ -1700,9 +1695,16 @@ void Application::checkChangeCursor() { } } -void Application::showCursor(const QCursor& cursor) { +void Application::showCursor(const Cursor::Icon& cursor) { QMutexLocker locker(&_changeCursorLock); - _desiredCursor = cursor; + + auto managedCursor = Cursor::Manager::instance().getCursor(); + auto curIcon = managedCursor->getIcon(); + if (curIcon != cursor) { + managedCursor->setIcon(cursor); + curIcon = cursor; + } + _desiredCursor = cursor == Cursor::Icon::SYSTEM ? Qt::ArrowCursor : Qt::BlankCursor; _cursorNeedsChanging = true; } @@ -2121,9 +2123,11 @@ void Application::initializeUi() { _window->setMenuBar(new Menu()); auto compositorHelper = DependencyManager::get(); - connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] { + connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] { if (isHMDMode()) { - showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor); + showCursor(compositorHelper->getAllowMouseCapture() ? + Cursor::Manager::lookupIcon(_preferredCursor.get()) : + Cursor::Icon::SYSTEM); } }); @@ -2426,6 +2430,12 @@ void Application::setPreferAvatarFingerOverStylus(bool value) { _preferAvatarFingerOverStylusSetting.set(value); } +void Application::setPreferredCursor(const QString& cursorName) { + qCDebug(interfaceapp) << "setPreferredCursor" << cursorName; + _preferredCursor.set(cursorName.isEmpty() ? DEFAULT_CURSOR_NAME : cursorName); + showCursor(Cursor::Manager::lookupIcon(_preferredCursor.get())); +} + void Application::setSettingConstrainToolbarPosition(bool setting) { _constrainToolbarPosition.set(setting); DependencyManager::get()->setConstrainToolbarToCenterX(setting); @@ -2989,9 +2999,13 @@ void Application::keyPressEvent(QKeyEvent* event) { auto cursor = Cursor::Manager::instance().getCursor(); auto curIcon = cursor->getIcon(); if (curIcon == Cursor::Icon::DEFAULT) { - cursor->setIcon(Cursor::Icon::LINK); + showCursor(Cursor::Icon::RETICLE); + } else if (curIcon == Cursor::Icon::RETICLE) { + showCursor(Cursor::Icon::SYSTEM); + } else if (curIcon == Cursor::Icon::SYSTEM) { + showCursor(Cursor::Icon::LINK); } else { - cursor->setIcon(Cursor::Icon::DEFAULT); + showCursor(Cursor::Icon::DEFAULT); } } else { resetSensors(true); diff --git a/interface/src/Application.h b/interface/src/Application.h index cab42d1e1c..95042aa041 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -54,6 +54,7 @@ #include "BandwidthRecorder.h" #include "FancyCamera.h" #include "ConnectionMonitor.h" +#include "CursorManager.h" #include "gpu/Context.h" #include "Menu.h" #include "octree/OctreePacketProcessor.h" @@ -163,7 +164,7 @@ public: QSize getDeviceSize() const; bool hasFocus() const; - void showCursor(const QCursor& cursor); + void showCursor(const Cursor::Icon& cursor); bool isThrottleRendering() const; @@ -398,6 +399,9 @@ public slots: void loadDomainConnectionDialog(); void showScriptLogs(); + const QString getPreferredCursor() const { return _preferredCursor.get(); } + void setPreferredCursor(const QString& cursor); + private slots: void showDesktop(); void clearDomainOctreeDetails(); @@ -562,6 +566,7 @@ private: Setting::Handle _hmdTabletBecomesToolbarSetting; Setting::Handle _preferAvatarFingerOverStylusSetting; Setting::Handle _constrainToolbarPosition; + Setting::Handle _preferredCursor; float _scaleMirror; float _rotateMirror; @@ -636,7 +641,7 @@ private: void checkChangeCursor(); mutable QMutex _changeCursorLock { QMutex::Recursive }; - QCursor _desiredCursor{ Qt::BlankCursor }; + Qt::CursorShape _desiredCursor{ Qt::BlankCursor }; bool _cursorNeedsChanging { false }; QThread* _deadlockWatchdogThread; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 41a4ebdf68..a808e3f77e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -106,6 +106,12 @@ void setupPreferences() { auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter)); } + { + static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) }; + auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; }; + auto setter = [](bool value) { qApp->setPreferredCursor(value ? RETICLE_ICON_NAME : QString()); }; + preferences->addPreference(new CheckPreference(UI_CATEGORY, "Use reticle cursor instead of arrow", getter, setter)); + } // Snapshots static const QString SNAPSHOTS { "Snapshots" }; diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index f768b5f227..106574ff4f 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -31,12 +31,34 @@ namespace Cursor { } }; - static QMap ICONS; + QMap Manager::ICON_NAMES { + { Icon::SYSTEM, "SYSTEM", }, + { Icon::DEFAULT, "DEFAULT", }, + { Icon::LINK, "LINK", }, + { Icon::ARROW, "ARROW", }, + { Icon::RETICLE, "RETICLE", }, + }; + QMap Manager::ICONS; static uint16_t _customIconId = Icon::USER_BASE; Manager::Manager() { - ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png"; - ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/link.png"; + ICONS[Icon::SYSTEM] = PathUtils::resourcesPath() + "images/cursor-none.png"; + ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/cursor-arrow.png"; + ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/cursor-link.png"; + ICONS[Icon::ARROW] = PathUtils::resourcesPath() + "images/cursor-arrow.png"; + ICONS[Icon::RETICLE] = PathUtils::resourcesPath() + "images/cursor-reticle.png"; + } + + Icon Manager::lookupIcon(const QString& name) { + for (const auto& kv : ICON_NAMES.toStdMap()) { + if (kv.second == name) { + return static_cast(kv.first); + } + } + return Icon::DEFAULT; + } + const QString& Manager::getIconName(const Icon& icon) { + return ICON_NAMES.count(icon) ? ICON_NAMES[icon] : ICON_NAMES[Icon::DEFAULT]; } Manager& Manager::instance() { diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h index 99d5ccdc77..2a92cc7f45 100644 --- a/libraries/ui/src/CursorManager.h +++ b/libraries/ui/src/CursorManager.h @@ -18,16 +18,18 @@ namespace Cursor { }; enum Icon { + SYSTEM, DEFAULT, LINK, GRAB, + ARROW, + RETICLE, // Add new system cursors here // User cursors will have ids over this value USER_BASE = 0xFF, }; - class Instance { public: virtual Source getType() const = 0; @@ -49,6 +51,11 @@ namespace Cursor { uint16_t registerIcon(const QString& path); QList registeredIcons() const; const QString& getIconImage(uint16_t icon); + + static QMap ICONS; + static QMap ICON_NAMES; + static Icon lookupIcon(const QString& name); + static const QString& getIconName(const Icon& icon); private: float _scale{ 1.0f }; }; From 5abd254a5893c7f10b1e1e9462ad1964d2f27a0c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jun 2017 15:58:14 +1200 Subject: [PATCH 015/280] Bail in Neuron plugin update if plugin not enabled --- plugins/hifiNeuron/src/NeuronPlugin.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 93e7da028f..4e27777628 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -367,6 +367,12 @@ void NeuronPlugin::init() { auto preferences = DependencyManager::get(); static const QString NEURON_PLUGIN { "Perception Neuron" }; + { + auto getter = [this]()->bool { return _enabled; }; + auto setter = [this](bool value) { _enabled = value; saveSettings(); }; + auto preference = new CheckPreference(NEURON_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } { auto getter = [this]()->QString { return _serverAddress; }; auto setter = [this](const QString& value) { _serverAddress = value; saveSettings(); }; @@ -387,12 +393,6 @@ void NeuronPlugin::init() { preference->setStep(1); preferences->addPreference(preference); } - { - auto getter = [this]()->bool { return _enabled; }; - auto setter = [this](bool value) { _enabled = value; saveSettings(); }; - auto preference = new CheckPreference(NEURON_PLUGIN, "Enabled", getter, setter); - preferences->addPreference(preference); - } } bool NeuronPlugin::isSupported() const { @@ -455,6 +455,10 @@ void NeuronPlugin::deactivate() { } void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!_enabled) { + return; + } + std::vector joints; { // lock and copy From 361bc1ce034190a81b913a9fe078284d5a2d4a2a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jun 2017 16:00:18 +1200 Subject: [PATCH 016/280] Add "enabled" setting for SDL2 joystick; bail in update if not enabled --- .../hifi/dialogs/GeneralPreferencesDialog.qml | 2 +- .../hifi/tablet/TabletGeneralPreferences.qml | 2 +- plugins/hifiSdl2/src/SDL2Manager.cpp | 48 ++++++++++++++++++- plugins/hifiSdl2/src/SDL2Manager.h | 8 +++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 44cae95696..605e2e79db 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index fe043f6ac7..2558100d0f 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] } } diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index 5ab5758412..2f7d91846d 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -11,8 +11,10 @@ #include -#include #include +#include +#include +#include #include "SDL2Manager.h" @@ -38,10 +40,13 @@ static_assert( (int)controller::RY == (int)SDL_CONTROLLER_AXIS_RIGHTY && (int)controller::LT == (int)SDL_CONTROLLER_AXIS_TRIGGERLEFT && (int)controller::RT == (int)SDL_CONTROLLER_AXIS_TRIGGERRIGHT, - "SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h"); + "SDL2 equivalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h"); const char* SDL2Manager::NAME = "SDL2"; +const char* SDL2Manager::SDL2_ID_STRING = "SDL2"; + +const bool DEFAULT_ENABLED = false; SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); @@ -49,6 +54,20 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { } void SDL2Manager::init() { + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString SDL2_PLUGIN { "SDL2 Joystick" }; + { + auto getter = [this]()->bool { return _isEnabled; }; + auto setter = [this](bool value) { + _isEnabled = value; + saveSettings(); + }; + auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } + bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0); if (initSuccess) { @@ -110,6 +129,27 @@ void SDL2Manager::deactivate() { InputPlugin::deactivate(); } +const char* SETTINGS_ENABLED_KEY = "enabled"; + +void SDL2Manager::saveSettings() const { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + settings.setValue(QString(SETTINGS_ENABLED_KEY), _isEnabled); + } + settings.endGroup(); +} + +void SDL2Manager::loadSettings() { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + _isEnabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool(); + } + settings.endGroup(); +} bool SDL2Manager::isSupported() const { return true; @@ -122,6 +162,10 @@ void SDL2Manager::pluginFocusOutEvent() { } void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!_isEnabled) { + return; + } + if (_isInitialized) { auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { diff --git a/plugins/hifiSdl2/src/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h index 9cb4d268c0..48e779a204 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -25,6 +25,7 @@ public: // Plugin functions bool isSupported() const override; const QString getName() const override { return NAME; } + const QString getID() const override { return SDL2_ID_STRING; } QStringList getSubdeviceNames() override; @@ -39,6 +40,9 @@ public: void pluginFocusOutEvent() override; void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + virtual void saveSettings() const override; + virtual void loadSettings() override; + signals: void joystickAdded(Joystick* joystick); void joystickRemoved(Joystick* joystick); @@ -77,8 +81,10 @@ private: int buttonRelease() const { return SDL_RELEASED; } QMap _openJoysticks; - bool _isInitialized { false } ; + bool _isEnabled { false }; + bool _isInitialized { false }; static const char* NAME; + static const char* SDL2_ID_STRING; QStringList _subdeviceNames; }; From 31714675c358d3e76204d4d25a6927452aa4213f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jun 2017 16:14:57 +1200 Subject: [PATCH 017/280] Remove unused Sixense preference and associated methods --- .../qml/hifi/dialogs/GeneralPreferencesDialog.qml | 2 +- .../qml/hifi/tablet/TabletGeneralPreferences.qml | 2 +- interface/src/ui/PreferencesDialog.cpp | 11 ----------- .../controllers/src/controllers/InputDevice.cpp | 14 -------------- .../controllers/src/controllers/InputDevice.h | 4 ---- 5 files changed, 2 insertions(+), 31 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 605e2e79db..08631bc6e8 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Perception Neuron", "Kinect"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index 2558100d0f..d893bab776 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] } } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 41a4ebdf68..d9f71c8d3d 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -297,17 +297,6 @@ void setupPreferences() { preferences->addPreference(preference); } - - { - auto getter = []()->float { return controller::InputDevice::getReticleMoveSpeed(); }; - auto setter = [](float value) { controller::InputDevice::setReticleMoveSpeed(value); }; - auto preference = new SpinnerPreference("Sixense Controllers", "Reticle movement speed", getter, setter); - preference->setMin(0); - preference->setMax(100); - preference->setStep(1); - preferences->addPreference(preference); - } - { static const QString RENDER("Graphics"); auto renderConfig = qApp->getRenderEngine()->getConfiguration(); diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index dd27726a55..a907842a17 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -15,20 +15,6 @@ namespace controller { - const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; - float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; - - //Constants for getCursorPixelRangeMultiplier() - const float MIN_PIXEL_RANGE_MULT = 0.4f; - const float MAX_PIXEL_RANGE_MULT = 2.0f; - const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; - - //Returns a multiplier to be applied to the cursor range for the controllers - float InputDevice::getCursorPixelRangeMult() { - //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; - } - float InputDevice::getButton(int channel) const { if (!_buttonPressedMap.empty()) { if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 9d46930104..7315868bb4 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -73,10 +73,6 @@ public: int getDeviceID() { return _deviceID; } void setDeviceID(int deviceID) { _deviceID = deviceID; } - static float getCursorPixelRangeMult(); - static float getReticleMoveSpeed() { return _reticleMoveSpeed; } - static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; } - Input makeInput(StandardButtonChannel button) const; Input makeInput(StandardAxisChannel axis) const; Input makeInput(StandardPoseChannel pose) const; From 72d712ac767ebc04ab8a1fcb83b0f79ede6d43e7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jun 2017 16:43:06 +1200 Subject: [PATCH 018/280] Add "enabled" setting for Sixense; bail in update if note enabled --- .../hifi/dialogs/GeneralPreferencesDialog.qml | 2 +- .../hifi/tablet/TabletGeneralPreferences.qml | 2 +- plugins/hifiSixense/src/SixenseManager.cpp | 47 +++++++++++++++---- plugins/hifiSixense/src/SixenseManager.h | 7 ++- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 08631bc6e8..605e2e79db 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index d893bab776..2558100d0f 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] } } diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index 7d443bd50d..88f73786bd 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -46,19 +47,26 @@ static const unsigned int BUTTON_TRIGGER = 1U << 8; const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; + +bool SixenseManager::_isEnabled = false; bool SixenseManager::_sixenseLoaded = false; +#define BAIL_IF_NOT_ENABLED \ + if (!_isEnabled) { \ + return; \ + } + #define BAIL_IF_NOT_LOADED \ if (!_sixenseLoaded) { \ return; \ } - - const char* SixenseManager::NAME { "Sixense" }; -const char* SixenseManager::HYDRA_ID_STRING { "Razer Hydra" }; +const char* SixenseManager::SIXENSE_ID_STRING { "Sixense" }; -const char* MENU_PARENT { "Developer" }; +const bool DEFAULT_ENABLED = false; + +const char* MENU_PARENT{ "Developer" }; const char* MENU_NAME { "Sixense" }; const char* MENU_PATH { "Developer" ">" "Sixense" }; const char* TOGGLE_SMOOTH { "Smooth Sixense Movement" }; @@ -73,6 +81,22 @@ bool SixenseManager::isSupported() const { #endif } +void SixenseManager::init() { + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString SIXENSE_PLUGIN { "Sixense Controllers" }; + { + auto getter = [this]()->bool { return _isEnabled; }; + auto setter = [this](bool value) { + _isEnabled = value; + saveSettings(); + }; + auto preference = new CheckPreference(SIXENSE_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } +} + bool SixenseManager::activate() { InputPlugin::activate(); @@ -133,6 +157,7 @@ void SixenseManager::setSixenseFilter(bool filter) { } void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + BAIL_IF_NOT_ENABLED BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE @@ -553,14 +578,19 @@ QString SixenseManager::InputDevice::getDefaultMappingConfig() const { return MAPPING_JSON; } +const char* SETTINGS_ENABLED_KEY = "enabled"; +const char* SETTINGS_AVATAR_POSITION_KEY = "avatarPosition"; +const char* SETTINGS_AVATAR_ROTATION_KEY = "avatarPosition"; + // virtual void SixenseManager::saveSettings() const { Settings settings; QString idString = getID(); settings.beginGroup(idString); { - settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); - settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.setValue(QString(SETTINGS_ENABLED_KEY), _isEnabled); + settings.setVec3Value(QString(SETTINGS_AVATAR_POSITION_KEY), _inputDevice->_avatarPosition); + settings.setQuatValue(QString(SETTINGS_AVATAR_ROTATION_KEY), _inputDevice->_avatarRotation); } settings.endGroup(); } @@ -570,8 +600,9 @@ void SixenseManager::loadSettings() { QString idString = getID(); settings.beginGroup(idString); { - settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); - settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); + _isEnabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool(); + settings.getVec3ValueIfValid(QString(SETTINGS_AVATAR_POSITION_KEY), _inputDevice->_avatarPosition); + settings.getQuatValueIfValid(QString(SETTINGS_AVATAR_ROTATION_KEY), _inputDevice->_avatarRotation); } settings.endGroup(); } diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 5b2c140868..e9d571ad8c 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -29,12 +29,14 @@ public: // Plugin functions virtual bool isSupported() const override; virtual const QString getName() const override { return NAME; } - virtual const QString getID() const override { return HYDRA_ID_STRING; } + virtual const QString getID() const override { return SIXENSE_ID_STRING; } // Sixense always seems to initialize even if the hydras are not present. Is there // a way we can properly detect whether the hydras are present? // bool isHandController() const override { return true; } + virtual void init() override; + virtual bool activate() override; virtual void deactivate() override; @@ -93,8 +95,9 @@ private: std::shared_ptr _inputDevice { std::make_shared() }; static const char* NAME; - static const char* HYDRA_ID_STRING; + static const char* SIXENSE_ID_STRING; + static bool _isEnabled; static bool _sixenseLoaded; }; From 88ed69a4a12ae021edc33b7dde633f8bbbd2ae43 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 29 Jun 2017 08:58:27 +1200 Subject: [PATCH 019/280] Code review --- libraries/controllers/src/controllers/InputDevice.h | 3 --- plugins/hifiSixense/src/SixenseManager.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 7315868bb4..ff665d912d 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -95,9 +95,6 @@ protected: ButtonPressedMap _buttonPressedMap; AxisStateMap _axisStateMap; PoseStateMap _poseStateMap; - -private: - static float _reticleMoveSpeed; }; } diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index 88f73786bd..c2c1cb2ab2 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -54,7 +54,7 @@ bool SixenseManager::_sixenseLoaded = false; #define BAIL_IF_NOT_ENABLED \ if (!_isEnabled) { \ return; \ - } + } #define BAIL_IF_NOT_LOADED \ if (!_sixenseLoaded) { \ From be0e1a75a4cf84f82a096379a4837546d200b90e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 29 Jun 2017 10:03:58 +1200 Subject: [PATCH 020/280] User-friendly SDL2 controller name --- .../resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml | 2 +- .../resources/qml/hifi/tablet/TabletGeneralPreferences.qml | 2 +- plugins/hifiSdl2/src/SDL2Manager.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 605e2e79db..33a5e62b1f 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index 2558100d0f..005f09afe6 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "SDL2 Joystick", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] } } diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index 2f7d91846d..021cb4dfec 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -57,7 +57,7 @@ void SDL2Manager::init() { loadSettings(); auto preferences = DependencyManager::get(); - static const QString SDL2_PLUGIN { "SDL2 Joystick" }; + static const QString SDL2_PLUGIN { "Game Controller" }; { auto getter = [this]()->bool { return _isEnabled; }; auto setter = [this](bool value) { From 2b111358e1cb445d14128c00c7e1712986c5b0b2 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 29 Jun 2017 16:49:00 -0700 Subject: [PATCH 021/280] extra quote --- scripts/tutorials/createMidiSphere.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/createMidiSphere.js b/scripts/tutorials/createMidiSphere.js index cfec8b59dc..56eae98585 100644 --- a/scripts/tutorials/createMidiSphere.js +++ b/scripts/tutorials/createMidiSphere.js @@ -9,7 +9,7 @@ // //var SCRIPT_URL = "file:///e:/hifi/scripts/tutorials/entity_scripts/midiSphere.js"; -var SCRIPT_URL = "http://hifi-files.s3-website-us-west-2.amazonaws.com/midiSphere.js";" +var SCRIPT_URL = "http://hifi-files.s3-website-us-west-2.amazonaws.com/midiSphere.js"; var center = Vec3.sum(MyAvatar.position, Vec3.multiply(0.5, Quat.getForward(Camera.getOrientation()))); var BALL_GRAVITY = { From be68d99bedba857159262cc2e27c47a7f9e1796f Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Thu, 6 Jul 2017 23:17:23 -0700 Subject: [PATCH 022/280] added ability to drag ParticleEffects and Light Entities around via Overlay Icon. --- scripts/system/edit.js | 10 ++++++++-- .../libraries/entityIconOverlayManager.js | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index a83d2159bb..11ab4771b9 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -648,7 +648,10 @@ var toolBar = (function () { // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); } - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + var activeLightParticle = (isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + entityIconOverlayManager.setVisible(activeLightParticle); + entityIconOverlayManager.setIconsSelectable(activeLightParticle); + Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; @@ -1464,7 +1467,10 @@ function handeMenuEvent(menuItem) { } else if (menuItem === "Select All Entities Touching Box") { selectAllEtitiesInCurrentSelectionBox(true); } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { - entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + var activeLightParticle = (isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); + entityIconOverlayManager.setVisible(activeLightParticle); + entityIconOverlayManager.setIconsSelectable(activeLightParticle); + } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index f557a05f60..65a98df90d 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -2,6 +2,7 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { var visible = false; + var iconsSelectable = false; // List of all created overlays var allOverlays = []; @@ -69,6 +70,18 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { } }; + + this.setIconsSelectable = function(isIconsSelectable) { + if (iconsSelectable !== isIconsSelectable) { + iconsSelectable = isIconsSelectable; + for (var id in entityOverlays) { + Overlays.editOverlay(entityOverlays[id], { + ignoreRayIntersection: iconsSelectable + }); + } + } + }; + // Allocate or get an unused overlay function getOverlay() { var overlay; @@ -114,6 +127,10 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { overlayProperties[key] = customProperties[key]; } } + + if(properties.type === 'ParticleEffect' || properties.type === 'Light'){ + overlayProperties.ignoreRayIntersection = true; + } Overlays.editOverlay(overlay, overlayProperties); } } From d9706452ac4c781a87cc1cf261c1fa27ef2b0924 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Thu, 6 Jul 2017 23:32:22 -0700 Subject: [PATCH 023/280] added ability to drag ParticleEffects and Light Entities around via Overlay Icon. --- scripts/system/libraries/entityIconOverlayManager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index 65a98df90d..1953b02d94 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -127,7 +127,6 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { overlayProperties[key] = customProperties[key]; } } - if(properties.type === 'ParticleEffect' || properties.type === 'Light'){ overlayProperties.ignoreRayIntersection = true; } From 53f581c3d418a246809e019668361dc7a4601bc9 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Sat, 8 Jul 2017 02:52:27 -0700 Subject: [PATCH 024/280] Fixed Icons not being selectable while not highlighted. --- scripts/system/edit.js | 14 ++++++------- .../libraries/entityIconOverlayManager.js | 21 ++++++++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 11ab4771b9..e9afc3a167 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -644,14 +644,12 @@ var toolBar = (function () { selectionDisplay.triggerMapping.enable(); print("starting tablet in landscape mode") tablet.landscape = true; + entityIconOverlayManager.setIconsSelectable(null,false); // Not sure what the following was meant to accomplish, but it currently causes // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); } - var activeLightParticle = (isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); - entityIconOverlayManager.setVisible(activeLightParticle); - entityIconOverlayManager.setIconsSelectable(activeLightParticle); - + entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; @@ -944,6 +942,9 @@ function mouseClickEvent(event) { } else { selectionManager.addEntity(foundEntity, true); } + + entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); + if (wantDebug) { print("Model selected: " + foundEntity); } @@ -1467,10 +1468,7 @@ function handeMenuEvent(menuItem) { } else if (menuItem === "Select All Entities Touching Box") { selectAllEtitiesInCurrentSelectionBox(true); } else if (menuItem === MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE) { - var activeLightParticle = (isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); - entityIconOverlayManager.setVisible(activeLightParticle); - entityIconOverlayManager.setIconsSelectable(activeLightParticle); - + entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } diff --git a/scripts/system/libraries/entityIconOverlayManager.js b/scripts/system/libraries/entityIconOverlayManager.js index 1953b02d94..a374783b1f 100644 --- a/scripts/system/libraries/entityIconOverlayManager.js +++ b/scripts/system/libraries/entityIconOverlayManager.js @@ -1,8 +1,8 @@ /* globals EntityIconOverlayManager:true */ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { + var visible = false; - var iconsSelectable = false; // List of all created overlays var allOverlays = []; @@ -71,14 +71,25 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) { }; - this.setIconsSelectable = function(isIconsSelectable) { - if (iconsSelectable !== isIconsSelectable) { - iconsSelectable = isIconsSelectable; + this.setIconsSelectable = function(arrayOfSelectedEntityIDs, isIconsSelectable) { + if (arrayOfSelectedEntityIDs === null) { for (var id in entityOverlays) { Overlays.editOverlay(entityOverlays[id], { - ignoreRayIntersection: iconsSelectable + ignoreRayIntersection: isIconsSelectable }); } + } else { + for (var id in entityOverlays) { + if (arrayOfSelectedEntityIDs.indexOf(id) !== -1) { // in the entityOverlays array and selectable + Overlays.editOverlay(entityOverlays[id], { + ignoreRayIntersection: isIconsSelectable + }); + } else { + Overlays.editOverlay(entityOverlays[id], { + ignoreRayIntersection: !isIconsSelectable + }); + } + } } }; From 1d333a24c17db824386f6b438294809eeab920f2 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Mon, 10 Jul 2017 11:51:42 -0400 Subject: [PATCH 025/280] Add point and thumb up gestures to the vive. When the thumb is off the touchpad, trigger the 'thumb up' animation. When on the thumbpad an 75% forward, trigger the 'index point animation'. WL 21362 --- interface/resources/controllers/vive.json | 6 +++++- plugins/openvr/src/ViveControllerManager.cpp | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index a0e9bd30d4..7795667125 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -64,6 +64,10 @@ { "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] }, { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] }, + { "from": "Vive.LeftThumbUp", "to": "Standard.LeftThumbUp" }, + { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" }, + { "from": "Vive.LeftIndexPoint", "to": "Standard.LeftIndexPoint" }, + { "from": "Vive.RightIndexPoint", "to": "Standard.RightIndexPoint" } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index d914cdcfad..24d49289dd 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -754,6 +754,12 @@ void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32 } _axisStateMap[isLeftHand ? LX : RX] = stick.x; _axisStateMap[isLeftHand ? LY : RY] = stick.y; + + if (stick.y > 0.75f) + { + // Simulate pointing gesture from the oculus controller + _buttonPressedMap.insert(isLeftHand ? LEFT_INDEX_POINT : RIGHT_INDEX_POINT); + } } else if (axis == vr::k_EButton_SteamVR_Trigger) { _axisStateMap[isLeftHand ? LT : RT] = x; // The click feeling on the Vive controller trigger represents a value of *precisely* 1.0, @@ -808,9 +814,11 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint } } - if (touched) { - if (button == vr::k_EButton_SteamVR_Touchpad) { - _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); + if (button == vr::k_EButton_SteamVR_Touchpad) { + if (touched) { + _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); + } else { + _buttonPressedMap.insert(isLeftHand ? LEFT_THUMB_UP : RIGHT_THUMB_UP); } } } @@ -1102,6 +1110,11 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI // app button above trackpad. Input::NamedPair(Input(_deviceID, LEFT_APP_MENU, ChannelType::BUTTON), "LeftApplicationMenu"), Input::NamedPair(Input(_deviceID, RIGHT_APP_MENU, ChannelType::BUTTON), "RightApplicationMenu"), + + makePair(LEFT_THUMB_UP, "LeftThumbUp"), + makePair(RIGHT_THUMB_UP, "RightThumbUp"), + makePair(LEFT_INDEX_POINT, "LeftIndexPoint"), + makePair(RIGHT_INDEX_POINT, "RightIndexPoint"), }; return availableInputs; From f5ccf508c93012af95190d5d543f9ccf71c2d677 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Mon, 10 Jul 2017 14:11:04 -0400 Subject: [PATCH 026/280] Fixing braces dropped in merge conflict edit. --- interface/resources/controllers/vive.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index fbe65950c4..062a8787ce 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -59,8 +59,8 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, - { "from": "Vive.RightArm", "to" : "Standard.RightArm", - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", + { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, { "from": "Vive.LeftThumbUp", "to": "Standard.LeftThumbUp" }, { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" }, { "from": "Vive.LeftIndexPoint", "to": "Standard.LeftIndexPoint" }, From 9a2084425da9b0022a4eb78a8b01046b7dfaf54d Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Mon, 10 Jul 2017 14:18:11 -0400 Subject: [PATCH 027/280] Stylistic updates. --- plugins/openvr/src/ViveControllerManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 871b1a837f..d29f396342 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -62,6 +62,8 @@ static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; +static const float POINTING_THRESHOLD = 0.75f; + const char* ViveControllerManager::NAME { "OpenVR" }; const std::map TRACKING_RESULT_TO_STRING = { @@ -810,8 +812,7 @@ void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32 _axisStateMap[isLeftHand ? LX : RX] = stick.x; _axisStateMap[isLeftHand ? LY : RY] = stick.y; - if (stick.y > 0.75f) - { + if (stick.y > POINTING_THRESHOLD) { // Simulate pointing gesture from the oculus controller _buttonPressedMap.insert(isLeftHand ? LEFT_INDEX_POINT : RIGHT_INDEX_POINT); } From 2cd10fd459560a6ce52f2935b1c7a36356566ac2 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Wed, 12 Jul 2017 11:08:50 -0400 Subject: [PATCH 028/280] Move 'index point' logic into json config file. --- interface/resources/controllers/vive.json | 13 ++++++++++--- plugins/openvr/src/ViveControllerManager.cpp | 9 --------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 062a8787ce..349554673d 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,6 +1,15 @@ { "name": "Vive to Standard", "channels": [ + { "from": "Vive.LY", "to": "Standard.LeftIndexPoint", + "peek": true, + "filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ] + }, + { "from": "Vive.RY", "to": "Standard.RightIndexPoint", + "peek": true, + "filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ] + }, + { "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" }, { "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" }, { @@ -62,8 +71,6 @@ { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, { "from": "Vive.LeftThumbUp", "to": "Standard.LeftThumbUp" }, - { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" }, - { "from": "Vive.LeftIndexPoint", "to": "Standard.LeftIndexPoint" }, - { "from": "Vive.RightIndexPoint", "to": "Standard.RightIndexPoint" } + { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index d29f396342..895aff4f03 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -62,8 +62,6 @@ static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; -static const float POINTING_THRESHOLD = 0.75f; - const char* ViveControllerManager::NAME { "OpenVR" }; const std::map TRACKING_RESULT_TO_STRING = { @@ -811,11 +809,6 @@ void ViveControllerManager::InputDevice::handleAxisEvent(float deltaTime, uint32 } _axisStateMap[isLeftHand ? LX : RX] = stick.x; _axisStateMap[isLeftHand ? LY : RY] = stick.y; - - if (stick.y > POINTING_THRESHOLD) { - // Simulate pointing gesture from the oculus controller - _buttonPressedMap.insert(isLeftHand ? LEFT_INDEX_POINT : RIGHT_INDEX_POINT); - } } else if (axis == vr::k_EButton_SteamVR_Trigger) { _axisStateMap[isLeftHand ? LT : RT] = x; // The click feeling on the Vive controller trigger represents a value of *precisely* 1.0, @@ -1182,8 +1175,6 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI makePair(LEFT_THUMB_UP, "LeftThumbUp"), makePair(RIGHT_THUMB_UP, "RightThumbUp"), - makePair(LEFT_INDEX_POINT, "LeftIndexPoint"), - makePair(RIGHT_INDEX_POINT, "RightIndexPoint"), }; return availableInputs; From 22d8adcb9951ab22c3b10693b692c6edb4ee7c23 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 10:39:48 -0700 Subject: [PATCH 029/280] Better shoulder/arm puck calibration. --- .../resources/avatar/avatar-animation.json | 36 +++++----- plugins/openvr/src/ViveControllerManager.cpp | 66 ++++++++++++++----- plugins/openvr/src/ViveControllerManager.h | 3 +- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index a493d8e9af..a179f452c1 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -61,6 +61,24 @@ "weight": 1.0, "flexCoefficients": [1] }, + { + "jointName": "LeftArm", + "positionVar": "leftArmPosition", + "rotationVar": "leftArmRotation", + "typeVar": "leftArmType", + "weightVar": "leftArmWeight", + "weight": 0.75, + "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] + }, + { + "jointName": "RightArm", + "positionVar": "rightArmPosition", + "rotationVar": "rightArmRotation", + "typeVar": "rightArmType", + "weightVar": "rightArmWeight", + "weight": 0.75, + "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] + }, { "jointName": "RightHand", "positionVar": "rightHandPosition", @@ -126,24 +144,6 @@ "weightVar": "headWeight", "weight": 4.0, "flexCoefficients": [1, 0.5, 0.25, 0.2, 0.1] - }, - { - "jointName": "LeftArm", - "positionVar": "leftArmPosition", - "rotationVar": "leftArmRotation", - "typeVar": "leftArmType", - "weightVar": "leftArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, - { - "jointName": "RightArm", - "positionVar": "rightArmPosition", - "rotationVar": "rightArmRotation", - "typeVar": "rightArmType", - "weightVar": "rightArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] } ] }, diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 07b3b2f73d..216d50a4af 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -624,7 +624,8 @@ bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToRefer void ViveControllerManager::InputDevice::uncalibrate() { _config = Config::None; - _pucksOffset.clear(); + _pucksPostOffset.clear(); + _pucksPreOffset.clear(); _jointToPuckMap.clear(); _calibrated = false; _overrideHead = false; @@ -654,10 +655,17 @@ controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joi if (puck != _jointToPuckMap.end()) { uint32_t puckIndex = puck->second; auto puckPose = _poseStateMap.find(puckIndex); - auto puckOffset = _pucksOffset.find(puckIndex); + auto puckPostOffset = _pucksPostOffset.find(puckIndex); + auto puckPreOffset = _pucksPreOffset.find(puckIndex); - if ((puckPose != _poseStateMap.end()) && (puckOffset != _pucksOffset.end())) { - return puckPose->second.postTransform(puckOffset->second); + if (puckPose != _poseStateMap.end()) { + if (puckPreOffset != _pucksPreOffset.end() && puckPostOffset != _pucksPostOffset.end()) { + return puckPose->second.postTransform(puckPostOffset->second).transform(puckPreOffset->second); + } else if (puckPostOffset != _pucksPostOffset.end()) { + return puckPose->second.postTransform(puckPostOffset->second); + } else if (puckPreOffset != _pucksPreOffset.end()) { + return puckPose->second.transform(puckPreOffset->second); + } } } return controller::Pose(); @@ -970,7 +978,7 @@ void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToR glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset); _jointToPuckMap[controller::LEFT_HAND] = handPair.first; - _pucksOffset[handPair.first] = offsetMat; + _pucksPostOffset[handPair.first] = offsetMat; } void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { @@ -1001,7 +1009,7 @@ void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultTo glm::mat4 offsetMat = createMatFromQuatAndPos(rotationOffset, translationOffset); _jointToPuckMap[controller::RIGHT_HAND] = handPair.first; - _pucksOffset[handPair.first] = offsetMat; + _pucksPostOffset[handPair.first] = offsetMat; } @@ -1037,21 +1045,21 @@ void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToRefer if (isLeftFoot) { _jointToPuckMap[controller::LEFT_FOOT] = footPair.first; - _pucksOffset[footPair.first] = finalOffset; + _pucksPostOffset[footPair.first] = finalOffset; } else { _jointToPuckMap[controller::RIGHT_FOOT] = footPair.first; - _pucksOffset[footPair.first] = finalOffset; + _pucksPostOffset[footPair.first] = finalOffset; } } void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; - _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); + _pucksPostOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); } void ViveControllerManager::InputDevice::calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first; - _pucksOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); + _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, @@ -1061,16 +1069,44 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo const controller::Pose& firstShoulderPose = firstShoulder.second; const controller::Pose& secondShoulderPose = secondShoulder.second; + glm::mat4 refLeftArm = defaultToReferenceMat * inputCalibration.defaultLeftArm; + glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; + glm::mat4 userRefLeftArm = refLeftArm; + glm::mat4 userRefRightArm = refRightArm; + const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06; + if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; - _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftArm, firstShoulder.second); _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; - _pucksOffset[secondShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, secondShoulder.second); + + // move the userRefArm to the same height as the puck. + userRefLeftArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + userRefRightArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + + // compute the post offset from the userRefArm + _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); + _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); + + // compute the pre offset from the diff between userRefArm and refArm transforms. + // as an optimization we don't do a full inverse, but subtract the translations. + _pucksPreOffset[firstShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefLeftArm) - extractTranslation(refLeftArm)); + _pucksPreOffset[secondShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefRightArm) - extractTranslation(refRightArm)); } else { _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; - _pucksOffset[secondShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftArm, secondShoulder.second); _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; - _pucksOffset[firstShoulder.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightArm, firstShoulder.second); + + // move the userRefArm to the same height as the puck + userRefLeftArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + userRefRightArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; + + // compute the post offset from the userRefArm + _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); + _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); + + // compute the pre offset from the diff between userRefArm and refArm transforms. + // as an optimization we don't do a full inverse, but subtract the translations. + _pucksPreOffset[secondShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefLeftArm) - extractTranslation(refLeftArm)); + _pucksPreOffset[firstShoulder.first] = createMatFromQuatAndPos(glm::quat(), extractTranslation(userRefRightArm) - extractTranslation(refRightArm)); } } @@ -1078,7 +1114,7 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer size_t headIndex = _validTrackedObjects.size() - 1; const PuckPosePair& head = _validTrackedObjects[headIndex]; _jointToPuckMap[controller::HEAD] = head.first; - _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, head.second); + _pucksPostOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, head.second); } QString ViveControllerManager::InputDevice::configToString(Config config) { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index a9bcc7e4e2..d82b895579 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -161,7 +161,8 @@ private: FilteredStick _filteredRightStick; std::vector _validTrackedObjects; - std::map _pucksOffset; + std::map _pucksPostOffset; + std::map _pucksPreOffset; std::map _jointToPuckMap; std::map _configStringMap; PoseData _lastSimPoseData; From fe711ccbe9044fe6b3aaf4e026f472a15feab949 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 15:01:14 -0700 Subject: [PATCH 030/280] Refactored how controller poses are stored in MyAvatar. --- interface/src/Application.cpp | 105 +++++----- interface/src/avatar/AvatarActionHold.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 228 ++++++---------------- interface/src/avatar/MyAvatar.h | 60 +----- interface/src/avatar/MyHead.cpp | 2 +- interface/src/avatar/MySkeletonModel.cpp | 148 +++++++++----- interface/src/avatar/MySkeletonModel.h | 2 +- 7 files changed, 228 insertions(+), 321 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ddd1870723..9497456677 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4335,10 +4335,9 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - auto headPose = myAvatar->getHeadControllerPoseInSensorFrame(); + auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD); if (headPose.isValid()) { - glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix(); - lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE)); + lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE)); } else { lookAtSpot = myAvatar->getHead()->getEyePosition() + (myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); @@ -4750,52 +4749,64 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } - controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND); - controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); - auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); - auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); - auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; - myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix)); + static std::vector avatarControllerActions = { + controller::Action::LEFT_HAND, + controller::Action::RIGHT_HAND, + controller::Action::LEFT_FOOT, + controller::Action::RIGHT_FOOT, + controller::Action::HIPS, + controller::Action::SPINE2, + controller::Action::HEAD, + controller::Action::LEFT_HAND_THUMB1, + controller::Action::LEFT_HAND_THUMB2, + controller::Action::LEFT_HAND_THUMB3, + controller::Action::LEFT_HAND_THUMB4, + controller::Action::LEFT_HAND_INDEX1, + controller::Action::LEFT_HAND_INDEX2, + controller::Action::LEFT_HAND_INDEX3, + controller::Action::LEFT_HAND_INDEX4, + controller::Action::LEFT_HAND_MIDDLE1, + controller::Action::LEFT_HAND_MIDDLE2, + controller::Action::LEFT_HAND_MIDDLE3, + controller::Action::LEFT_HAND_MIDDLE4, + controller::Action::LEFT_HAND_RING1, + controller::Action::LEFT_HAND_RING2, + controller::Action::LEFT_HAND_RING3, + controller::Action::LEFT_HAND_RING4, + controller::Action::LEFT_HAND_PINKY1, + controller::Action::LEFT_HAND_PINKY2, + controller::Action::LEFT_HAND_PINKY3, + controller::Action::LEFT_HAND_PINKY4, + controller::Action::RIGHT_HAND_THUMB1, + controller::Action::RIGHT_HAND_THUMB2, + controller::Action::RIGHT_HAND_THUMB3, + controller::Action::RIGHT_HAND_THUMB4, + controller::Action::RIGHT_HAND_INDEX1, + controller::Action::RIGHT_HAND_INDEX2, + controller::Action::RIGHT_HAND_INDEX3, + controller::Action::RIGHT_HAND_INDEX4, + controller::Action::RIGHT_HAND_MIDDLE1, + controller::Action::RIGHT_HAND_MIDDLE2, + controller::Action::RIGHT_HAND_MIDDLE3, + controller::Action::RIGHT_HAND_MIDDLE4, + controller::Action::RIGHT_HAND_RING1, + controller::Action::RIGHT_HAND_RING2, + controller::Action::RIGHT_HAND_RING3, + controller::Action::RIGHT_HAND_RING4, + controller::Action::RIGHT_HAND_PINKY1, + controller::Action::RIGHT_HAND_PINKY2, + controller::Action::RIGHT_HAND_PINKY3, + controller::Action::RIGHT_HAND_PINKY4 + }; - // If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if - // fingers are not being controlled, finger joints are not updated in MySkeletonModel. - // Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint. - MyAvatar::FingerPosesMap leftHandFingerPoses; - if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0 - || userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) { - for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) { - leftHandFingerPoses[i] = { - userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), - userInputMapper->getActionName((controller::Action)i) - }; - } + glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); + glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); + glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; + + for (auto& action : avatarControllerActions) { + controller::Pose pose = userInputMapper->getPoseState(action); + myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix)); } - MyAvatar::FingerPosesMap rightHandFingerPoses; - if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0 - || userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) { - for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) { - rightHandFingerPoses[i] = { - userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), - userInputMapper->getActionName((controller::Action)i) - }; - } - } - myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses); - - controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT); - controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT); - myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix)); - - controller::Pose hipsPose = userInputMapper->getPoseState(controller::Action::HIPS); - controller::Pose spine2Pose = userInputMapper->getPoseState(controller::Action::SPINE2); - myAvatar->setSpineControllerPosesInSensorFrame(hipsPose.transform(avatarToSensorMatrix), spine2Pose.transform(avatarToSensorMatrix)); - - controller::Pose headPose = userInputMapper->getPoseState(controller::Action::HEAD); - myAvatar->setHeadControllerPoseInSensorFrame(headPose.transform(avatarToSensorMatrix)); - - controller::Pose leftArmPose = userInputMapper->getPoseState(controller::Action::LEFT_ARM); - controller::Pose rightArmPose = userInputMapper->getPoseState(controller::Action::RIGHT_ARM); - myAvatar->setArmControllerPosesInSensorFrame(leftArmPose.transform(avatarToSensorMatrix), rightArmPose.transform(avatarToSensorMatrix)); updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index c1d2f903f3..fe5355ff2e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -134,9 +134,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: // fetch the hand controller pose controller::Pose pose; if (isRightHand) { - pose = myAvatar->getRightHandControllerPoseInWorldFrame(); + pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND); } else { - pose = myAvatar->getLeftHandControllerPoseInWorldFrame(); + pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::LEFT_HAND); } if (pose.isValid()) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f6fb87dad9..3b480ac9bf 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -422,7 +422,7 @@ void MyAvatar::update(float deltaTime) { } #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getHeadControllerPoseInAvatarFrame() * + glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getControllerPoseInAvatarFrame(controller::Pose::HEAD) * glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f)); p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() + @@ -657,7 +657,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorPosition = newHmdSensorPosition; _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); - auto headPose = _headControllerPoseInSensorFrameCache.get(); + auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { _headControllerFacing = getFacingDir2D(headPose.rotation); } else { @@ -753,37 +753,37 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } glm::vec3 MyAvatar::getLeftHandPosition() const { - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandPosition() const { - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getLeftHandTipPosition() const { const float TIP_LENGTH = 0.3f; - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandTipPosition() const { const float TIP_LENGTH = 0.3f; - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f); } controller::Pose MyAvatar::getLeftHandPose() const { - return getLeftHandControllerPoseInAvatarFrame(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); } controller::Pose MyAvatar::getRightHandPose() const { - return getRightHandControllerPoseInAvatarFrame(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); } controller::Pose MyAvatar::getLeftHandTipPose() const { - auto pose = getLeftHandControllerPoseInAvatarFrame(); + auto pose = getLeftHandPose(); glm::vec3 tipTrans = getLeftHandTipPosition(); pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans); pose.translation = tipTrans; @@ -791,7 +791,7 @@ controller::Pose MyAvatar::getLeftHandTipPose() const { } controller::Pose MyAvatar::getRightHandTipPose() const { - auto pose = getRightHandControllerPoseInAvatarFrame(); + auto pose = getRightHandPose(); glm::vec3 tipTrans = getRightHandTipPosition(); pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans); pose.translation = tipTrans; @@ -1425,159 +1425,43 @@ void MyAvatar::rebuildCollisionShape() { _characterController.setLocalBoundingBox(corner, diagonal); } - -void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { - _leftHandControllerPoseInSensorFrameCache.set(left); - _rightHandControllerPoseInSensorFrameCache.set(right); +void MyAvatar::setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose) { + std::lock_guard guard(_controllerPoseMapMutex); + auto iter = _controllerPoseMap.find(action); + if (iter != _controllerPoseMap.end()) { + iter->second = pose; + } else { + _controllerPoseMap.insert({ action, pose }); + } } -controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const { - return _leftHandControllerPoseInSensorFrameCache.get(); +controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action action) const { + std::lock_guard guard(_controllerPoseMapMutex); + auto iter = _controllerPoseMap.find(action); + if (iter != _controllerPoseMap.end()) { + return iter->second; + } else { + return controller::Pose(); // invalid pose + } } -controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const { - return _rightHandControllerPoseInSensorFrameCache.get(); +controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { + auto pose = getControllerPoseInSensorFrame(action); + if (pose.valid) { + return pose.transform(getSensorToWorldMatrix()); + } else { + return controller::Pose(); // invalid pose + } } -controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const { - return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const { - return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) { - _leftHandFingerPosesInSensorFramceCache.set(left); - _rightHandFingerPosesInSensorFramceCache.set(right); -} - -MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const { - return _leftHandFingerPosesInSensorFramceCache.get(); -} - -MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const { - return _rightHandFingerPosesInSensorFramceCache.get(); -} - -void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { - _leftFootControllerPoseInSensorFrameCache.set(left); - _rightFootControllerPoseInSensorFrameCache.set(right); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const { - return _leftFootControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInSensorFrame() const { - return _rightFootControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInWorldFrame() const { - return _leftFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInWorldFrame() const { - return _rightFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftFootControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftFootControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) { - _hipsControllerPoseInSensorFrameCache.set(hips); - _spine2ControllerPoseInSensorFrameCache.set(spine2); -} - -controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const { - return _hipsControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInSensorFrame() const { - return _spine2ControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getHipsControllerPoseInWorldFrame() const { - return _hipsControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInWorldFrame() const { - return _spine2ControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getHipsControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getHipsControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) { - _headControllerPoseInSensorFrameCache.set(head); -} - -controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const { - return _headControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getHeadControllerPoseInWorldFrame() const { - return _headControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const { - glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix); -} - -void MyAvatar::setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { - _leftArmControllerPoseInSensorFrameCache.set(left); - _rightArmControllerPoseInSensorFrameCache.set(right); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInSensorFrame() const { - return _leftArmControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInSensorFrame() const { - return _rightArmControllerPoseInSensorFrameCache.get(); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInWorldFrame() const { - return getLeftArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInWorldFrame() const { - return getRightArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix()); -} - -controller::Pose MyAvatar::getLeftArmControllerPoseInAvatarFrame() const { - glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getLeftArmControllerPoseInWorldFrame().transform(worldToAvatarMat); -} - -controller::Pose MyAvatar::getRightArmControllerPoseInAvatarFrame() const { - glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); - return getRightArmControllerPoseInWorldFrame().transform(worldToAvatarMat); +controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { + auto pose = getControllerPoseInWorldFrame(action); + if (pose.valid) { + glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition())); + return pose.transform(invAvatarMatrix); + } else { + return controller::Pose(); // invalid pose + } } void MyAvatar::updateMotors() { @@ -1635,7 +1519,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setParentVelocity(parentVelocity); _characterController.setPositionAndOrientation(getPosition(), getOrientation()); - auto headPose = getHeadControllerPoseInAvatarFrame(); + auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput()); } else { @@ -1869,8 +1753,8 @@ void MyAvatar::postUpdate(float deltaTime) { } if (_enableDebugDrawHandControllers) { - auto leftHandPose = getLeftHandControllerPoseInWorldFrame(); - auto rightHandPose = getRightHandControllerPoseInWorldFrame(); + auto leftHandPose = getControllerPoseInWorldFrame(controller::Action::LEFT_HAND); + auto rightHandPose = getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND); if (leftHandPose.isValid()) { DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1)); @@ -2023,7 +1907,7 @@ void MyAvatar::updateOrientation(float deltaTime) { getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); - auto headPose = getHeadControllerPoseInAvatarFrame(); + auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { glm::quat localOrientation = headPose.rotation * Quaternions::Y_180; // these angles will be in radians @@ -2641,10 +2525,10 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const { glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::vec3 headPosition; glm::quat headOrientation; - auto headPose = getHeadControllerPoseInSensorFrame(); + auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD); if (headPose.isValid()) { - headPosition = getHeadControllerPoseInSensorFrame().translation; - headOrientation = getHeadControllerPoseInSensorFrame().rotation * Quaternions::Y_180; + headPosition = headPose.translation; + headOrientation = headPose.rotation * Quaternions::Y_180; } const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation); @@ -2953,19 +2837,19 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const { switch (index) { case CONTROLLER_LEFTHAND_INDEX: { - return getLeftHandControllerPoseInAvatarFrame().getRotation(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getRotation(); } case CONTROLLER_RIGHTHAND_INDEX: { - return getRightHandControllerPoseInAvatarFrame().getRotation(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getRotation(); } case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: { - auto pose = _leftHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return glmExtractRotation(result); } case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: { - auto pose = _rightHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return glmExtractRotation(result); @@ -2990,19 +2874,19 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const { switch (index) { case CONTROLLER_LEFTHAND_INDEX: { - return getLeftHandControllerPoseInAvatarFrame().getTranslation(); + return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation(); } case CONTROLLER_RIGHTHAND_INDEX: { - return getRightHandControllerPoseInAvatarFrame().getTranslation(); + return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation(); } case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: { - auto pose = _leftHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return extractTranslation(result); } case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: { - auto pose = _rightHandControllerPoseInSensorFrameCache.get(); + auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND); glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation); glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix); return extractTranslation(result); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4d599230fb..9b9f841ff0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -466,49 +466,12 @@ public: virtual void rebuildCollisionShape() override; - void setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); - controller::Pose getLeftHandControllerPoseInSensorFrame() const; - controller::Pose getRightHandControllerPoseInSensorFrame() const; - controller::Pose getLeftHandControllerPoseInWorldFrame() const; - controller::Pose getRightHandControllerPoseInWorldFrame() const; - controller::Pose getLeftHandControllerPoseInAvatarFrame() const; - controller::Pose getRightHandControllerPoseInAvatarFrame() const; - - typedef std::map> FingerPosesMap; - void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right); - FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const; - FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const; - - void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); - controller::Pose getLeftFootControllerPoseInSensorFrame() const; - controller::Pose getRightFootControllerPoseInSensorFrame() const; - controller::Pose getLeftFootControllerPoseInWorldFrame() const; - controller::Pose getRightFootControllerPoseInWorldFrame() const; - controller::Pose getLeftFootControllerPoseInAvatarFrame() const; - controller::Pose getRightFootControllerPoseInAvatarFrame() const; - - void setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2); - controller::Pose getHipsControllerPoseInSensorFrame() const; - controller::Pose getSpine2ControllerPoseInSensorFrame() const; - controller::Pose getHipsControllerPoseInWorldFrame() const; - controller::Pose getSpine2ControllerPoseInWorldFrame() const; - controller::Pose getHipsControllerPoseInAvatarFrame() const; - controller::Pose getSpine2ControllerPoseInAvatarFrame() const; - - void setHeadControllerPoseInSensorFrame(const controller::Pose& head); - controller::Pose getHeadControllerPoseInSensorFrame() const; - controller::Pose getHeadControllerPoseInWorldFrame() const; - controller::Pose getHeadControllerPoseInAvatarFrame() const; const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } - - void setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); - controller::Pose getLeftArmControllerPoseInSensorFrame() const; - controller::Pose getRightArmControllerPoseInSensorFrame() const; - controller::Pose getLeftArmControllerPoseInWorldFrame() const; - controller::Pose getRightArmControllerPoseInWorldFrame() const; - controller::Pose getLeftArmControllerPoseInAvatarFrame() const; - controller::Pose getRightArmControllerPoseInAvatarFrame() const; + void setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose); + controller::Pose getControllerPoseInSensorFrame(controller::Action action) const; + controller::Pose getControllerPoseInWorldFrame(controller::Action action) const; + controller::Pose getControllerPoseInAvatarFrame(controller::Action action) const; bool hasDriveInput() const; @@ -794,18 +757,9 @@ private: bool _hoverReferenceCameraFacingIsCaptured { false }; glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space - // These are stored in SENSOR frame - ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _leftHandFingerPosesInSensorFramceCache { }; - ThreadSafeValueCache _rightHandFingerPosesInSensorFramceCache { }; - ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _headControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _leftArmControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _rightArmControllerPoseInSensorFrameCache { controller::Pose() }; + // all poses are in sensor-frame + std::unordered_map _controllerPoseMap; + mutable std::mutex _controllerPoseMapMutex; bool _hmdLeanRecenterEnabled = true; AnimPose _prePhysicsRoomPose; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 9f2d080cd6..40d1fa3ffc 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -34,7 +34,7 @@ glm::quat MyHead::getHeadOrientation() const { // always the same. MyAvatar* myAvatar = static_cast(_owningAvatar); - auto headPose = myAvatar->getHeadControllerPoseInWorldFrame(); + auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD); if (headPose.isValid()) { return headPose.rotation * Quaternions::Y_180; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 97309d9678..f97b20dcca 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -46,13 +46,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } MyAvatar* myAvatar = static_cast(_owningAvatar); + assert(myAvatar); Rig::ControllerParameters params; AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f)); // input action is the highest priority source for head orientation. - auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); + auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.controllerPoses[Rig::ControllerType_Head] = avatarToRigPose * pose; @@ -67,7 +68,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Head] = false; } - auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); + auto avatarHipsPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HIPS); if (avatarHipsPose.isValid()) { AnimPose pose(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); params.controllerPoses[Rig::ControllerType_Hips] = avatarToRigPose * pose; @@ -77,7 +78,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Hips] = false; } - auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); + auto avatarSpine2Pose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::SPINE2); if (avatarSpine2Pose.isValid()) { AnimPose pose(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); params.controllerPoses[Rig::ControllerType_Spine2] = avatarToRigPose * pose; @@ -87,7 +88,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_Spine2] = false; } - auto avatarRightArmPose = myAvatar->getRightArmControllerPoseInAvatarFrame(); + auto avatarRightArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_ARM); if (avatarRightArmPose.isValid()) { AnimPose pose(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightArm] = avatarToRigPose * pose; @@ -96,8 +97,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity; params.controllerActiveFlags[Rig::ControllerType_RightArm] = false; } - - auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame(); + + auto avatarLeftArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_ARM); if (avatarLeftArmPose.isValid()) { AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftArm] = avatarToRigPose * pose; @@ -107,7 +108,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftArm] = false; } - auto avatarLeftHandPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); + auto avatarLeftHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); if (avatarLeftHandPose.isValid()) { AnimPose pose(avatarLeftHandPose.getRotation(), avatarLeftHandPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftHand] = avatarToRigPose * pose; @@ -117,7 +118,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftHand] = false; } - auto avatarRightHandPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); + auto avatarRightHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); if (avatarRightHandPose.isValid()) { AnimPose pose(avatarRightHandPose.getRotation(), avatarRightHandPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightHand] = avatarToRigPose * pose; @@ -127,7 +128,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_RightHand] = false; } - auto avatarLeftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); + auto avatarLeftFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_FOOT); if (avatarLeftFootPose.isValid()) { AnimPose pose(avatarLeftFootPose.getRotation(), avatarLeftFootPose.getTranslation()); params.controllerPoses[Rig::ControllerType_LeftFoot] = avatarToRigPose * pose; @@ -137,7 +138,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = false; } - auto avatarRightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); + auto avatarRightFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_FOOT); if (avatarRightFootPose.isValid()) { AnimPose pose(avatarRightFootPose.getRotation(), avatarRightFootPose.getTranslation()); params.controllerPoses[Rig::ControllerType_RightFoot] = avatarToRigPose * pose; @@ -175,49 +176,106 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig.updateFromEyeParameters(eyeParams); - updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame()); - updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame()); + updateFingers(); } -void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) { - // Assumes that finger poses are kept in order in the poses map. - - if (fingerPoses.size() == 0) { - return; -} - - auto posesMapItr = fingerPoses.begin(); - - bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1; +void MySkeletonModel::updateFingers() { MyAvatar* myAvatar = static_cast(_owningAvatar); - auto handPose = isLeftHand - ? myAvatar->getLeftHandControllerPoseInSensorFrame() - : myAvatar->getRightHandControllerPoseInSensorFrame(); - auto handJointRotation = handPose.getRotation(); - bool isHandValid = handPose.isValid(); - bool isFingerValid = false; - glm::quat previousJointRotation; - - while (posesMapItr != fingerPoses.end()) { - auto jointName = posesMapItr->second.second; - if (isHandValid && jointName.right(1) == "1") { - isFingerValid = posesMapItr->second.first.isValid(); - previousJointRotation = handJointRotation; + static std::vector>> fingerChains = { + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_THUMB1, "LeftHandThumb1" }, + { controller::Action::LEFT_HAND_THUMB2, "LeftHandThumb2" }, + { controller::Action::LEFT_HAND_THUMB3, "LeftHandThumb3" }, + { controller::Action::LEFT_HAND_THUMB4, "LeftHandThumb4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_INDEX1, "LeftHandIndex1" }, + { controller::Action::LEFT_HAND_INDEX2, "LeftHandIndex2" }, + { controller::Action::LEFT_HAND_INDEX3, "LeftHandIndex3" }, + { controller::Action::LEFT_HAND_INDEX4, "LeftHandIndex4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1" }, + { controller::Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2" }, + { controller::Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3" }, + { controller::Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_RING1, "LeftHandRing1" }, + { controller::Action::LEFT_HAND_RING2, "LeftHandRing2" }, + { controller::Action::LEFT_HAND_RING3, "LeftHandRing3" }, + { controller::Action::LEFT_HAND_RING4, "LeftHandRing4" } + }, + { + { controller::Action::LEFT_HAND, "LeftHand" }, + { controller::Action::LEFT_HAND_PINKY1, "LeftHandPinky1" }, + { controller::Action::LEFT_HAND_PINKY2, "LeftHandPinky2" }, + { controller::Action::LEFT_HAND_PINKY3, "LeftHandPinky3" }, + { controller::Action::LEFT_HAND_PINKY4, "LeftHandPinky4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_THUMB1, "RightHandThumb1" }, + { controller::Action::RIGHT_HAND_THUMB2, "RightHandThumb2" }, + { controller::Action::RIGHT_HAND_THUMB3, "RightHandThumb3" }, + { controller::Action::RIGHT_HAND_THUMB4, "RightHandThumb4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_INDEX1, "RightHandIndex1" }, + { controller::Action::RIGHT_HAND_INDEX2, "RightHandIndex2" }, + { controller::Action::RIGHT_HAND_INDEX3, "RightHandIndex3" }, + { controller::Action::RIGHT_HAND_INDEX4, "RightHandIndex4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1" }, + { controller::Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2" }, + { controller::Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3" }, + { controller::Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_RING1, "RightHandRing1" }, + { controller::Action::RIGHT_HAND_RING2, "RightHandRing2" }, + { controller::Action::RIGHT_HAND_RING3, "RightHandRing3" }, + { controller::Action::RIGHT_HAND_RING4, "RightHandRing4" } + }, + { + { controller::Action::RIGHT_HAND, "RightHand" }, + { controller::Action::RIGHT_HAND_PINKY1, "RightHandPinky1" }, + { controller::Action::RIGHT_HAND_PINKY2, "RightHandPinky2" }, + { controller::Action::RIGHT_HAND_PINKY3, "RightHandPinky3" }, + { controller::Action::RIGHT_HAND_PINKY4, "RightHandPinky4" } } + }; - if (isHandValid && isFingerValid) { - auto thisJointRotation = posesMapItr->second.first.getRotation(); - const float CONTROLLER_PRIORITY = 2.0f; - _rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation, - CONTROLLER_PRIORITY); - previousJointRotation = thisJointRotation; - } else { - _rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName)); + const float CONTROLLER_PRIORITY = 2.0f; + + for (auto& chain : fingerChains) { + glm::quat prevAbsRot = Quaternions::IDENTITY; + for (auto& link : chain) { + int index = _rig.indexOfJoint(link.second); + if (index >= 0) { + auto pose = myAvatar->getControllerPoseInSensorFrame(link.first); + if (pose.valid) { + glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation(); + // only set the rotation for the finger joints, not the hands. + if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) { + _rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY); + } + prevAbsRot = pose.getRotation(); + } else { + _rig.clearJointAnimationPriority(index); + } + } } - - posesMapItr++; } } diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index 6867c596af..ad0ae1b8e9 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -24,7 +24,7 @@ public: void updateRig(float deltaTime, glm::mat4 parentTransform) override; private: - void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses); + void updateFingers(); }; #endif // hifi_MySkeletonModel_h From 2e0bc36cfda23a12cc22691ae0e68b425cd1ed1a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 15:02:05 -0700 Subject: [PATCH 031/280] warning fix + whitespace --- plugins/openvr/src/ViveControllerManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 216d50a4af..ddafa5cb5b 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -716,7 +716,7 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u // pseudo buttons the depend on both of the above for-loops partitionTouchpad(controller::LS, controller::LX, controller::LY, controller::LS_CENTER, controller::LS_X, controller::LS_Y); partitionTouchpad(controller::RS, controller::RX, controller::RY, controller::RS_CENTER, controller::RS_X, controller::RS_Y); - } + } } } @@ -1073,7 +1073,7 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; glm::mat4 userRefLeftArm = refLeftArm; glm::mat4 userRefRightArm = refRightArm; - const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06; + const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06f; if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; From c85e187c6174c9894a660b433464519db1e0ccad Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 13 Jul 2017 18:12:33 -0700 Subject: [PATCH 032/280] first stab at secondary target pose support --- interface/src/Application.cpp | 16 ++- interface/src/avatar/MySkeletonModel.cpp | 132 +++++++----------- .../animation/src/AnimInverseKinematics.cpp | 31 ++++ .../animation/src/AnimInverseKinematics.h | 6 + libraries/animation/src/Rig.cpp | 71 ++++++---- libraries/animation/src/Rig.h | 43 ++++-- .../controllers/src/controllers/Actions.h | 14 +- 7 files changed, 190 insertions(+), 123 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9497456677..37001b55bb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4749,7 +4749,7 @@ void Application::update(float deltaTime) { myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } - static std::vector avatarControllerActions = { + static const std::vector avatarControllerActions = { controller::Action::LEFT_HAND, controller::Action::RIGHT_HAND, controller::Action::LEFT_FOOT, @@ -4796,7 +4796,19 @@ void Application::update(float deltaTime) { controller::Action::RIGHT_HAND_PINKY1, controller::Action::RIGHT_HAND_PINKY2, controller::Action::RIGHT_HAND_PINKY3, - controller::Action::RIGHT_HAND_PINKY4 + controller::Action::RIGHT_HAND_PINKY4, + controller::Action::LEFT_ARM, + controller::Action::RIGHT_ARM, + controller::Action::LEFT_SHOULDER, + controller::Action::RIGHT_SHOULDER, + controller::Action::LEFT_FORE_ARM, + controller::Action::RIGHT_FORE_ARM, + controller::Action::LEFT_LEG, + controller::Action::RIGHT_LEG, + controller::Action::LEFT_UP_LEG, + controller::Action::RIGHT_UP_LEG, + controller::Action::LEFT_TOE_BASE, + controller::Action::RIGHT_TOE_BASE }; glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f97b20dcca..4a86e0ce0a 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -56,96 +56,72 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Head] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Head] = true; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = true; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and // down in desktop mode. // preMult 180 is necessary to convert from avatar to rig coordinates. // postMult 180 is necessary to convert head from -z forward to z forward. glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; - params.controllerPoses[Rig::ControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); - params.controllerActiveFlags[Rig::ControllerType_Head] = false; + params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); + params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = false; } - auto avatarHipsPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HIPS); - if (avatarHipsPose.isValid()) { - AnimPose pose(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Hips] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Hips] = true; - } else { - params.controllerPoses[Rig::ControllerType_Hips] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_Hips] = false; + // + // primary controller poses, control IK targets directly. + // + + static const std::vector> primaryControllers = { + { controller::Action::LEFT_HAND, Rig::PrimaryControllerType_LeftHand }, + { controller::Action::RIGHT_HAND, Rig::PrimaryControllerType_RightHand }, + { controller::Action::HIPS, Rig::PrimaryControllerType_Hips }, + { controller::Action::LEFT_FOOT, Rig::PrimaryControllerType_LeftFoot }, + { controller::Action::RIGHT_FOOT, Rig::PrimaryControllerType_RightFoot }, + { controller::Action::SPINE2, Rig::PrimaryControllerType_Spine2 } + }; + + for (auto pair : primaryControllers) { + auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (pose.isValid()) { + AnimPose pose(pose.getRotation(), pose.getTranslation()); + params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; + params.primaryControllerActiveFlags[pair.second] = true; + } else { + params.primaryControllerPoses[pair.second] = AnimPose::identity; + params.primaryControllerActiveFlags[pair.second] = false; + } } - auto avatarSpine2Pose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::SPINE2); - if (avatarSpine2Pose.isValid()) { - AnimPose pose(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); - params.controllerPoses[Rig::ControllerType_Spine2] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_Spine2] = true; - } else { - params.controllerPoses[Rig::ControllerType_Spine2] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_Spine2] = false; - } + // + // secondary controller poses, influence the pose of the skeleton indirectly. + // - auto avatarRightArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_ARM); - if (avatarRightArmPose.isValid()) { - AnimPose pose(avatarRightArmPose.getRotation(), avatarRightArmPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightArm] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightArm] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightArm] = false; - } + static const std::vector> secondaryControllers = { + { controller::Action::LEFT_SHOULDER, Rig::SecondaryControllerType_LeftShoulder }, + { controller::Action::RIGHT_SHOULDER, Rig::SecondaryControllerType_RightShoulder }, + { controller::Action::LEFT_ARM, Rig::SecondaryControllerType_LeftArm }, + { controller::Action::RIGHT_ARM, Rig::SecondaryControllerType_RightArm }, + { controller::Action::LEFT_FORE_ARM, Rig::SecondaryControllerType_LeftForeArm }, + { controller::Action::RIGHT_FORE_ARM, Rig::SecondaryControllerType_RightForeArm }, + { controller::Action::LEFT_UP_LEG, Rig::SecondaryControllerType_LeftUpLeg }, + { controller::Action::RIGHT_UP_LEG, Rig::SecondaryControllerType_RightUpLeg }, + { controller::Action::LEFT_LEG, Rig::SecondaryControllerType_LeftLeg }, + { controller::Action::RIGHT_LEG, Rig::SecondaryControllerType_RightLeg }, + { controller::Action::LEFT_TOE_BASE, Rig::SecondaryControllerType_LeftToeBase }, + { controller::Action::RIGHT_TOE_BASE, Rig::SecondaryControllerType_RightToeBase } + }; - auto avatarLeftArmPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_ARM); - if (avatarLeftArmPose.isValid()) { - AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftArm] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftArm] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftArm] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftArm] = false; - } - - auto avatarLeftHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND); - if (avatarLeftHandPose.isValid()) { - AnimPose pose(avatarLeftHandPose.getRotation(), avatarLeftHandPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftHand] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftHand] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftHand] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftHand] = false; - } - - auto avatarRightHandPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND); - if (avatarRightHandPose.isValid()) { - AnimPose pose(avatarRightHandPose.getRotation(), avatarRightHandPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightHand] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightHand] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightHand] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightHand] = false; - } - - auto avatarLeftFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_FOOT); - if (avatarLeftFootPose.isValid()) { - AnimPose pose(avatarLeftFootPose.getRotation(), avatarLeftFootPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_LeftFoot] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = true; - } else { - params.controllerPoses[Rig::ControllerType_LeftFoot] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_LeftFoot] = false; - } - - auto avatarRightFootPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_FOOT); - if (avatarRightFootPose.isValid()) { - AnimPose pose(avatarRightFootPose.getRotation(), avatarRightFootPose.getTranslation()); - params.controllerPoses[Rig::ControllerType_RightFoot] = avatarToRigPose * pose; - params.controllerActiveFlags[Rig::ControllerType_RightFoot] = true; - } else { - params.controllerPoses[Rig::ControllerType_RightFoot] = AnimPose::identity; - params.controllerActiveFlags[Rig::ControllerType_RightFoot] = false; + for (auto pair : secondaryControllers) { + auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (pose.isValid()) { + AnimPose pose(pose.getRotation(), pose.getTranslation()); + params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; + params.secondaryControllerActiveFlags[pair.second] = true; + } else { + params.secondaryControllerPoses[pair.second] = AnimPose::identity; + params.secondaryControllerActiveFlags[pair.second] = false; + } } params.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d7076a443e..86ebe5fc91 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -921,6 +921,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars { PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0); preconditionRelativePosesToAvoidLimbLock(context, targets); + setSecondaryTargets(context); solve(context, targets); } @@ -1015,6 +1016,22 @@ void AnimInverseKinematics::clearIKJointLimitHistory() { } } +void AnimInverseKinematics::setSecondaryTargetInRigFrame(int jointIndex, const AnimPose& pose) { + auto iter = _secondaryTargetsInRigFrame.find(jointIndex); + if (iter != _secondaryTargetsInRigFrame.end()) { + iter->second = pose; + } else { + _secondaryTargetsInRigFrame.insert({ jointIndex, pose }); + } +} + +void AnimInverseKinematics::clearSecondaryTarget(int jointIndex) { + auto iter = _secondaryTargetsInRigFrame.find(jointIndex); + if (iter != _secondaryTargetsInRigFrame.end()) { + _secondaryTargetsInRigFrame.erase(iter); + } +} + RotationConstraint* AnimInverseKinematics::getConstraint(int index) const { RotationConstraint* constraint = nullptr; std::map::const_iterator constraintItr = _constraints.find(index); @@ -1722,6 +1739,20 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC } } +// overwrites _relativePoses with secondary poses. +void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { + AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); + for (auto& iter : _secondaryTargetsInRigFrame) { + AnimPose absPose = rigToGeometryPose * iter.second; + AnimPose parentAbsPose; + int parentIndex = _skeleton->getParentIndex(iter.first); + if (parentIndex >= 0) { + parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); + } + _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; + } +} + void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPoses) { const float RELAX_BLEND_FACTOR = (1.0f / 16.0f); const float COPY_BLEND_FACTOR = 1.0f; diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index d473ae3698..2ca333323a 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -63,6 +63,9 @@ public: NumSolutionSources, }; + void setSecondaryTargetInRigFrame(int jointIndex, const AnimPose& pose); + void clearSecondaryTarget(int jointIndex); + void setSolutionSource(SolutionSource solutionSource) { _solutionSource = solutionSource; } void setSolutionSourceVar(const QString& solutionSourceVar) { _solutionSourceVar = solutionSourceVar; } @@ -83,6 +86,7 @@ protected: void initRelativePosesFromSolutionSource(SolutionSource solutionSource, const AnimPoseVec& underPose); void blendToPoses(const AnimPoseVec& targetPoses, const AnimPoseVec& underPose, float blendFactor); void preconditionRelativePosesToAvoidLimbLock(const AnimContext& context, const std::vector& targets); + void setSecondaryTargets(const AnimContext& context); // used to pre-compute information about each joint influeced by a spline IK target. struct SplineJointInfo { @@ -136,6 +140,8 @@ protected: AnimPoseVec _relativePoses; // current relative poses AnimPoseVec _limitCenterPoses; // relative + std::map _secondaryTargetsInRigFrame; + std::map> _splineJointInfoMap; // experimental data for moving hips during IK diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6ebb68773f..9db8b187b6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1474,24 +1474,25 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo _animVars.set("isTalking", params.isTalking); _animVars.set("notIsTalking", !params.isTalking); - bool headEnabled = params.controllerActiveFlags[ControllerType_Head]; - bool leftHandEnabled = params.controllerActiveFlags[ControllerType_LeftHand]; - bool rightHandEnabled = params.controllerActiveFlags[ControllerType_RightHand]; - bool hipsEnabled = params.controllerActiveFlags[ControllerType_Hips]; - bool leftFootEnabled = params.controllerActiveFlags[ControllerType_LeftFoot]; - bool rightFootEnabled = params.controllerActiveFlags[ControllerType_RightFoot]; - bool leftArmEnabled = params.controllerActiveFlags[ControllerType_LeftArm]; - bool rightArmEnabled = params.controllerActiveFlags[ControllerType_RightArm]; - bool spine2Enabled = params.controllerActiveFlags[ControllerType_Spine2]; + bool headEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Head]; + bool leftHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftHand]; + bool rightHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightHand]; + bool hipsEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Hips]; + bool leftFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftFoot]; + bool rightFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightFoot]; + bool spine2Enabled = params.primaryControllerActiveFlags[PrimaryControllerType_Spine2]; - updateHead(headEnabled, hipsEnabled, params.controllerPoses[ControllerType_Head]); + bool leftArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_LeftArm]; + bool rightArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_RightArm]; + + updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]); updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt, - params.controllerPoses[ControllerType_LeftHand], params.controllerPoses[ControllerType_RightHand], + params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand], params.bodyCapsuleRadius, params.bodyCapsuleHalfHeight, params.bodyCapsuleLocalOffset); updateFeet(leftFootEnabled, rightFootEnabled, - params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]); + params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot]); // if the hips or the feet are being controlled. if (hipsEnabled || rightFootEnabled || leftFootEnabled) { @@ -1512,34 +1513,46 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo if (hipsEnabled) { _animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans()); - _animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot()); + _animVars.set("hipsPosition", params.primaryControllerPoses[PrimaryControllerType_Hips].trans()); + _animVars.set("hipsRotation", params.primaryControllerPoses[PrimaryControllerType_Hips].rot()); } else { _animVars.set("hipsType", (int)IKTarget::Type::Unknown); } if (hipsEnabled && spine2Enabled) { _animVars.set("spine2Type", (int)IKTarget::Type::Spline); - _animVars.set("spine2Position", params.controllerPoses[ControllerType_Spine2].trans()); - _animVars.set("spine2Rotation", params.controllerPoses[ControllerType_Spine2].rot()); + _animVars.set("spine2Position", params.primaryControllerPoses[PrimaryControllerType_Spine2].trans()); + _animVars.set("spine2Rotation", params.primaryControllerPoses[PrimaryControllerType_Spine2].rot()); } else { _animVars.set("spine2Type", (int)IKTarget::Type::Unknown); } - if (leftArmEnabled) { - _animVars.set("leftArmType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("leftArmPosition", params.controllerPoses[ControllerType_LeftArm].trans()); - _animVars.set("leftArmRotation", params.controllerPoses[ControllerType_LeftArm].rot()); - } else { - _animVars.set("leftArmType", (int)IKTarget::Type::Unknown); - } + // set secondary targets + static const std::vector secondaryControllerJointNames = { + "LeftShoulder", + "RightShoulder", + "LeftArm", + "RightArm", + "LeftForeArm", + "RightForeArm", + "LeftUpLeg", + "RightUpLeg", + "LeftLeg", + "RightLeg", + "LeftToeBase", + "RightToeBase" + }; - if (rightArmEnabled) { - _animVars.set("rightArmType", (int)IKTarget::Type::RotationAndPosition); - _animVars.set("rightArmPosition", params.controllerPoses[ControllerType_RightArm].trans()); - _animVars.set("rightArmRotation", params.controllerPoses[ControllerType_RightArm].rot()); - } else { - _animVars.set("rightArmType", (int)IKTarget::Type::Unknown); + std::shared_ptr ikNode = getAnimInverseKinematicsNode(); + for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) { + int index = indexOfJoint(secondaryControllerJointNames[i]); + if (index >= 0) { + if (params.secondaryControllerActiveFlags[i]) { + ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]); + } else { + ikNode->clearSecondaryTarget(index); + } + } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c17a7b9c8f..5718c62c26 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -41,22 +41,39 @@ public: bool useNames; }; - enum ControllerType { - ControllerType_Head = 0, - ControllerType_LeftHand, - ControllerType_RightHand, - ControllerType_Hips, - ControllerType_LeftFoot, - ControllerType_RightFoot, - ControllerType_LeftArm, - ControllerType_RightArm, - ControllerType_Spine2, - NumControllerTypes + enum PrimaryControllerType { + PrimaryControllerType_Head = 0, + PrimaryControllerType_LeftHand, + PrimaryControllerType_RightHand, + PrimaryControllerType_Hips, + PrimaryControllerType_LeftFoot, + PrimaryControllerType_RightFoot, + PrimaryControllerType_Spine2, + NumPrimaryControllerTypes + }; + + // NOTE: These should ordered such that joint parents appear before their children. + enum SecondaryControllerType { + SecondaryControllerType_LeftShoulder = 0, + SecondaryControllerType_RightShoulder, + SecondaryControllerType_LeftArm, + SecondaryControllerType_RightArm, + SecondaryControllerType_LeftForeArm, + SecondaryControllerType_RightForeArm, + SecondaryControllerType_LeftUpLeg, + SecondaryControllerType_RightUpLeg, + SecondaryControllerType_LeftLeg, + SecondaryControllerType_RightLeg, + SecondaryControllerType_LeftToeBase, + SecondaryControllerType_RightToeBase, + NumSecondaryControllerTypes }; struct ControllerParameters { - AnimPose controllerPoses[NumControllerTypes]; // rig space - bool controllerActiveFlags[NumControllerTypes]; + AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space + bool primaryControllerActiveFlags[NumPrimaryControllerTypes]; + AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space + bool secondaryControllerActiveFlags[NumSecondaryControllerTypes]; bool isTalking; float bodyCapsuleRadius; float bodyCapsuleHalfHeight; diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index ec4800c9aa..9aa9bbdb29 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -101,6 +101,7 @@ enum class Action { // Bisected aliases for TRANSLATE_CAMERA_Z BOOM_IN, BOOM_OUT, + LEFT_ARM, RIGHT_ARM, @@ -146,7 +147,18 @@ enum class Action { RIGHT_HAND_PINKY3, RIGHT_HAND_PINKY4, - NUM_ACTIONS, + LEFT_SHOULDER, + RIGHT_SHOULDER, + LEFT_FORE_ARM, + RIGHT_FORE_ARM, + LEFT_LEG, + RIGHT_LEG, + LEFT_UP_LEG, + RIGHT_UP_LEG, + LEFT_TOE_BASE, + RIGHT_TOE_BASE, + + NUM_ACTIONS }; template From af751c8b8c77dd7f522a14771ef438093d759dcd Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Mon, 17 Jul 2017 10:53:37 -0400 Subject: [PATCH 033/280] Add filter to map the boolean negation of a flag. --- .../src/controllers/impl/Filter.cpp | 2 ++ .../controllers/impl/RouteBuilderProxy.cpp | 6 ++++++ .../src/controllers/impl/RouteBuilderProxy.h | 1 + .../controllers/impl/filters/NotFilter.cpp | 10 +++++++++ .../src/controllers/impl/filters/NotFilter.h | 21 +++++++++++++++++++ 5 files changed, 40 insertions(+) create mode 100644 libraries/controllers/src/controllers/impl/filters/NotFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/NotFilter.h diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index e75465f007..08cba9c296 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -22,6 +22,7 @@ #include "filters/DeadZoneFilter.h" #include "filters/HysteresisFilter.h" #include "filters/InvertFilter.h" +#include "filters/NotFilter.h" #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" @@ -40,6 +41,7 @@ REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPos REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "not") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 581d51ea61..c6abc2d402 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -24,6 +24,7 @@ #include "filters/DeadZoneFilter.h" #include "filters/HysteresisFilter.h" #include "filters/InvertFilter.h" +#include "filters/NotFilter.h" #include "filters/PulseFilter.h" #include "filters/ScaleFilter.h" #include "filters/TranslateFilter.h" @@ -148,6 +149,11 @@ QObject* RouteBuilderProxy::pulse(float interval) { return this; } +QObject* RouteBuilderProxy::not() { + addFilter(std::make_shared()); + return this; +} + void RouteBuilderProxy::addFilter(Filter::Pointer filter) { _route->filters.push_back(filter); } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 102e72b3ad..d70bdb649f 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* postTransform(glm::mat4 transform); Q_INVOKABLE QObject* rotate(glm::quat rotation); Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant); + Q_INVOKABLE QObject* not(); private: void to(const Endpoint::Pointer& destination); diff --git a/libraries/controllers/src/controllers/impl/filters/NotFilter.cpp b/libraries/controllers/src/controllers/impl/filters/NotFilter.cpp new file mode 100644 index 0000000000..73460aed91 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/NotFilter.cpp @@ -0,0 +1,10 @@ +#include "NotFilter.h" + +using namespace controller; + +NotFilter::NotFilter() { +} + +float NotFilter::apply(float value) const { + return (value == 0.0f) ? 1.0f : 0.0f; +} diff --git a/libraries/controllers/src/controllers/impl/filters/NotFilter.h b/libraries/controllers/src/controllers/impl/filters/NotFilter.h new file mode 100644 index 0000000000..ceb7d29de3 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/NotFilter.h @@ -0,0 +1,21 @@ +#pragma once +#ifndef hifi_Controllers_Filters_Not_h +#define hifi_Controllers_Filters_Not_h + +#include "../Filter.h" + +namespace controller { + +class NotFilter : public Filter { + REGISTER_FILTER_CLASS(NotFilter); +public: + NotFilter(); + + virtual float apply(float value) const override; + virtual Pose apply(Pose value) const override { return value; } +}; + +} + +#endif + From 7319998df9cf8a838e8d812bb8ff6a7a6eae7e41 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Mon, 17 Jul 2017 10:58:30 -0400 Subject: [PATCH 034/280] Move ThumbUp gesture detection into json map file. --- interface/resources/controllers/vive.json | 12 +++++++++--- plugins/openvr/src/ViveControllerManager.cpp | 11 +++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 349554673d..cc8e2a437a 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -22,6 +22,10 @@ { "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" }, { "from": "Vive.LS", "to": "Standard.LS" }, + { "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp", + "peek": true, + "filters": [ { "type": "not" } ] + }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, { "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" }, @@ -36,6 +40,10 @@ { "from": "Vive.RightGrip", "to": "Standard.RightGrip" }, { "from": "Vive.RS", "to": "Standard.RS" }, + { "from": "Vive.RSTouch", "to": "Standard.RightThumbUp", + "peek": true, + "filters": [ { "type": "not" } ] + }, { "from": "Vive.RSTouch", "to": "Standard.RSTouch" }, { "from": "Vive.LSCenter", "to": "Standard.LeftPrimaryThumb" }, @@ -69,8 +77,6 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, - { "from": "Vive.LeftThumbUp", "to": "Standard.LeftThumbUp" }, - { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 895aff4f03..07b3b2f73d 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -863,11 +863,9 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint } } - if (button == vr::k_EButton_SteamVR_Touchpad) { - if (touched) { - _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); - } else { - _buttonPressedMap.insert(isLeftHand ? LEFT_THUMB_UP : RIGHT_THUMB_UP); + if (touched) { + if (button == vr::k_EButton_SteamVR_Touchpad) { + _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); } } } @@ -1172,9 +1170,6 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI // app button above trackpad. Input::NamedPair(Input(_deviceID, LEFT_APP_MENU, ChannelType::BUTTON), "LeftApplicationMenu"), Input::NamedPair(Input(_deviceID, RIGHT_APP_MENU, ChannelType::BUTTON), "RightApplicationMenu"), - - makePair(LEFT_THUMB_UP, "LeftThumbUp"), - makePair(RIGHT_THUMB_UP, "RightThumbUp"), }; return availableInputs; From 7bcd9f9f9544256638657ca15c78671c4b7e47d6 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 12:22:11 -0400 Subject: [PATCH 035/280] * integrate support for room scale "moves as you move" * migrate doppleganger debug controls into separate module * misc cleanup --- .../app-doppleganger-attachments.js | 106 +- .../dist/app-doppleganger-marketplace.js | 1501 +++++++++++++++++ .../doppleganger-attachments.js | 53 +- .../doppleganger-debug.js | 158 ++ .../doppleganger-attachments/doppleganger.js | 238 +-- .../doppleganger-attachments/makefile | 11 + .../doppleganger-attachments/model-helper.js | 9 +- .../doppleganger-attachments/package.json | 5 + .../doppleganger-attachments/readme.md | 4 + .../doppleganger-attachments/utils.js | 8 + 10 files changed, 1843 insertions(+), 250 deletions(-) create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/makefile create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/package.json create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/readme.md diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js index 4617cf47b6..c09ad602f8 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js @@ -9,21 +9,44 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ -var require = Script.require; +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (typeof require !== 'function') { + require = Script.require; +} +var VERSION = '0.0.0'; var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); var DopplegangerClass = require('./doppleganger.js'), DopplegangerAttachments = require('./doppleganger-attachments.js'), - modelHelper = require('./model-helper.js').modelHelper; + DebugControls = require('./doppleganger-debug.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: require('./doppleganger-i.svg.json'), + activeIcon: require('./doppleganger-a.svg.json'), +}); var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), - button = tablet.addButton({ - icon: Script.resolvePath('./doppleganger-i.svg'), - activeIcon: Script.resolvePath('./doppleganger-a.svg'), - text: 'MIRROR' - }); + button = tablet.addButton(buttonConfig); Script.scriptEnding.connect(function() { tablet.removeButton(button); @@ -65,6 +88,59 @@ var doppleganger = new DopplegangerClass({ } } +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + // hide the doppleganger if this client script is unloaded Script.scriptEnding.connect(doppleganger, 'stop'); @@ -103,15 +179,21 @@ doppleganger.modelLoaded.connect(function(error, result) { // add debug indicators, but only if the user has configured the settings value if (Settings.getValue('debug.doppleganger', false)) { - WANT_DEBUG = true; - DopplegangerClass.addDebugControls(doppleganger); + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { - if (WANT_DEBUG) { - print('app-doppleganger | ' + [].slice.call(arguments).join(' ')); - } + WANT_DEBUG && log.apply(this, arguments); } + // ---------------------------------------------------------------------------- UserActivityLogger.logAction('doppleganger_app_load'); diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js new file mode 100644 index 0000000000..6abd3a0fc5 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -0,0 +1,1501 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/* eslint-env commonjs */ +/* global console */ + +module.exports = { + version: '0.0.1', + bind: bind, + signal: signal, + assign: assign, + assert: assert +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +// @function - bind a function to a `this` context +// @param {Object} - the `this` context +// @param {Function|String} - function or method name +// @param {value} varargs... - optional curry-right arguments (passed to method after any explicit arguments) +function bind(thiz, method, varargs) { + method = thiz[method] || method; + varargs = [].slice.call(arguments, 2); + return function() { + var args = [].slice.call(arguments).concat(varargs); + return method.apply(thiz, args); + }; +} + +// @function - Qt signal polyfill +function signal(template) { + var callbacks = []; + return Object.defineProperties(function() { + var args = [].slice.call(arguments); + callbacks.forEach(function(obj) { + obj.handler.apply(obj.scope, args); + }); + }, { + connect: { value: function(scope, handler) { + var callback = {scope: scope, handler: scope[handler] || handler || scope}; + if (!callback.handler || !callback.handler.apply) { + throw new Error('invalid arguments to connect:' + [template, scope, handler]); + } + callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); + }}, + disconnect: { value: function(scope, handler) { + var match = {scope: scope, handler: scope[handler] || handler || scope}; + callbacks = callbacks.filter(function(obj) { + return !(obj.scope === match.scope && obj.handler === match.handler); + }); + }} + }); +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill +/* eslint-disable */ +function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; +} +/* eslint-enable */ +// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill + +// examples: +// assert(function assertion() { return (conditions === true) }, 'assertion failed!') +// var neededValue = assert(idString, 'idString not specified!'); +// assert(false, 'unexpected state'); +function assert(truthy, message) { + message = message || 'Assertion Failed:'; + + if (typeof truthy === 'function' && truthy.name === 'assertion') { + // extract function body to display with the assertion message + var assertion = (truthy+'').replace(/[\r\n]/g, ' ') + .replace(/^[^{]+\{|\}$|^\s*|\s*$/g, '').trim() + .replace(/^return /,'').replace(/\s[\r\n\t\s]+/g, ' '); + message += ' ' + JSON.stringify(assertion); + try { + truthy = truthy(); + } catch (e) { + message += '(exception: ' + e +')'; + } + } + if (!truthy) { + message += ' ('+truthy+')'; + throw new Error(message); + } + return truthy; +} + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +// model-helper.js +// +// Created by Timothy Dedischew on 06/01/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module model-helper +// +// This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and +// also initial plumbing helpers to eliminate unnecessary API differences when working with Model Overlays and +// Model Entities at a high-level from scripting. + +var utils = __webpack_require__(0), + assert = utils.assert; + +module.exports = { + version: '0.0.1', + ModelReadyWatcher: ModelReadyWatcher +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +var _objectDeleted = utils.signal(function objectDeleted(objectID){}); +// proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal +var objectDeleted = utils.assign(function objectDeleted(objectID){}, { + connect: function() { + Overlays.overlayDeleted.connect(_objectDeleted); + // Entities.deletingEntity.connect(objectDeleted); + Script.scriptEnding.connect(function() { + Overlays.overlayDeleted.disconnect(_objectDeleted); + // Entities.deletingEntity.disconnect(objectDeleted); + }); + // hereafter _objectDeleted.connect will be used instead + this.connect = utils.bind(_objectDeleted, 'connect'); + return this.connect.apply(this, arguments); + }, + disconnect: utils.bind(_objectDeleted, 'disconnect') +}); + +var modelHelper = module.exports.modelHelper = { + // Entity <-> Overlay property translations + _entityFromOverlay: { + modelURL: function url() { + return this.url; + }, + dimensions: function dimensions() { + return Vec3.multiply(this.scale, this.naturalDimensions); + } + }, + _overlayFromEntity: { + url: function modelURL() { + return this.modelURL; + }, + scale: function scale() { + return this.dimensions && this.naturalDimensions && { + x: this.dimensions.x / this.naturalDimensions.x, + y: this.dimensions.y / this.naturalDimensions.y, + z: this.dimensions.z / this.naturalDimensions.z + }; + } + }, + objectDeleted: objectDeleted, + type: function(objectID) { + // TODO: support Model Entities (by detecting type from objectID, which is already possible) + return !Uuid.isNull(objectID) ? 'overlay' : null; + }, + addObject: function(properties) { + var type = 'overlay'; // this.resolveType(properties) + switch (type) { + case 'overlay': return Overlays.addOverlay(properties.type, this.toOverlayProps(properties)); + } + return false; + }, + editObject: function(objectID, properties) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.editOverlay(objectID, this.toOverlayProps(properties)); + } + return false; + }, + deleteObject: function(objectID) { + return this.type(objectID) === 'overlay' && Overlays.deleteOverlay(objectID); + }, + getProperty: function(objectID, propertyName) { + return this.type(objectID) === 'overlay' && Overlays.getProperty(objectID, propertyName); + }, + getProperties: function(objectID, filter) { + switch (this.type(objectID)) { + case 'overlay': + filter = Array.isArray(filter) ? filter : [ + 'position', 'rotation', 'localPosition', 'localRotation', 'parentID', + 'parentJointIndex', 'scale', 'name', 'visible', 'type', 'url', + 'dimensions', 'naturalDimensions', 'grabbable' + ]; + var properties = filter.reduce(function(out, propertyName) { + out[propertyName] = Overlays.getProperty(objectID, propertyName); + return out; + }, {}); + return this.toEntityProps(properties); + } + return null; + }, + // adapt Entity conventions to Overlay (eg: { modelURL: ... } -> { url: ... }) + toOverlayProps: function(properties) { + var result = {}; + for (var from in this._overlayFromEntity) { + var adapter = this._overlayFromEntity[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + // adapt Overlay conventions to Entity (eg: { url: ... } -> { modelURL: ... }) + toEntityProps: function(properties) { + var result = {}; + for (var from in this._entityToOverlay) { + var adapter = this._entityToOverlay[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + editObjects: function(updatedObjects) { + var objectIDs = Object.keys(updatedObjects), + type = this.type(objectIDs[0]); + switch (type) { + case 'overlay': + var translated = {}; + for (var objectID in updatedObjects) { + translated[objectID] = this.toOverlayProps(updatedObjects[objectID]); + } + return Overlays.editOverlays(translated); + } + return false; + }, + getJointIndex: function(objectID, name) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames').indexOf(name); + } + return -1; + }, + getJointNames: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames'); + } + return []; + }, + // @function - derives mirrored joint names from a list of regular joint names + // @param {Array} - list of joint names to mirror + // @return {Array} - list of mirrored joint names (note: entries for non-mirrored joints will be `undefined`) + deriveMirroredJointNames: function(jointNames) { + return jointNames.map(function(name, i) { + if (/Left/.test(name)) { + return name.replace('Left', 'Right'); + } + if (/Right/.test(name)) { + return name.replace('Right', 'Left'); + } + return undefined; + }); + }, + getJointPosition: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions')[index]; + } + return Vec3.ZERO; + }, + getJointPositions: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions'); + } + return []; + }, + getJointOrientation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations')[index]; + } + return Quat.normalize({}); + }, + getJointOrientations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations'); + } + }, + getJointTranslation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations')[index]; + } + return Vec3.ZERO; + }, + getJointTranslations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations'); + } + return []; + }, + getJointRotation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations')[index]; + } + return Quat.normalize({}); + }, + getJointRotations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations'); + } + return []; + } +}; // modelHelper + + +// @property {PreconditionFunction} - indicates when the model's jointNames have become available +ModelReadyWatcher.JOINTS = function(state) { + return Array.isArray(state.jointNames); +}; +// @property {PreconditionFunction} - indicates when a model's naturalDimensions have become available +ModelReadyWatcher.DIMENSIONS = function(state) { + return Vec3.length(state.naturalDimensions) > 0; +}; +// @property {PreconditionFunction} - indicates when both a model's naturalDimensions and jointNames have become available +ModelReadyWatcher.JOINTS_AND_DIMENSIONS = function(state) { + // eslint-disable-next-line new-cap + return ModelReadyWatcher.JOINTS(state) && ModelReadyWatcher.DIMENSIONS(state); +}; +// @property {int} - interval used for continually rechecking model readiness, until ready or a timeout occurs +ModelReadyWatcher.RECHECK_MS = 50; +// @property {int} - default wait time before considering a model unready-able. +ModelReadyWatcher.DEFAULT_TIMEOUT_SECS = 10; + +// @private @class - waits for model to become usable inworld and tracks errors/timeouts +// @param [Object} options -- key/value config options: +// @param {ModelResource} options.resource - a ModelCache prefetched resource to observe for determining load state +// @param {Uuid} options.objectID - an inworld object to observe for determining readiness states +// @param {Function} [options.precondition=ModelReadyWatcher.JOINTS] - the precondition used to determine if the model is usable +// @param {int} [options.maxWaitSeconds=10] - max seconds to wait for the model to become usable, after which a timeout error is emitted +// @return {ModelReadyWatcher} +function ModelReadyWatcher(options) { + options = utils.assign({ + precondition: ModelReadyWatcher.JOINTS, + maxWaitSeconds: ModelReadyWatcher.DEFAULT_TIMEOUT_SECS + }, options); + + assert(!Uuid.isNull(options.objectID), 'Error: invalid options.objectID'); + assert(options.resource && 'state' in options.resource, 'Error: invalid options.resource'); + assert(typeof options.precondition === 'function', 'Error: invalid options.precondition'); + + utils.assign(this, { + resource: options.resource, + objectID: options.objectID, + precondition: options.precondition, + + // @signal - emitted when the model becomes ready, or with the error that prevented it + modelReady: utils.signal(function modelReady(error, result){}), + + // @public + ready: false, // tracks readiness state + jointNames: null, // populated with detected jointNames + naturalDimensions: null, // populated with detected naturalDimensions + + _startTime: new Date, + _watchdogTimer: Script.setTimeout(utils.bind(this, function() { + this._watchdogTimer = null; + }), options.maxWaitSeconds * 1000), + _interval: Script.setInterval(utils.bind(this, '_waitUntilReady'), ModelReadyWatcher.RECHECK_MS) + }); + + this.modelReady.connect(this, function(error, result) { + this.ready = !error && result; + }); +} + +ModelReadyWatcher.prototype = { + contructor: ModelReadyWatcher, + // @public method -- cancels monitoring and (if model was not yet ready) emits an error + cancel: function() { + return this._stop() && !this.ready && this.modelReady('cancelled', null); + }, + // stop pending timers + _stop: function() { + var stopped = 0; + if (this._watchdogTimer) { + Script.clearTimeout(this._watchdogTimer); + this._watchdogTimer = null; + stopped++; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = null; + stopped++; + } + return stopped; + }, + // the monitoring thread func + _waitUntilReady: function() { + var error = null, result = null; + if (!this._watchdogTimer) { + error = this.precondition.name || 'timeout'; + } else if (this.resource.state === Resource.State.FAILED) { + error = 'prefetch_failed'; + } else if (this.resource.state === Resource.State.FINISHED) { + // in theory there will always be at least one "joint name" that represents the main submesh + var names = modelHelper.getJointNames(this.objectID); + if (Array.isArray(names) && names.length) { + this.jointNames = names; + } + var props = modelHelper.getProperties(this.objectID, ['naturalDimensions']); + if (props && props.naturalDimensions && Vec3.length(props.naturalDimensions)) { + this.naturalDimensions = props.naturalDimensions; + } + var state = { + resource: this.resource, + objectID: this.objectID, + waitTime: (new Date - this._startTime) / 1000, + jointNames: this.jointNames, + naturalDimensions: this.naturalDimensions + }; + if (this.precondition(state)) { + result = state; + } + } + if (error || result !== null) { + this._stop(); + this.modelReady(error, result); + } + } +}; // ModelReadyWatcher.prototype + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// doppleganger.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module doppleganger +// +// This module contains the `Doppleganger` class implementation for creating an inspectable replica of +// an Avatar (as a model directly in front of and facing them). Joint positions and rotations are copied +// over in an update thread, so that the model automatically mirrors the Avatar's joint movements. +// An Avatar can then for example walk around "themselves" and examine from the back, etc. +// +// This should be helpful for inspecting your own look and debugging avatars, etc. +// +// The doppleganger is created as an overlay so that others do not see it -- and this also allows for the +// highest possible update rate when keeping joint data in sync. + +module.exports = Doppleganger; + +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +// @property {bool} - toggle verbose debug logging on/off +Doppleganger.WANT_DEBUG = false; + +// @property {bool} - when set true, Script.update will be used instead of setInterval for syncing joint data +Doppleganger.USE_SCRIPT_UPDATE = false; + +// @property {int} - the frame rate to target when using setInterval for joint updates +Doppleganger.TARGET_FPS = 60; + +// @class Doppleganger - Creates a new instance of a Doppleganger. +// @param {Avatar} [options.avatar=MyAvatar] - Avatar used to retrieve position and joint data. +// @param {bool} [options.mirrored=true] - Apply "symmetric mirroring" of Left/Right joints. +// @param {bool} [options.autoUpdate=true] - Automatically sync joint data. +function Doppleganger(options) { + this.options = options = options || {}; + this.avatar = options.avatar || MyAvatar; + this.mirrored = 'mirrored' in options ? options.mirrored : true; + this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; + + // @public + this.active = false; // whether doppleganger is currently being displayed/updated + this.objectID = null; // current doppleganger's Overlay or Entity id + this.frame = 0; // current joint update frame + + // @signal - emitted when .active state changes + this.activeChanged = utils.signal(function(active, reason) {}); + // @signal - emitted once model is either loaded or errors out + this.modelLoaded = utils.signal(function(error, result){}); + // @signal - emitted each time the model's joint data has been synchronized + this.jointsUpdated = utils.signal(function(objectID){}); +} + +Doppleganger.prototype = { + // @public @method - toggles doppleganger on/off + toggle: function() { + if (this.active) { + debugPrint('toggling off'); + this.stop(); + } else { + debugPrint('toggling on'); + this.start(); + } + return this.active; + }, + + // @public @method - synchronize the joint data between Avatar / doppleganger + update: function() { + this.frame++; + try { + if (!this.objectID) { + throw new Error('!this.objectID'); + } + + if (this.avatar.skeletonModelURL !== this.skeletonModelURL) { + return this.stop('avatar_changed'); + } + + var rotations = this.avatar.getJointRotations(); + var translations = this.avatar.getJointTranslations(); + var size = rotations.length; + + // note: this mismatch can happen when the avatar's model is actively changing + if (size !== translations.length || + (this.jointStateCount && size !== this.jointStateCount)) { + debugPrint('mismatched joint counts (avatar model likely changed)', size, translations.length, this.jointStateCount); + this.stop('avatar_changed_joints'); + return; + } + this.jointStateCount = size; + + if (this.mirrored) { + var mirroredIndexes = this.mirroredIndexes; + var outRotations = new Array(size); + var outTranslations = new Array(size); + for (var i=0; i < size; i++) { + var index = mirroredIndexes[i]; + if (index < 0 || index === false) { + index = i; + } + var rot = rotations[index]; + var trans = translations[index]; + trans.x *= -1; + rot.y *= -1; + rot.z *= -1; + outRotations[i] = rot; + outTranslations[i] = trans; + } + rotations = outRotations; + translations = outTranslations; + } + var jointUpdates = { + jointRotations: rotations, + jointTranslations: translations + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); + } catch (e) { + log('.update error: '+ e, index, e.stack); + this.stop('update_error'); + throw e; + } + }, + + // @public @method - show the doppleganger (and start the update thread, if options.autoUpdate was specified). + // @param {vec3} [options.position=(in front of avatar)] - starting position + // @param {quat} [options.orientation=avatar.orientation] - starting orientation + start: function(options) { + options = utils.assign(this.options, options); + if (this.objectID) { + log('start() called but object model already exists', this.objectID); + return; + } + var avatar = this.avatar; + if (!avatar.jointNames.length) { + return this.stop('joints_unavailable'); + } + + this.frame = 0; + var localPosition = Vec3.multiply(2, Quat.getForward(avatar.orientation)); + this.position = options.position || Vec3.sum(avatar.position, localPosition); + this.orientation = options.orientation || avatar.orientation; + this.skeletonModelURL = avatar.skeletonModelURL; + this.scale = avatar.scale || 1.0; + this.jointStateCount = 0; + this.jointNames = avatar.jointNames; + this.type = options.type || 'overlay'; + this.mirroredNames = modelHelper.deriveMirroredJointNames(this.jointNames); + this.mirroredIndexes = this.mirroredNames.map(function(name) { + return name ? avatar.getJointIndex(name) : false; + }); + + this.objectID = modelHelper.addObject({ + type: this.type === 'overlay' ? 'model' : 'Model', + modelURL: this.skeletonModelURL, + position: this.position, + rotation: this.orientation, + scale: this.scale + }); + Script.scriptEnding.connect(this, function() { + modelHelper.deleteObject(this.objectID); + }); + debugPrint('doppleganger created; objectID =', this.objectID); + + // trigger clean up (and stop updates) if the object gets deleted + this.onObjectDeleted = function(uuid) { + if (uuid === this.objectID) { + log('onObjectDeleted', uuid); + this.stop('object_deleted'); + } + }; + modelHelper.objectDeleted.connect(this, 'onObjectDeleted'); + + if ('onLoadComplete' in avatar) { + // stop the current doppleganger if Avatar loads a different model URL + this.onLoadComplete = function() { + if (avatar.skeletonModelURL !== this.skeletonModelURL) { + this.stop('avatar_changed_load'); + } + }; + avatar.onLoadComplete.connect(this, 'onLoadComplete'); + } + + this.onModelLoaded = function(error, result) { + if (error) { + return this.stop(error); + } + debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); + var props = { visible: true }; + if (naturalDimensions) { + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); + } + debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); + modelHelper.editObject(this.objectID, props); + if (!options.position) { + this.syncVerticalPosition(); + } + if (this.autoUpdate) { + this._createUpdateThread(); + } + }; + + this._resource = ModelCache.prefetch(this.skeletonModelURL); + this._modelReadier = new ModelReadyWatcher({ + resource: this._resource, + objectID: this.objectID + }); + this._modelReadier.modelReady.connect(this, 'onModelLoaded'); + this.activeChanged(this.active = true, 'start'); + }, + + // @public @method - hide the doppleganger + // @param {String} [reason=stop] - the reason stop was called + stop: function(reason) { + reason = reason || 'stop'; + if (this.onUpdate) { + Script.update.disconnect(this, 'onUpdate'); + delete this.onUpdate; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = undefined; + } + if (this.onObjectDeleted) { + modelHelper.objectDeleted.disconnect(this, 'onObjectDeleted'); + delete this.onObjectDeleted; + } + if (this.onLoadComplete) { + this.avatar.onLoadComplete.disconnect(this, 'onLoadComplete'); + delete this.onLoadComplete; + } + if (this.onModelLoaded) { + this._modelReadier && this._modelReadier.modelReady.disconnect(this, 'onModelLoaded'); + this._modelReadier = this.onModelLoaded = undefined; + } + if (this.objectID) { + modelHelper.deleteObject(this.objectID); + this.objectID = undefined; + } + if (this.active) { + this.activeChanged(this.active = false, reason); + } else if (reason) { + debugPrint('already stopped so not triggering another activeChanged; latest reason was:', reason); + } + }, + // @public @method - Reposition the doppleganger so it sees "eye to eye" with the Avatar. + // @param {String} [byJointName=Hips] - the reference joint used to align the Doppleganger and Avatar + syncVerticalPosition: function(byJointName) { + byJointName = byJointName || 'Hips'; + var positions = modelHelper.getJointPositions(this.objectID), + properties = modelHelper.getProperties(this.objectID), + dopplePosition = properties.position, + doppleJointIndex = modelHelper.getJointIndex(this.objectID, byJointName),// names.indexOf(byJointName), + doppleJointPosition = positions[doppleJointIndex]; + + debugPrint('........... doppleJointPosition', JSON.stringify({ + byJointName: byJointName, + dopplePosition: dopplePosition, + doppleJointIndex: doppleJointIndex, + doppleJointPosition: doppleJointPosition, + properties: properties.type, + positions: positions[0] + },0,2)); + var avatarPosition = this.avatar.position, + avatarJointIndex = this.avatar.getJointIndex(byJointName), + avatarJointPosition = this.avatar.getJointPosition(avatarJointIndex); + + var offset = (avatarJointPosition.y - doppleJointPosition.y); + debugPrint('adjusting for offset', offset); + if (properties.type === 'model') { + dopplePosition.y = avatarPosition.y + offset; + } else { + dopplePosition.y = avatarPosition.y - offset; + } + + this.position = dopplePosition; + modelHelper.editObject(this.objectID, { position: this.position }); + }, + + // @private @method - creates the update thread to synchronize joint data + _createUpdateThread: function() { + if (Doppleganger.USE_SCRIPT_UPDATE) { + debugPrint('creating Script.update thread'); + this.onUpdate = this.update; + Script.update.connect(this, 'onUpdate'); + } else { + debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); + var timeout = 1000 / Doppleganger.TARGET_FPS; + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); + } + } + +}; + +// @function - debug logging +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + Doppleganger.WANT_DEBUG && log.apply(this, arguments); +} + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +// doppleganger-app.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// This Client script creates an instance of a Doppleganger that can be toggled on/off via tablet button. +// (for more info see doppleganger.js) +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ + +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (false) { + require = Script.require; +} + +var VERSION = '0.0.0'; +var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); + +var DopplegangerClass = __webpack_require__(2), + DopplegangerAttachments = __webpack_require__(4), + DebugControls = __webpack_require__(5), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: __webpack_require__(6), + activeIcon: __webpack_require__(7), +}); + +var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), + button = tablet.addButton(buttonConfig); + +Script.scriptEnding.connect(function() { + tablet.removeButton(button); + button = null; +}); + +var doppleganger = new DopplegangerClass({ + avatar: MyAvatar, + mirrored: false, + autoUpdate: true, + type: 'overlay' +}); + +// add support for displaying regular (non-soft) attachments on the doppleganger +{ + var RECHECK_ATTACHMENT_MS = 1000; + var dopplegangerAttachments = new DopplegangerAttachments(doppleganger), + attachmentInterval = null, + lastHash = dopplegangerAttachments.getAttachmentsHash(); + + // monitor for attachment changes, but only when the doppleganger is active + doppleganger.activeChanged.connect(function(active, reason) { + if (attachmentInterval) { + Script.clearInterval(attachmentInterval); + } + if (active) { + attachmentInterval = Script.setInterval(checkAttachmentsHash, RECHECK_ATTACHMENT_MS); + } else { + attachmentInterval = null; + } + }); + function checkAttachmentsHash() { + var currentHash = dopplegangerAttachments.getAttachmentsHash(); + if (currentHash !== lastHash) { + lastHash = currentHash; + debugPrint('app-doppleganger | detect attachment change'); + dopplegangerAttachments.refreshAttachments(); + } + } +} + +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + +// hide the doppleganger if this client script is unloaded +Script.scriptEnding.connect(doppleganger, 'stop'); + +// hide the doppleganger if the user switches domains (which might place them arbitrarily far away in world space) +function onDomainChanged() { + if (doppleganger.active) { + doppleganger.stop('domain_changed'); + } +} +Window.domainChanged.connect(onDomainChanged); +Window.domainConnectionRefused.connect(onDomainChanged); +Script.scriptEnding.connect(function() { + Window.domainChanged.disconnect(onDomainChanged); + Window.domainConnectionRefused.disconnect(onDomainChanged); +}); + +// toggle on/off via tablet button +button.clicked.connect(doppleganger, 'toggle'); + +// highlight tablet button based on current doppleganger state +doppleganger.activeChanged.connect(function(active, reason) { + if (button) { + button.editProperties({ isActive: active }); + debugPrint('doppleganger.activeChanged', active, reason); + } +}); + +// alert the user if there was an error applying their skeletonModelURL +doppleganger.modelLoaded.connect(function(error, result) { + if (doppleganger.active && error) { + Window.alert('doppleganger | ' + error + '\n' + doppleganger.skeletonModelURL); + } +}); + +// ---------------------------------------------------------------------------- + +// add debug indicators, but only if the user has configured the settings value +if (Settings.getValue('debug.doppleganger', false)) { + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + WANT_DEBUG && log.apply(this, arguments); +} + +// ---------------------------------------------------------------------------- + +UserActivityLogger.logAction('doppleganger_app_load'); +doppleganger.activeChanged.connect(function(active, reason) { + if (active) { + UserActivityLogger.logAction('doppleganger_enable'); + } else { + if (reason === 'stop') { + // user intentionally toggled the doppleganger + UserActivityLogger.logAction('doppleganger_disable'); + } else { + debugPrint('doppleganger stopped:', reason); + UserActivityLogger.logAction('doppleganger_autodisable', { reason: reason }); + } + } +}); +dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { + UserActivityLogger.logAction('doppleganger_attachments', { count: attachments.length }); +}); + + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } +module.exports = DopplegangerAttachments; + +DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.WANT_DEBUG = false; + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); +} +log(DopplegangerAttachments.version); + +function debugPrint() { + DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); +} + +function DopplegangerAttachments(doppleganger, options) { + utils.assign(this, { + _options: options, + doppleganger: doppleganger, + attachments: undefined, + manualJointSync: true, + attachmentsUpdated: utils.signal(function attachmentsUpdated(currentAttachments, previousAttachments){}), + }); + this._initialize(); + debugPrint('DopplegangerAttachments...', JSON.stringify(options)); +} +DopplegangerAttachments.prototype = { + // "hash" the current attachments (so that changes can be detected) + getAttachmentsHash: function() { + return JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant()); + }, + // create a pure Object copy of the current native attachments variant + _cloneAttachmentsVariant: function() { + return JSON.parse(JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant())); + }, + // fetch and resolve attachments to include jointIndex and other relevant $metadata + _getResolvedAttachments: function() { + var attachments = this._cloneAttachmentsVariant(), + objectID = this.doppleganger.objectID; + function toString() { + return '[attachment #' + this.$index + ' ' + this.$path + ' @ ' + this.jointName + '{' + this.$jointIndex + '}]'; + } + return attachments.map(function(attachment, i) { + var jointIndex = modelHelper.getJointIndex(objectID, attachment.jointName), + path = (attachment.modelUrl+'').split(/[?#]/)[0].split('/').slice(-3).join('/'); + return Object.defineProperties(attachment, { + $hash: { value: JSON.stringify(attachment) }, + $index: { value: i }, + $jointIndex: { value: jointIndex }, + $path: { value: path }, + toString: { value: toString }, + }); + }); + }, + // compare before / after attachment sets to determine which ones need to be (re)created + refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } + var before = this.attachments || [], + beforeIndex = before.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + var after = this._getResolvedAttachments(), + afterIndex = after.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + + Object.keys(beforeIndex).concat(Object.keys(afterIndex)).forEach(function(hash) { + if (hash in beforeIndex && hash in afterIndex) { + // print('reusing previous attachment', hash); + after[afterIndex[hash]] = before[beforeIndex[hash]]; + } else if (!(hash in afterIndex)) { + var attachment = before[beforeIndex[hash]]; + attachment.properties && attachment.properties.objectID && + modelHelper.deleteObject(attachment.properties.objectID); + delete attachment.properties; + } + }); + this.attachments = after; + this._createAttachmentObjects(); + this.attachmentsUpdated(after, before); + }, + _createAttachmentObjects: function() { + try { + var attachments = this.attachments, + parentID = this.doppleganger.objectID, + jointNames = this.doppleganger.jointNames, + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); + debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ + modelType: modelType, + attachments: attachments.length, + parentID: parentID, + jointNames: jointNames.join(' | '), + },0,2)); + return attachments.map(utils.bind(this, function(attachment, i) { + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); + return attachment; + } + var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), + scale = this.doppleganger.avatar.scale * (attachment.scale||1.0); + + attachment.properties = utils.assign({ + name: attachment.toString(), + type: modelType, + modelURL: attachment.modelUrl, + scale: scale, + dimensions: modelHelper.type(parentID) === 'entity' ? + Vec3.multiply(attachment.scale||1.0, Vec3.ONE) : undefined, + visible: false, + collisionless: true, + dynamic: false, + shapeType: 'none', + lifetime: 60, + grabbable: true, + }, !this.manualJointSync && { + parentID: parentID, + parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), + }); + var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); + attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); + attachment._modelReadier = new ModelReadyWatcher({ + resource: attachment._resource, + objectID: objectID, + }); + this.doppleganger.activeChanged.connect(attachment._modelReadier, '_stop'); + + attachment._modelReadier.modelReady.connect(this, function(err, result) { + if (err) { + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); + modelHelper.deleteObject(objectID); + return objectID = null; + } + debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); + var properties = modelHelper.getProperties(result.objectID), + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { + dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, + }); + this.onJointsUpdated(parentID); // trigger once to initialize position/rotation + // give time for model overlay to "settle", then make it visible + Script.setTimeout(utils.bind(this, function() { + modelHelper.editObject(objectID, { + visible: true, + }); + attachment._loaded = true; + }), 100); + }); + return attachment; + })); + } catch (e) { + log('_createAttachmentObjects ERROR:', e.stack || e, e.fileName, e.lineNumber); + } + }, + + _removeAttachmentObjects: function() { + if (this.attachments) { + this.attachments.forEach(function(attachment) { + if (attachment.properties) { + if (attachment.properties.objectID) { + modelHelper.deleteObject(attachment.properties.objectID); + } + delete attachment.properties.objectID; + } + }); + delete this.attachments; + } + }, + + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { + var jointOrientations = modelHelper.getJointOrientations(objectID), + jointPositions = modelHelper.getJointPositions(objectID), + parentID = objectID, + avatarScale = this.doppleganger.scale, + manualJointSync = this.manualJointSync; + + if (!this.attachments) { + this.refreshAttachments(); + } + var updatedObjects = this.attachments.reduce(function(updates, attachment, i) { + if (!attachment.properties || !attachment._loaded) { + return updates; + } + var objectID = attachment.properties.objectID, + jointIndex = attachment.$jointIndex, + jointOrientation = jointOrientations[jointIndex], + jointPosition = jointPositions[jointIndex]; + + var translation = Vec3.multiply(avatarScale, attachment.translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + localRotation = rotation; + + updates[objectID] = manualJointSync ? { + visible: true, + position: Vec3.sum(jointPosition, localPosition), + rotation: Quat.multiply(jointOrientation, localRotation), + scale: avatarScale * attachment.scale, + } : { + visible: true, + parentID: parentID, + parentJointIndex: jointIndex, + localRotation: localRotation, + localPosition: localPosition, + scale: attachment.scale, + }; + return updates; + }, {}); + modelHelper.editObjects(updatedObjects); + }, + + _initialize: function() { + var doppleganger = this.doppleganger; + if ('$attachmentControls' in doppleganger) { + throw new Error('only one set of attachment controls can be added per doppleganger'); + } + doppleganger.$attachmentControls = this; + doppleganger.activeChanged.connect(this, function(active) { + if (active) { + doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } else { + doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + this._removeAttachmentObjects(); + } + }); + + Script.scriptEnding.connect(this, '_removeAttachmentObjects'); + }, +}; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = __webpack_require__(2), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index a3b3873c2d..5dee264fae 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -1,10 +1,12 @@ "use strict"; /* eslint-env commonjs */ /* eslint-disable comma-dangle */ +/* global console */ +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.0'; +DopplegangerAttachments.version = '0.0.1a'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -13,8 +15,10 @@ var _modelHelper = require('./model-helper.js'), utils = require('./utils.js'); function log() { - print('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); } +log(DopplegangerAttachments.version); function debugPrint() { DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); @@ -61,6 +65,9 @@ DopplegangerAttachments.prototype = { }, // compare before / after attachment sets to determine which ones need to be (re)created refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } var before = this.attachments || [], beforeIndex = before.reduce(function(out, att, index) { out[att.$hash] = index; return out; @@ -90,18 +97,19 @@ DopplegangerAttachments.prototype = { var attachments = this.attachments, parentID = this.doppleganger.objectID, jointNames = this.doppleganger.jointNames, - properties = modelHelper.getProperties(this.doppleganger.objectID); - + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ - type: properties.type, + modelType: modelType, attachments: attachments.length, parentID: parentID, jointNames: jointNames.join(' | '), },0,2)); return attachments.map(utils.bind(this, function(attachment, i) { - var type = modelHelper.type(attachment.properties && attachment.properties.objectID); - if (type === 'overlay') { - debugPrint('skipping already-provisioned attachment object', type, attachment.properties && attachment.properties.name); + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); return attachment; } var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), @@ -109,7 +117,7 @@ DopplegangerAttachments.prototype = { attachment.properties = utils.assign({ name: attachment.toString(), - type: properties.type, + type: modelType, modelURL: attachment.modelUrl, scale: scale, dimensions: modelHelper.type(parentID) === 'entity' ? @@ -123,10 +131,13 @@ DopplegangerAttachments.prototype = { }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), }); var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); - attachment._modelReadier = new ModelReadyWatcher( { + attachment._modelReadier = new ModelReadyWatcher({ resource: attachment._resource, objectID: objectID, }); @@ -134,21 +145,23 @@ DopplegangerAttachments.prototype = { attachment._modelReadier.modelReady.connect(this, function(err, result) { if (err) { - log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.modelUrl); + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); modelHelper.deleteObject(objectID); return objectID = null; } debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', - result.jointNames && result.jointNames.length, result.naturalDimensions, result.objectID); + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); var properties = modelHelper.getProperties(result.objectID), - naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions; - modelHelper.editObject(result.objectID, { + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, }); this.onJointsUpdated(parentID); // trigger once to initialize position/rotation // give time for model overlay to "settle", then make it visible Script.setTimeout(utils.bind(this, function() { - modelHelper.editObject(result.objectID, { + modelHelper.editObject(objectID, { visible: true, }); attachment._loaded = true; @@ -175,7 +188,7 @@ DopplegangerAttachments.prototype = { } }, - onJointsUpdated: function onJointsUpdated(objectID) { + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { var jointOrientations = modelHelper.getJointOrientations(objectID), jointPositions = modelHelper.getJointPositions(objectID), parentID = objectID, @@ -195,8 +208,9 @@ DopplegangerAttachments.prototype = { jointPosition = jointPositions[jointIndex]; var translation = Vec3.multiply(avatarScale, attachment.translation), - rotation = Quat.fromVec3Degrees(attachment.rotation), - localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), localRotation = rotation; updates[objectID] = manualJointSync ? { @@ -212,7 +226,6 @@ DopplegangerAttachments.prototype = { localPosition: localPosition, scale: attachment.scale, }; - onJointsUpdated[objectID] = updates[objectID]; return updates; }, {}); modelHelper.editObjects(updatedObjects); @@ -221,7 +234,7 @@ DopplegangerAttachments.prototype = { _initialize: function() { var doppleganger = this.doppleganger; if ('$attachmentControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); + throw new Error('only one set of attachment controls can be added per doppleganger'); } doppleganger.$attachmentControls = this; doppleganger.activeChanged.connect(this, function(active) { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js new file mode 100644 index 0000000000..b0a83117fb --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js @@ -0,0 +1,158 @@ +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + +"use strict"; +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = require('./doppleganger.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js index bebd36df45..190a8aa69e 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js @@ -10,6 +10,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module doppleganger // // This module contains the `Doppleganger` class implementation for creating an inspectable replica of @@ -24,9 +25,13 @@ module.exports = Doppleganger; +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + var _modelHelper = require('./model-helper.js'), modelHelper = _modelHelper.modelHelper, - ModelReadyWatcher = _modelHelper.ModelReadyWatcher; + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = require('./utils.js'); // @property {bool} - toggle verbose debug logging on/off Doppleganger.WANT_DEBUG = false; @@ -48,16 +53,16 @@ function Doppleganger(options) { this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; // @public - this.active = false; // whether doppleganger is currently being displayed/updated + this.active = false; // whether doppleganger is currently being displayed/updated this.objectID = null; // current doppleganger's Overlay or Entity id - this.frame = 0; // current joint update frame + this.frame = 0; // current joint update frame // @signal - emitted when .active state changes - this.activeChanged = signal(function(active, reason) {}); + this.activeChanged = utils.signal(function(active, reason) {}); // @signal - emitted once model is either loaded or errors out - this.modelLoaded = signal(function(error, result){}); + this.modelLoaded = utils.signal(function(error, result){}); // @signal - emitted each time the model's joint data has been synchronized - this.jointsUpdated = signal(function(objectID){}); + this.jointsUpdated = utils.signal(function(objectID){}); } Doppleganger.prototype = { @@ -118,11 +123,12 @@ Doppleganger.prototype = { rotations = outRotations; translations = outTranslations; } - modelHelper.editObject(this.objectID, { + var jointUpdates = { jointRotations: rotations, jointTranslations: translations - }); - this.jointsUpdated(this.objectID); + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); } catch (e) { log('.update error: '+ e, index, e.stack); this.stop('update_error'); @@ -134,7 +140,7 @@ Doppleganger.prototype = { // @param {vec3} [options.position=(in front of avatar)] - starting position // @param {quat} [options.orientation=avatar.orientation] - starting orientation start: function(options) { - options = assign(this.options, options); + options = utils.assign(this.options, options); if (this.objectID) { log('start() called but object model already exists', this.objectID); return; @@ -194,11 +200,11 @@ Doppleganger.prototype = { return this.stop(error); } debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); - var naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); var props = { visible: true }; if (naturalDimensions) { - props.dimensions = Vec3.multiply(this.scale, naturalDimensions); + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); } debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); modelHelper.editObject(this.objectID, props); @@ -296,220 +302,18 @@ Doppleganger.prototype = { } else { debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); var timeout = 1000 / Doppleganger.TARGET_FPS; - this._interval = Script.setInterval(bind(this, 'update'), timeout); + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); } } }; -// @function - bind a function to a `this` context -// @param {Object} - the `this` context -// @param {Function|String} - function or method name -function bind(thiz, method) { - method = thiz[method] || method; - return function() { - return method.apply(thiz, arguments); - }; -} - -// @function - Qt signal polyfill -function signal(template) { - var callbacks = []; - return Object.defineProperties(function() { - var args = [].slice.call(arguments); - callbacks.forEach(function(obj) { - obj.handler.apply(obj.scope, args); - }); - }, { - connect: { value: function(scope, handler) { - var callback = {scope: scope, handler: scope[handler] || handler || scope}; - if (!callback.handler || !callback.handler.apply) { - throw new Error('invalid arguments to connect:' + [template, scope, handler]); - } - callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); - }}, - disconnect: { value: function(scope, handler) { - var match = {scope: scope, handler: scope[handler] || handler || scope}; - callbacks = callbacks.filter(function(obj) { - return !(obj.scope === match.scope && obj.handler === match.handler); - }); - }} - }); -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill -/* eslint-disable */ -function assign(target, varArgs) { // .length of function is 2 - 'use strict'; - if (target == null) { // TypeError if undefined or null - throw new TypeError('Cannot convert undefined or null to object'); - } - - var to = Object(target); - - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - - if (nextSource != null) { // Skip over if undefined or null - for (var nextKey in nextSource) { - // Avoid bugs when hasOwnProperty is shadowed - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; -} -/* eslint-enable */ -// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill - // @function - debug logging function log() { - print('doppleganger | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { Doppleganger.WANT_DEBUG && log.apply(this, arguments); } - -// -- ADVANCED DEBUGGING -- -// @function - Add debug joint indicators / extra debugging info. -// @param {Doppleganger} - existing Doppleganger instance to add controls to -// -// @note: -// * rightclick toggles mirror mode on/off -// * shift-rightclick toggles the debug indicators on/off -// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. -// -// Example use: -// var doppleganger = new Doppleganger(); -// Doppleganger.addDebugControls(doppleganger); -Doppleganger.addDebugControls = function(doppleganger) { - DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; - DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; - - function DebugControls() { - this.enableIndicators = true; - this.selectedJointName = null; - this.debugOverlayIDs = undefined; - this.jointSelected = signal(function(result) {}); - } - DebugControls.prototype = { - start: function() { - if (!this.onMousePressEvent) { - this.onMousePressEvent = this._onMousePressEvent; - Controller.mousePressEvent.connect(this, 'onMousePressEvent'); - } - }, - - stop: function() { - this.removeIndicators(); - if (this.onMousePressEvent) { - Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); - delete this.onMousePressEvent; - } - }, - - createIndicators: function(jointNames) { - this.jointNames = jointNames; - return jointNames.map(function(name, i) { - return Overlays.addOverlay('shape', { - shape: 'Icosahedron', - scale: 0.1, - solid: false, - alpha: 0.5 - }); - }); - }, - - removeIndicators: function() { - if (this.debugOverlayIDs) { - this.debugOverlayIDs.forEach(Overlays.deleteOverlay); - this.debugOverlayIDs = undefined; - } - }, - - onJointsUpdated: function(overlayID) { - if (!this.enableIndicators) { - return; - } - var jointNames = Overlays.getProperty(overlayID, 'jointNames'), - jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), - jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), - selectedIndex = jointNames.indexOf(this.selectedJointName); - - if (!this.debugOverlayIDs) { - this.debugOverlayIDs = this.createIndicators(jointNames); - } - - // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) - var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { - updates[id] = { - position: jointPositions[i], - rotation: jointOrientations[i], - color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, - solid: i === selectedIndex - }; - return updates; - }, {}); - Overlays.editOverlays(updatedOverlays); - }, - - _onMousePressEvent: function(evt) { - if (!evt.isLeftButton || !this.enableIndicators || !this.debugOverlayIDs) { - return; - } - var ray = Camera.computePickRay(evt.x, evt.y), - hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); - - hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); - hit.jointName = this.jointNames[hit.jointIndex]; - this.jointSelected(hit); - } - }; - - if ('$debugControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); - } - var debugControls = new DebugControls(); - doppleganger.$debugControls = debugControls; - - function onMousePressEvent(evt) { - if (evt.isRightButton) { - if (evt.isShifted) { - debugControls.enableIndicators = !debugControls.enableIndicators; - if (!debugControls.enableIndicators) { - debugControls.removeIndicators(); - } - } else { - doppleganger.mirrored = !doppleganger.mirrored; - } - } - } - - doppleganger.activeChanged.connect(function(active) { - if (active) { - debugControls.start(); - doppleganger.jointsUpdated.connect(debugControls, 'onJointsUpdated'); - Controller.mousePressEvent.connect(onMousePressEvent); - } else { - Controller.mousePressEvent.disconnect(onMousePressEvent); - doppleganger.jointsUpdated.disconnect(debugControls, 'onJointsUpdated'); - debugControls.stop(); - } - }); - - debugControls.jointSelected.connect(function(hit) { - debugControls.selectedJointName = hit.jointName; - if (hit.jointIndex < 0) { - return; - } - hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; - log('selected joint:', JSON.stringify(hit, 0, 2)); - }); - - Script.scriptEnding.connect(debugControls, 'removeIndicators'); - - return doppleganger; -}; diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/makefile b/unpublishedScripts/marketplace/doppleganger-attachments/makefile new file mode 100644 index 0000000000..eaf02dbed9 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/makefile @@ -0,0 +1,11 @@ +all: + @echo "make dist" + +dist: doppleganger-a.svg.json doppleganger-i.svg.json dist/app-doppleganger-marketplace.js + @echo "OK" + +%.svg.json: %.svg + cat $< | jq -sR '"data:image/svg+xml;xml,"+.' > $@ + +dist/app-doppleganger-marketplace.js: *.js + ./node_modules/.bin/webpack --verbose app-doppleganger-attachments.js $@ diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js index 2dda2c12ec..8edaf50cb7 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js @@ -8,6 +8,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module model-helper // // This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and @@ -18,10 +19,16 @@ var utils = require('./utils.js'), assert = utils.assert; module.exports = { - version: '0.0.0', + version: '0.0.1', ModelReadyWatcher: ModelReadyWatcher }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + var _objectDeleted = utils.signal(function objectDeleted(objectID){}); // proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal var objectDeleted = utils.assign(function objectDeleted(objectID){}, { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/package.json b/unpublishedScripts/marketplace/doppleganger-attachments/package.json new file mode 100644 index 0000000000..f18136fc1b --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "webpack": "^3.0.0" + } +} diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/readme.md b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md new file mode 100644 index 0000000000..d0ebf2d6e1 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md @@ -0,0 +1,4 @@ +note: to rebuild webpack version: +* install `jq` https://stedolan.github.io/jq (used to encode the icon.svg's as Data URI JSON strings) +* `npm install` +* `make dist` diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js index 76c6e1ef7f..b34af1e632 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js @@ -1,12 +1,20 @@ /* eslint-env commonjs */ +/* global console */ module.exports = { + version: '0.0.1', bind: bind, signal: signal, assign: assign, assert: assert }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + // @function - bind a function to a `this` context // @param {Object} - the `this` context // @param {Function|String} - function or method name From 96f52a9812bd77d3931c46c03a91883a73002147 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:05:16 -0700 Subject: [PATCH 036/280] First pass at getting overlays to show up on hover --- interface/src/Application.cpp | 2 +- .../src/ui/overlays/HoverOverlayInterface.cpp | 72 +++++++++++++++++++ .../src/ui/overlays}/HoverOverlayInterface.h | 17 ++++- .../src/ui/overlays}/HoverOverlayLogging.cpp | 2 +- .../src/ui/overlays}/HoverOverlayLogging.h | 2 +- .../src/EntityTreeRenderer.cpp | 7 -- .../entities/src/HoverOverlayInterface.cpp | 30 -------- 7 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayInterface.h (67%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.cpp (92%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.h (93%) delete mode 100644 libraries/entities/src/HoverOverlayInterface.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c73d6614f1..bc9fa25af0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include +#include "ui/overlays/HoverOverlayInterface.h" #include #include #include diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp new file mode 100644 index 0000000000..8576c0b622 --- /dev/null +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -0,0 +1,72 @@ +// +// HoverOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HoverOverlayInterface.h" +#include "Application.h" +#include + +HoverOverlayInterface::HoverOverlayInterface() { + // "hover_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { + _hoverOverlay = std::make_shared(); + _hoverOverlay->setAlpha(1.0f); + _hoverOverlay->setBorderSize(1.0f); + _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); + _hoverOverlay->setIsSolid(true); + _hoverOverlay->setPulseMin(0.5); + _hoverOverlay->setPulseMax(1.0); + _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setIgnoreRayIntersection(false); + _hoverOverlay->setDrawInFront(false); + _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); + } + + _hoverOverlay->setPosition(entityProperties.getPosition()); + _hoverOverlay->setRotation(entityProperties.getRotation()); + _hoverOverlay->setVisible(true); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(QUuid()); + + qApp->getOverlays().deleteOverlay(_hoverOverlayID); + _hoverOverlay = NULL; + _hoverOverlayID = UNKNOWN_OVERLAY_ID; +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); +} diff --git a/libraries/entities/src/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h similarity index 67% rename from libraries/entities/src/HoverOverlayInterface.h rename to interface/src/ui/overlays/HoverOverlayInterface.h index 1e56cc03dd..8322c3e75c 100644 --- a/libraries/entities/src/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -1,6 +1,6 @@ // // HoverOverlayInterface.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-14. // Copyright 2017 High Fidelity, Inc. @@ -18,14 +18,24 @@ #include #include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Overlays.h" #include "EntityTree.h" #include "HoverOverlayLogging.h" +/**jsdoc +* @namespace HoverOverlay +*/ class HoverOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); @@ -34,11 +44,14 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyHoverOverlay(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; - QUuid _currentHoveredEntity{}; + QUuid _currentHoveredEntity {}; + }; #endif // hifi_HoverOverlayInterface_h diff --git a/libraries/entities/src/HoverOverlayLogging.cpp b/interface/src/ui/overlays/HoverOverlayLogging.cpp similarity index 92% rename from libraries/entities/src/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/HoverOverlayLogging.cpp index 99a2dff782..69c977a58c 100644 --- a/libraries/entities/src/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/HoverOverlayLogging.cpp @@ -1,6 +1,6 @@ // // HoverOverlayLogging.cpp -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities/src/HoverOverlayLogging.h b/interface/src/ui/overlays/HoverOverlayLogging.h similarity index 93% rename from libraries/entities/src/HoverOverlayLogging.h rename to interface/src/ui/overlays/HoverOverlayLogging.h index f0a024bba9..965a1c6d03 100644 --- a/libraries/entities/src/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/HoverOverlayLogging.h @@ -1,6 +1,6 @@ // // HoverOverlayLogging.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 475471b0fb..a8eca41077 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include "RenderableEntityItem.h" @@ -453,8 +452,6 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) { - auto hoverOverlayInterface = DependencyManager::get().data(); - connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); @@ -464,12 +461,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity); connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); - connect(this, &EntityTreeRenderer::hoverEnterEntity, hoverOverlayInterface, &HoverOverlayInterface::createHoverOverlay); - connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, hoverOverlayInterface, &HoverOverlayInterface::destroyHoverOverlay); connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); diff --git a/libraries/entities/src/HoverOverlayInterface.cpp b/libraries/entities/src/HoverOverlayInterface.cpp deleted file mode 100644 index 85b2738fc4..0000000000 --- a/libraries/entities/src/HoverOverlayInterface.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// HoverOverlayInterface.cpp -// libraries/entities/src -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); -} From 67c8ddfd57c0861af35209f5269395f3405b700a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:09:40 -0700 Subject: [PATCH 037/280] Merge in changes from laser PR --- interface/src/Application.cpp | 2 ++ scripts/system/controllers/handControllerGrab.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc9fa25af0..327643df1d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2124,6 +2124,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5826,6 +5827,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); + scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 04921fe14d..78c4b2960e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var entitiesWithHoverOverlays = []; + var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2201,6 +2203,15 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID); + entitiesWithHoverOverlays.push(rayPickInfo.entityID); + } + var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); @@ -3763,6 +3774,11 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 2b1a0921da5d0961c446064cd7ecf6d16059a2b0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:01:16 -0700 Subject: [PATCH 038/280] Initial work on clickable overlays --- interface/src/Application.cpp | 9 ++++- .../src/ui/overlays/HoverOverlayInterface.cpp | 10 +++-- .../src/ui/overlays/HoverOverlayInterface.h | 2 +- interface/src/ui/overlays/Overlays.cpp | 12 +++--- interface/src/ui/overlays/Overlays.h | 12 +++--- .../system/controllers/handControllerGrab.js | 39 +++++++++++++------ 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 327643df1d..b9a76ef58c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1323,12 +1323,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Keyboard focus handling for Web overlays. auto overlays = &(qApp->getOverlays()); - connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) { + connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); }); - connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) { + connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { if (overlayID == _keyboardFocusedOverlay.get()) { setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); } @@ -1343,6 +1343,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); + connect(overlays, + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 8576c0b622..655f167074 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -54,10 +54,6 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, _hoverOverlay->setVisible(true); } -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); -} - void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; setCurrentHoveredEntity(QUuid()); @@ -70,3 +66,9 @@ void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); } + +void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _hoverOverlayID) { + qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 8322c3e75c..7cda22a0c1 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -44,9 +44,9 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyHoverOverlay(const EntityItemID& entityItemID); + void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 72682fcb8c..b3ab4599a5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -705,27 +705,27 @@ bool Overlays::isAddedOverlay(OverlayID id) { return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); } -void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mousePressOnOverlay(overlayID, event); } -void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseReleaseOnOverlay(overlayID, event); } -void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseMoveOnOverlay(overlayID, event); } -void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverEnterOverlay(id, event); } -void Overlays::sendHoverOverOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverOverOverlay(id, event); } -void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverLeaveOverlay(id, event); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 100f853a96..3d9ac93c81 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -288,13 +288,13 @@ public slots: #endif - void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); + void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); - void sendHoverEnterOverlay(OverlayID id, PointerEvent event); - void sendHoverOverOverlay(OverlayID id, PointerEvent event); - void sendHoverLeaveOverlay(OverlayID id, PointerEvent event); + void sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event); OverlayID getKeyboardFocusOverlay(); void setKeyboardFocusOverlay(OverlayID id); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 78c4b2960e..dc9d2eb521 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entitiesWithHoverOverlays = []; +var entityWithHoverOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -1391,6 +1391,11 @@ function MyController(hand) { color: color, endParentID: farParentID }); + + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2203,13 +2208,21 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID); - entitiesWithHoverOverlays.push(rayPickInfo.entityID); + if (rayPickInfo.entityID) + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + } + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); + entityWithHoverOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3484,6 +3497,8 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); + //HoverOverlay + this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3774,10 +3789,10 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } if (this.grabbedThingID !== null) { From 084f4d2ab023a20b1dc0827122fb99696e7ed360 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:42:59 -0700 Subject: [PATCH 039/280] Why were Web3DOverlays done this way? --- interface/src/Application.cpp | 8 +++++-- interface/src/ui/overlays/Overlays.cpp | 31 +++++++++++++------------- interface/src/ui/overlays/Overlays.h | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9a76ef58c..eacd9bf794 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1324,8 +1324,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { - setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - setKeyboardFocusOverlay(overlayID); + auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); + // Only Web overlays can have focus. + if (thisOverlay) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + setKeyboardFocusOverlay(overlayID); + } }); connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b3ab4599a5..862152bea9 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,7 +818,7 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { @@ -875,12 +875,13 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + PointerEvent pointerEvent; + if (thisWeb3DOverlay) { + pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); } + emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mousePressOffOverlay(); return false; @@ -895,9 +896,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -914,9 +915,9 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { if (rayPickResult.intersects) { // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Release); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } } @@ -935,14 +936,14 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { // Only Web overlays can have focus. auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); // If previously hovering over a different overlay then leave hover on that overlay. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } } @@ -962,7 +963,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3d9ac93c81..7235937077 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From 265f978a06ff26c1a6645eed3d205146843c5e4a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 15:40:10 -0700 Subject: [PATCH 040/280] It's working! --- .../src/ui/overlays/HoverOverlayInterface.cpp | 16 +++---- .../src/ui/overlays/HoverOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 44 +++++++------------ 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 655f167074..1ccbbafb1b 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -36,16 +36,16 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); + _hoverOverlay = std::make_shared(); _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setBorderSize(1.0f); - _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); - _hoverOverlay->setIsSolid(true); - _hoverOverlay->setPulseMin(0.5); - _hoverOverlay->setPulseMax(1.0); - _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setPulseMin(0.75f); + _hoverOverlay->setPulseMax(1.0f); + _hoverOverlay->setColorPulse(1.0f); _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(false); + _hoverOverlay->setDrawInFront(true); + _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _hoverOverlay->setIsFacingAvatar(true); + _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); } diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 7cda22a0c1..4ae14ffd1d 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -19,7 +19,7 @@ #include #include #include "EntityScriptingInterface.h" -#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" #include "EntityTree.h" @@ -35,7 +35,7 @@ class HoverOverlayInterface : public QObject, public Dependency { QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index dc9d2eb521..7ee9a33485 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1391,11 +1391,6 @@ function MyController(hand) { color: color, endParentID: farParentID }); - - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; - } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2208,14 +2203,14 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID) + if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { if (entityWithHoverOverlay) { HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); } var pointerEvent = { type: "Move", id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, @@ -2479,27 +2474,24 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if (Overlays.getProperty(overlay, "type") != "web3d") { - return false; - } - if (Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; - - pointerEvent = { - type: "Move", - id: HARDWARE_MOUSE_ID, - pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); } + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && @@ -3497,8 +3489,6 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); - //HoverOverlay - this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3595,7 +3585,7 @@ function MyController(hand) { }; this.overlayLaserTouchingEnter = function () { - // Test for intersection between controller laser and Web overlay plane. + // Test for intersection between controller laser and overlay plane. var controllerLocation = getControllerWorldLocation(this.handToController(), true); var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation); if (intersectInfo) { From 8f6af3a1ab3bc0ee5c8746aa45d44d0b29980fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:11:43 -0700 Subject: [PATCH 041/280] ContextOverlay --- interface/src/Application.cpp | 14 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 74 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 57 ++++++++++++++ ...yLogging.cpp => ContextOverlayLogging.cpp} | 6 +- ...erlayLogging.h => ContextOverlayLogging.h} | 10 +-- .../src/ui/overlays/HoverOverlayInterface.cpp | 74 ------------------- .../src/ui/overlays/HoverOverlayInterface.h | 57 -------------- .../system/controllers/handControllerGrab.js | 18 ++--- 8 files changed, 155 insertions(+), 155 deletions(-) create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.cpp create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.h rename interface/src/ui/overlays/{HoverOverlayLogging.cpp => ContextOverlayLogging.cpp} (67%) rename interface/src/ui/overlays/{HoverOverlayLogging.h => ContextOverlayLogging.h} (62%) delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eacd9bf794..8689d17032 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include "ui/overlays/HoverOverlayInterface.h" +#include "ui/overlays/ContextOverlayInterface.h" #include #include #include @@ -589,7 +589,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -1325,7 +1325,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); - // Only Web overlays can have focus. + // Only Web overlays can have keyboard focus. if (thisOverlay) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); @@ -1349,8 +1349,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; @@ -2133,7 +2133,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); - surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); + surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5836,7 +5836,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); - scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp new file mode 100644 index 0000000000..cb15c544c7 --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -0,0 +1,74 @@ +// +// ContextOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ContextOverlayInterface.h" +#include "Application.h" +#include + +ContextOverlayInterface::ContextOverlayInterface() { + // "context_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); +} + +void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +} + +void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _contextOverlayID) { + qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h new file mode 100644 index 0000000000..05215e3e5c --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -0,0 +1,57 @@ +// +// ContextOverlayInterface.h +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_ContextOverlayInterface_h +#define hifi_ContextOverlayInterface_h + +#include +#include + +#include +#include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Overlays.h" + +#include "EntityTree.h" +#include "ContextOverlayLogging.h" + +/**jsdoc +* @namespace ContextOverlay +*/ +class ContextOverlayInterface : public QObject, public Dependency { + Q_OBJECT + + Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _contextOverlay { nullptr }; +public: + ContextOverlayInterface(); + + Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } + void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + +public slots: + void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID); + void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + +private: + bool _verboseLogging { true }; + QUuid _currentEntityWithContextOverlay{}; + +}; + +#endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/HoverOverlayLogging.cpp b/interface/src/ui/overlays/ContextOverlayLogging.cpp similarity index 67% rename from interface/src/ui/overlays/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/ContextOverlayLogging.cpp index 69c977a58c..c2c3fb7734 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/ContextOverlayLogging.cpp @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.cpp +// ContextOverlayLogging.cpp // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -9,6 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "HoverOverlayLogging.h" +#include "ContextOverlayLogging.h" -Q_LOGGING_CATEGORY(hover_overlay, "hifi.hover_overlay") +Q_LOGGING_CATEGORY(context_overlay, "hifi.context_overlay") diff --git a/interface/src/ui/overlays/HoverOverlayLogging.h b/interface/src/ui/overlays/ContextOverlayLogging.h similarity index 62% rename from interface/src/ui/overlays/HoverOverlayLogging.h rename to interface/src/ui/overlays/ContextOverlayLogging.h index 965a1c6d03..182ebc1425 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/ContextOverlayLogging.h @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.h +// ContextOverlayLogging.h // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -10,11 +10,11 @@ // #pragma once -#ifndef hifi_HoverOverlayLogging_h -#define hifi_HoverOverlayLogging_h +#ifndef hifi_ContextOverlayLogging_h +#define hifi_ContextOverlayLogging_h #include -Q_DECLARE_LOGGING_CATEGORY(hover_overlay) +Q_DECLARE_LOGGING_CATEGORY(context_overlay) -#endif // hifi_HoverOverlayLogging_h +#endif // hifi_ContextOverlayLogging_h diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp deleted file mode 100644 index 1ccbbafb1b..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// HoverOverlayInterface.cpp -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" -#include "Application.h" -#include - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); - - _entityScriptingInterface = DependencyManager::get(); - _entityPropertyFlags += PROP_POSITION; - _entityPropertyFlags += PROP_ROTATION; - - auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); - - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - - if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); - _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setPulseMin(0.75f); - _hoverOverlay->setPulseMax(1.0f); - _hoverOverlay->setColorPulse(1.0f); - _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(true); - _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _hoverOverlay->setIsFacingAvatar(true); - _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); - } - - _hoverOverlay->setPosition(entityProperties.getPosition()); - _hoverOverlay->setRotation(entityProperties.getRotation()); - _hoverOverlay->setVisible(true); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); - - qApp->getOverlays().deleteOverlay(_hoverOverlayID); - _hoverOverlay = NULL; - _hoverOverlayID = UNKNOWN_OVERLAY_ID; -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); -} - -void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _hoverOverlayID) { - qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; - } -} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h deleted file mode 100644 index 4ae14ffd1d..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// HoverOverlayInterface.h -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once -#ifndef hifi_HoverOverlayInterface_h -#define hifi_HoverOverlayInterface_h - -#include -#include - -#include -#include -#include "EntityScriptingInterface.h" -#include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Overlays.h" - -#include "EntityTree.h" -#include "HoverOverlayLogging.h" - -/**jsdoc -* @namespace HoverOverlay -*/ -class HoverOverlayInterface : public QObject, public Dependency { - Q_OBJECT - - Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) - QSharedPointer _entityScriptingInterface; - EntityPropertyFlags _entityPropertyFlags; - OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; -public: - HoverOverlayInterface(); - - Q_INVOKABLE QUuid getCurrentHoveredEntity() { return _currentHoveredEntity; } - void setCurrentHoveredEntity(const QUuid& entityID) { _currentHoveredEntity = entityID; } - -public slots: - void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID); - void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); - -private: - bool _verboseLogging { true }; - QUuid _currentHoveredEntity {}; - -}; - -#endif // hifi_HoverOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 7ee9a33485..1ab49d223e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entityWithHoverOverlay = false; +var entityWithContextOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2203,9 +2203,9 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); } var pointerEvent = { type: "Move", @@ -2216,8 +2216,8 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "None" }; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); - entityWithHoverOverlay = rayPickInfo.entityID; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + entityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3779,9 +3779,9 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } if (this.grabbedThingID !== null) { From 82111a8f6afc83f3347494a863ff214bc5d0a58a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:40:18 -0700 Subject: [PATCH 042/280] Timer for hand controller lasers --- .../system/controllers/handControllerGrab.js | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1ab49d223e..2b84c3d507 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -224,6 +224,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", enterMethod: "searchEnter", + exitMethod: "searchExit", updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { @@ -2174,6 +2175,13 @@ function MyController(hand) { } }; + this.searchExit = function () { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + }; + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -2206,17 +2214,22 @@ function MyController(hand) { if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + Script.setTimeout(function() { + if (rayPickInfo.entityID === entityWithContextOverlay) { + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + } + }, 500); entityWithContextOverlay = rayPickInfo.entityID; } @@ -3779,11 +3792,6 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } - if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 761d35d6335fa87468a09f2e6dec24500a91fb6c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 17:10:23 -0700 Subject: [PATCH 043/280] Getting there! --- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 +- .../system/controllers/handControllerGrab.js | 8 +-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cb15c544c7..33d492bc7e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -25,33 +25,36 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } -void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); +void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + if (event.getButton() == PointerEvent::SecondaryButton) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); + } else { + destroyContextOverlay(entityItemID, event); } - - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { @@ -68,7 +71,8 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 05215e3e5c..01129ee4f5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -43,7 +43,7 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } public slots: - void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2b84c3d507..8cd71131ce 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -370,7 +370,9 @@ function projectOntoOverlayXYPlane(overlayID, worldPos) { dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); - dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + if (dimensions.z) { + dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + } } return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); @@ -2225,9 +2227,9 @@ function MyController(hand) { pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, - button: "None" + button: "Secondary" }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); entityWithContextOverlay = rayPickInfo.entityID; From c56450b986e59270730b1de48df087fa5f35ce39 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Wed, 19 Jul 2017 01:49:39 -0700 Subject: [PATCH 044/280] Moved function to a differnt location that handles both mouse and controller inputs. --- scripts/system/edit.js | 2 -- scripts/system/libraries/entitySelectionTool.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e9afc3a167..52b8c8d51e 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -943,8 +943,6 @@ function mouseClickEvent(event) { selectionManager.addEntity(foundEntity, true); } - entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); - if (wantDebug) { print("Model selected: " + foundEntity); } diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 725803f824..f9081080fd 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -4064,6 +4064,7 @@ SelectionDisplay = (function() { // if another mouse button than left is pressed ignore it return false; } + entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); var somethingClicked = false; var pickRay = generalComputePickRay(event.x, event.y); From f06ec715f23f6b54531940bce3dcbd1feb0eb1a7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 09:59:44 -0700 Subject: [PATCH 045/280] Quick bugfix --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 33d492bc7e..9182346352 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); - } else { + } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } } From c866bd814d6300442a05a23c1b43c7d63166384a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 12:21:06 -0700 Subject: [PATCH 046/280] Make clicks work right --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlays.cpp | 87 +++++++------------ interface/src/ui/overlays/Overlays.h | 2 +- interface/src/ui/overlays/Planar3DOverlay.h | 1 + .../system/controllers/handControllerGrab.js | 23 +++-- 5 files changed, 50 insertions(+), 65 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9182346352..915ddca7bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,10 +45,10 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 862152bea9..3f175ffcc7 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,15 +818,17 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { + auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (!overlay) { + return PointerEvent(); + } + glm::vec3 position = overlay->getPosition(); + glm::quat rotation = overlay->getRotation(); + glm::vec2 dimensions = overlay->getSize(); - auto thisOverlay = std::dynamic_pointer_cast(overlay); - - auto position = thisOverlay->getPosition(); - auto rotation = thisOverlay->getRotation(); - auto dimensions = thisOverlay->getSize(); glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult); @@ -874,12 +876,7 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - PointerEvent pointerEvent; - if (thisWeb3DOverlay) { - pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - } + PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -895,13 +892,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; - } + auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); + emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mouseDoublePressOffOverlay(); return false; @@ -913,13 +906,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { - - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); - emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); + emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } _currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID; @@ -932,40 +920,29 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); + emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - - // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } - } - - // If hovering over a new overlay then enter hover on that overlay. - if (rayPickResult.overlayID != _currentHoverOverOverlayID) { - emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); - } - - // Hover over current overlay. - emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); - - _currentHoverOverOverlayID = rayPickResult.overlayID; + // If previously hovering over a different overlay then leave hover on that overlay. + if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } + + // If hovering over a new overlay then enter hover on that overlay. + if (rayPickResult.overlayID != _currentHoverOverOverlayID) { + emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); + } + + // Hover over current overlay. + emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); + + _currentHoverOverOverlayID = rayPickResult.overlayID; } else { // If previously hovering an overlay then leave hover. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); _currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 7235937077..d52f6d5947 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 9c502ab75e..2ccf2c4513 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,6 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; + glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f173295a1e..5fd4df0e4d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -188,6 +188,7 @@ var USE_BLACKLIST = true; var blacklist = []; var entityWithContextOverlay = false; +var contextualHand = -1; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -352,7 +353,9 @@ function projectOntoXYPlane(worldPos, position, rotation, dimensions, registrati function projectOntoEntityXYPlane(entityID, worldPos) { var props = entityPropertiesCache.getProps(entityID); - return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + if (props) { + return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + } } function projectOntoOverlayXYPlane(overlayID, worldPos) { @@ -2186,10 +2189,7 @@ function MyController(hand) { }; this.searchExit = function () { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + contextualHand = -1; }; this.search = function(deltaTime, timestamp) { @@ -2226,11 +2226,11 @@ function MyController(hand) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; } - Script.setTimeout(function() { - if (rayPickInfo.entityID === entityWithContextOverlay) { + Script.setTimeout(function () { + if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { var pointerEvent = { type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse + id: contextualHand + 1, // 0 is reserved for hardware mouse pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, @@ -2241,6 +2241,7 @@ function MyController(hand) { } }, 500); entityWithContextOverlay = rayPickInfo.entityID; + contextualHand = this.hand; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3488,6 +3489,11 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + if (isInEditMode()) { this.searchSphereDistance = existingSearchDistance; } @@ -3624,6 +3630,7 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); + entityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From d9c6126000765f1783b36997831cf9cf80ac2c54 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Jul 2017 13:33:32 -0700 Subject: [PATCH 047/280] more descriptive names for methods that do stuff also: changes to dimensions will trigger update to QueryAACube --- libraries/entities/src/EntityItemProperties.cpp | 15 ++++++++++----- libraries/entities/src/EntityItemProperties.h | 5 +++-- .../entities/src/EntityScriptingInterface.cpp | 4 ++-- libraries/entities/src/EntityScriptingInterface.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a207902789..6b963435f1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2157,12 +2157,17 @@ QList EntityItemProperties::listChangedProperties() { return out; } -bool EntityItemProperties::parentDependentPropertyChanged() const { - return localPositionChanged() || positionChanged() || - localRotationChanged() || rotationChanged() || - localVelocityChanged() || localAngularVelocityChanged(); +bool EntityItemProperties::transformChanged() const { + return positionChanged() || rotationChanged() || + localPositionChanged() || localRotationChanged(); } bool EntityItemProperties::parentRelatedPropertyChanged() const { - return parentDependentPropertyChanged() || parentIDChanged() || parentJointIndexChanged(); + return positionChanged() || rotationChanged() || + localPositionChanged() || localRotationChanged() || + parentIDChanged() || parentJointIndexChanged(); +} + +bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { + return parentRelatedPropertyChanged() || dimensionsChanged(); } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b526ac663c..90afd39fbf 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -86,8 +86,9 @@ public: EntityPropertyFlags getChangedProperties() const; - bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret? - bool parentRelatedPropertyChanged() const; // parentDependentPropertyChanged or parentID or parentJointIndex + bool transformChanged() const; + bool parentRelatedPropertyChanged() const; + bool queryAACubeRelatedPropertyChanged() const; AABox getAABox() const; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2cefd647cb..3e5976d2a8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -221,7 +221,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties _entityTree->withWriteLock([&] { EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); if (entity) { - if (propertiesWithSimID.parentRelatedPropertyChanged()) { + if (propertiesWithSimID.queryAACubeRelatedPropertyChanged()) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. bool success; AACube queryAACube = entity->getQueryAACube(success); @@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->rememberHasSimulationOwnershipBid(); } } - if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { + if (properties.queryAACubeRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { properties.setQueryAACube(entity->getQueryAACube()); } entity->setLastBroadcast(usecTimestampNow()); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 575528fa78..4e681e0ee7 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -100,7 +100,7 @@ public: void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine); - float calculateCost(float mass, float oldVelocity, float newVelocity); + float calculateCost(float mass, float oldSpeed, float newSpeed); void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index ecfb2b55e4..85e51535e0 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -577,7 +577,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setActionData(_serverActionData); } - if (properties.parentRelatedPropertyChanged() && _entity->computePuffedQueryAACube()) { + if (properties.transformChanged() && _entity->computePuffedQueryAACube()) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. properties.setQueryAACube(_entity->getQueryAACube()); } From 9ca3ec43133ad17ddcc0d87e52527e906c552ecc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Jul 2017 13:35:34 -0700 Subject: [PATCH 048/280] faster computation of entity bounding cube --- libraries/entities/src/EntityItem.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..89d875db2b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1423,26 +1423,20 @@ void EntityItem::setDimensions(const glm::vec3& value) { /// AACube EntityItem::getMaximumAACube(bool& success) const { if (_recalcMaxAACube) { - // * we know that the position is the center of rotation glm::vec3 centerOfRotation = getPosition(success); // also where _registration point is if (success) { _recalcMaxAACube = false; - // * we know that the registration point is the center of rotation - // * we can calculate the length of the furthest extent from the registration point - // as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint) - glm::vec3 dimensions = getDimensions(); - glm::vec3 registrationPoint = (dimensions * _registrationPoint); - glm::vec3 registrationRemainder = (dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint)); - glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder); + // we want to compute the furthestExtent that an entity can extend out from its "position" + // to do this we compute the max of these two vec3s: registration and 1-registration + // and then scale by dimensions + glm::vec3 maxExtents = getDimensions() * glm::max(_registrationPoint, glm::vec3(1.0f) - _registrationPoint); - // * we know that if you rotate in any direction you would create a sphere - // that has a radius of the length of furthest extent from registration point - float radius = glm::length(furthestExtentFromRegistration); + // there exists a sphere that contains maxExtents for all rotations + float radius = glm::length(maxExtents); - // * we know that the minimum bounding cube of this maximum possible sphere is - // (center - radius) to (center + radius) + // put a cube around the sphere + // TODO? replace _maxAACube with _boundingSphereRadius glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius); - _maxAACube = AACube(minimumCorner, radius * 2.0f); } } else { From a8ab115b1240ea0caa6b4e20152f4af4bfcfe6fb Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:11:14 -0700 Subject: [PATCH 049/280] Open marketplace when we know the marketplaceID of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 24 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 915ddca7bd..f887aae7ff 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,7 +11,10 @@ #include "ContextOverlayInterface.h" #include "Application.h" +#include "scripting/HMDScriptingInterface.h" + #include +#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -73,6 +76,27 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } +static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; + +void ContextOverlayInterface::openMarketplace() { + // lets open the tablet and go to the current item in + // the marketplace (if the current entity has a + // marketplaceID) + if (!_currentEntityWithContextOverlay.isNull()) { + auto hmd = DependencyManager::get(); + auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); + + if (entity->getMarketplaceID().length() > 0) { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); + tablet->gotoWebScreen(url); + hmd->openTablet(); + } + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 01129ee4f5..cd2d00f0a9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - + void openMarketplace(); }; #endif // hifi_ContextOverlayInterface_h From f0091f16a18669de27c08ad99da25e34645c71ac Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:46:10 -0700 Subject: [PATCH 050/280] cr feedback --- .../src/ui/overlays/ContextOverlayInterface.cpp | 12 +++++------- interface/src/ui/overlays/ContextOverlayInterface.h | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f887aae7ff..948ade840e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,10 +11,8 @@ #include "ContextOverlayInterface.h" #include "Application.h" -#include "scripting/HMDScriptingInterface.h" #include -#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -24,6 +22,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); _entityScriptingInterface = DependencyManager::get(); + _hmdScriptingInterface = DependencyManager::get(); + _tabletScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; @@ -87,16 +88,13 @@ void ContextOverlayInterface::openMarketplace() { // the marketplace (if the current entity has a // marketplaceID) if (!_currentEntityWithContextOverlay.isNull()) { - auto hmd = DependencyManager::get(); auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); tablet->gotoWebScreen(url); - hmd->openTablet(); + _hmdScriptingInterface->openTablet(); } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index cd2d00f0a9..4959b3d985 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -18,9 +18,12 @@ #include #include +#include + #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" +#include "scripting/HMDScriptingInterface.h" #include "EntityTree.h" #include "ContextOverlayLogging.h" @@ -34,6 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; + QSharedPointer _hmdScriptingInterface; + QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; public: From 44c9f0e65ea7d948bc805c17f39f922b372d4231 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:13:26 -0700 Subject: [PATCH 051/280] Overlay appears only on marketplace items --- .../ui/overlays/ContextOverlayInterface.cpp | 53 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 + 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..c2473feccc 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -27,6 +27,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; + _entityPropertyFlags += PROP_MARKETPLACE_ID; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -35,27 +36,30 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + if (entityProperties.getMarketplaceID().length() != 0) { + _marketplaceID = entityProperties.getMarketplaceID(); + setCurrentEntityWithContextOverlay(entityItemID); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); } - - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } @@ -87,14 +91,11 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull()) { - auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); - tablet->gotoWebScreen(url); - _hmdScriptingInterface->openTablet(); - } + if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + _marketplaceID; + tablet->gotoWebScreen(url); + _hmdScriptingInterface->openTablet(); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 4959b3d985..f3eb8c2ba9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,6 +56,8 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; + QString _marketplaceID; + void openMarketplace(); }; From 4374d4d1e5cebf1ca14a5eb0a5ca7d07166c96db Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 15:27:48 -0700 Subject: [PATCH 052/280] Small improvements to overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlay.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..3ab01ea8b1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index b650da3522..675dff7e93 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -20,7 +20,7 @@ Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), _isLoaded(true), _alpha(DEFAULT_ALPHA), - _pulse(0.0f), + _pulse(1.0f), _pulseMax(0.0f), _pulseMin(0.0f), _pulsePeriod(1.0f), From ecd419e0d393cf8a9ae85d356f5f8aa2210fed2b Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:32:03 -0700 Subject: [PATCH 053/280] cr feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c2473feccc..79edc7f863 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -35,10 +35,10 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _marketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); @@ -72,6 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _marketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { From 920ac17caf0d7b002c5548995e15cb5b2fc9c1f0 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:46:28 -0700 Subject: [PATCH 054/280] minor renaming --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 ++++---- interface/src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9a86d1efde..148d511acb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,7 +39,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - _marketplaceID = entityProperties.getMarketplaceID(); + _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -72,7 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - _marketplaceID.clear(); + _entityMarketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { @@ -92,10 +92,10 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + _marketplaceID; + QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f3eb8c2ba9..c79ee247ab 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,7 +56,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - QString _marketplaceID; + QString _entityMarketplaceID; void openMarketplace(); }; From 40ec770bc10f80802a8b69a2d58213b8e519720f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:06:17 -0700 Subject: [PATCH 055/280] Highlight using simple yellow bounding box --- .../src/ui/overlays/ContextOverlayInterface.cpp | 17 +++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..1582f7f53f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -28,10 +28,12 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; + _entityPropertyFlags += PROP_DIMENSIONS; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } +static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { @@ -42,6 +44,18 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setVisible(true); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(1.0f); @@ -70,8 +84,11 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt setCurrentEntityWithContextOverlay(QUuid()); qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); _contextOverlay = NULL; + _bbOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c79ee247ab..b20b9b35d5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -22,6 +22,7 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -40,7 +41,9 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; + std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); From 2dc38441168ee0ff1b953d0425b657b6e4f60699 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:07:57 -0700 Subject: [PATCH 056/280] Improve reliability of hand lasers; increase size of target --- .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- .../system/controllers/handControllerGrab.js | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3ab01ea8b1..c52ef1bb16 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5fd4df0e4d..9a1540cc78 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var potentialEntityWithContextOverlay = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2221,13 +2222,13 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + if (rayPickInfo.entityID && !entityWithContextOverlay) { Script.setTimeout(function () { - if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { + if (rayPickInfo.entityID === potentialEntityWithContextOverlay && + !entityWithContextOverlay + && contextualHand !== -1) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2240,8 +2241,8 @@ function MyController(hand) { ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); - entityWithContextOverlay = rayPickInfo.entityID; contextualHand = this.hand; + potentialEntityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3492,6 +3493,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; } if (isInEditMode()) { @@ -3631,6 +3633,7 @@ function MyController(hand) { Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 2dce2549289584106deea64f87e7b8dbbf7620d4 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:11:45 -0700 Subject: [PATCH 057/280] Actually increase target size --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..378d8937bb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -55,7 +55,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); From 00238329385ce2cbd2dc0786ea2ad9068ab29050 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:38:19 -0700 Subject: [PATCH 058/280] simple fixes --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bb1424b86a..83ab4c6b13 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -69,7 +70,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); @@ -97,7 +98,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { + if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From 0a64a1e3b0dff67cf5c1452cbe659f25bc399145 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Wed, 19 Jul 2017 18:26:35 -0700 Subject: [PATCH 059/280] Added a page in installer for express/custom installation --- cmake/templates/NSIS.template.in | 92 +++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index f44c8185d8..1f28d9c1aa 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -354,7 +354,8 @@ SectionEnd !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @CPACK_NSIS_PAGE_COMPONENTS@ - + + Page custom InstallTypesPage ReadInstallTypes Page custom PostInstallOptionsPage ReadPostInstallOptions !insertmacro MUI_PAGE_INSTFILES @@ -442,6 +443,10 @@ Var CleanInstallCheckbox Var CurrentOffset Var OffsetUnits Var CopyFromProductionCheckbox +Var ExpressInstallRadioButton +Var CustomInstallRadioButton +Var InstallTypeDialog +Var Express !macro SetPostInstallOption Checkbox OptionName Default ; reads the value for the given post install option to the registry @@ -459,6 +464,52 @@ Var CopyFromProductionCheckbox ${EndIf} !macroend +Function InstallTypesPage + !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" + + nsDialogs::Create 1018 + Pop $InstallTypeDialog + + ${If} $InstallTypeDialog == error + Abort + ${EndIf} + + StrCpy $CurrentOffset 0 + StrCpy $OffsetUnits u + StrCpy $Express "0" + + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox" + pop $ExpressInstallRadioButton + ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel + IntOp $CurrentOffset $CurrentOffset + 15 + + ${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)" + pop $CustomInstallRadioButton + ${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel + ${EndIf} + + ; Express Install selected by default + ${NSD_Check} $ExpressInstallRadioButton + Call ChangeExpressLabel + + nsDialogs::Show +FunctionEnd + +Function ChangeExpressLabel + Push $R1 + GetDlgItem $R1 $HWNDPARENT 1 + SendMessage $R1 ${WM_SETTEXT} 0 "STR:Install" + Pop $R1 +FunctionEnd + +Function ChangeCustomLabel + Push $R1 + GetDlgItem $R1 $HWNDPARENT 1 + SendMessage $R1 ${WM_SETTEXT} 0 "STR:Next >" + Pop $R1 +FunctionEnd + Function PostInstallOptionsPage !insertmacro MUI_HEADER_TEXT "Setup Options" "" @@ -552,6 +603,12 @@ Function PostInstallOptionsPage ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} ${EndIf} + ; Check if Express is set, if so, abort the post install options page + Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults + StrCmp $Express "1" 0 end + Abort + end: + nsDialogs::Show FunctionEnd @@ -567,6 +624,16 @@ Var LaunchServerNowState Var LaunchClientNowState Var CopyFromProductionState Var CleanInstallState +Var ExpressInstallState +Var CustomInstallState + +Function ReadInstallTypes +${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ; check if the user asked for express/custom install + ${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState + ${NSD_GetState} $CustomInstallRadioButton $CustomInstallState +${EndIf} +FunctionEnd Function ReadPostInstallOptions ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} @@ -603,6 +670,28 @@ Function ReadPostInstallOptions ${EndIf} FunctionEnd +Function HandleInstallTypes + ${If} $ExpressInstallState == ${BST_CHECKED} + + StrCpy $Express "1" + + ; over ride custom checkboxes and select defaults + ${NSD_SetState} $DesktopClientCheckbox ${BST_CHECKED} + ${NSD_SetState} $ServerStartupCheckbox ${BST_CHECKED} + ${NSD_SetState} $LaunchServerNowCheckbox ${BST_CHECKED} + ${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED} + + ${If} @PR_BUILD@ == 1 + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} + ${EndIf} + + ; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected + Call ReadPostInstallOptions + Call HandlePostInstallOptions + + ${EndIf} +FunctionEnd + Function HandlePostInstallOptions ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity @@ -624,6 +713,7 @@ Function HandlePostInstallOptions !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now From b236a21adc3d98f5e4b5c5525ef77c793af284e5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 07:28:45 -0700 Subject: [PATCH 060/280] no clicks on 'highlight' overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83ab4c6b13..0ef5a0ad43 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,7 +49,6 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -98,7 +97,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From 110efd63c835038d19bc813268895c452d9156b6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:08:49 -0700 Subject: [PATCH 061/280] enabled property so we can turn it off during edit, etc... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 0ef5a0ad43..e66b8ee39e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,10 +33,11 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } + static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (event.getButton() == PointerEvent::SecondaryButton) { + if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b20b9b35d5..92173de050 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -36,6 +36,7 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -49,6 +50,8 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + void setEnabled(bool enabled) { _enabled = enabled; } + bool getEnabled() { return _enabled; } public slots: void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -58,6 +61,7 @@ public slots: private: bool _verboseLogging { true }; + bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; From 4f1489f8b43b7b97b63bba044e8667876dcd9ec0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:18:19 -0700 Subject: [PATCH 062/280] less magic --- libraries/shared/src/SpatiallyNestable.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4fe9cda825..afc295a9fd 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -999,7 +999,9 @@ bool SpatiallyNestable::computePuffedQueryAACube() { bool success; AACube currentAACube = getMaximumAACube(success); // make an AACube with edges thrice as long and centered on the object - _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(currentAACube.getScale()), currentAACube.getScale() * 3.0f); + const float PUFF_COEFICIENT = _children.size() > 0 ? 3.0f : 1.0f; + float scale = currentAACube.getScale(); + _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFICIENT); _queryAACubeSet = true; getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { From 0544c1e56173e2f12c09142b0381f88eacddebce Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:18:37 -0700 Subject: [PATCH 063/280] build issue --- interface/src/ui/overlays/Overlays.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index d52f6d5947..a6cc300b67 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -131,7 +131,7 @@ public slots: OverlayID cloneOverlay(OverlayID id); /**jsdoc - * Edit an overlay's properties. + * Edit an overlay's properties. * * @function Overlays.editOverlay * @param {Overlays.OverlayID} overlayID The ID of the overlay to edit. @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From d88d7dda2bb1d2a0460fc16a6f37411b245754c6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:19:59 -0700 Subject: [PATCH 064/280] changed dimensions flag entity for new queryAACube --- libraries/entities/src/EntityItem.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 89d875db2b..f3ddadcc39 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1628,6 +1628,8 @@ void EntityItem::updateDimensions(const glm::vec3& value) { if (getDimensions() != value) { setDimensions(value); markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + _queryAACubeSet = false; + dimensionsChanged(); } } From 54426a5b81043cf303e85b97d6046ba5fcbe1803 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:21:10 -0700 Subject: [PATCH 065/280] update queryAACube on server when it probably changed --- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3e5976d2a8..c6a8e6bdcf 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->rememberHasSimulationOwnershipBid(); } } - if (properties.queryAACubeRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { + if (properties.queryAACubeRelatedPropertyChanged() || entity->computePuffedQueryAACube()) { properties.setQueryAACube(entity->getQueryAACube()); } entity->setLastBroadcast(usecTimestampNow()); From fcd3126b85c2f8687d0ffc4f1148720aa28fa747 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:32:48 -0700 Subject: [PATCH 066/280] undo accidental name change --- libraries/entities/src/EntityScriptingInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 4e681e0ee7..575528fa78 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -100,7 +100,7 @@ public: void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine); - float calculateCost(float mass, float oldSpeed, float newSpeed); + float calculateCost(float mass, float oldVelocity, float newVelocity); void resetActivityTracking(); ActivityTracking getActivityTracking() const { return _activityTracking; } From 0fc0cf736f6589b6b0bf7ac41bd451a1d2331f7e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 09:34:28 -0700 Subject: [PATCH 067/280] fix spelling --- libraries/shared/src/SpatiallyNestable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index afc295a9fd..7b8751d257 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -999,9 +999,9 @@ bool SpatiallyNestable::computePuffedQueryAACube() { bool success; AACube currentAACube = getMaximumAACube(success); // make an AACube with edges thrice as long and centered on the object - const float PUFF_COEFICIENT = _children.size() > 0 ? 3.0f : 1.0f; + const float PUFF_COEFFICIENT = _children.size() > 0 ? 3.0f : 1.0f; float scale = currentAACube.getScale(); - _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFICIENT); + _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFFICIENT); _queryAACubeSet = true; getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { From 0277f6a7e6e6b4a7979f5cc13cf73874738ef60a Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 10:11:24 -0700 Subject: [PATCH 068/280] Update NSIS.template.in --- cmake/templates/NSIS.template.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 1f28d9c1aa..04eef1e476 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -712,7 +712,6 @@ Function HandlePostInstallOptions ${Else} !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} - ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} From de5984d121d3cd05bd39c84e1d78f0aa164f6dbb Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 10:39:04 -0700 Subject: [PATCH 069/280] Update NSIS.template.in --- cmake/templates/NSIS.template.in | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 04eef1e476..7e90daa8f9 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -712,7 +712,6 @@ Function HandlePostInstallOptions ${Else} !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} - ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now From 2310ecab8d88680056af867364493d33a1a105d7 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 11:18:49 -0700 Subject: [PATCH 070/280] Update NSIS.template.in --- cmake/templates/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 7e90daa8f9..04eef1e476 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -712,6 +712,7 @@ Function HandlePostInstallOptions ${Else} !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now From b7be727b4ce89e4556ca2716e66378ff78e8ca69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 11:30:55 -0700 Subject: [PATCH 071/280] turn off ContextOverlay for pal, edit. Improve clicks too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++++-- scripts/system/edit.js | 5 +++-- scripts/system/pal.js | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..35ed81bf03 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -29,6 +29,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; _entityPropertyFlags += PROP_DIMENSIONS; + _entityPropertyFlags += PROP_REGISTRATION_POINT; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -40,6 +41,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 position = entityProperties.getPosition(); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -50,11 +52,13 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setPosition(position); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -67,11 +71,12 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setPosition(position); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6bb0675bc8..57cb8b2407 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -14,7 +14,7 @@ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE - + "use strict"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -388,7 +388,6 @@ var toolBar = (function () { function initialize() { Script.scriptEnding.connect(cleanup); - Window.domainChanged.connect(function () { that.setActive(false); that.clearEntityList(); @@ -614,6 +613,7 @@ var toolBar = (function () { }; that.setActive = function (active) { + ContextOverlay.enabled = !active; Settings.setValue(EDIT_SETTING, active); if (active) { Controller.captureEntityClickEvents(); @@ -2184,6 +2184,7 @@ var PopupMenu = function () { }; function cleanup() { + ContextOverlay.enabled = true; for (var i = 0; i < overlays.length; i++) { Overlays.deleteOverlay(overlays[i]); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 8ea22192fc..112e1a7fd8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -732,7 +732,9 @@ function onTabletButtonClicked() { if (onPalScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); + ContextOverlay.enabled = true; } else { + ContextOverlay.enabled = false; tablet.loadQMLSource(PAL_QML_SOURCE); tablet.tabletShownChanged.connect(tabletVisibilityChanged); Users.requestsDomainListData = true; @@ -883,6 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } + ContextOverlay.enable = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From 3a31805dff12d499be937c9f1a798cf1e0720e69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Jun 2017 17:12:25 -0700 Subject: [PATCH 072/280] Fix warnings in Qt 59 / VS 2017 build --- CMakeLists.txt | 27 +++++++++++-------- cmake/externals/quazip/CMakeLists.txt | 4 +-- cmake/macros/SetPackagingParameters.cmake | 10 +++++-- .../gpu-gl/src/gpu/gl41/GL41BackendInput.cpp | 2 +- libraries/shared/src/shared/Storage.cpp | 5 ++-- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 4 +-- plugins/openvr/src/OpenVrHelpers.cpp | 2 +- 7 files changed, 32 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c90256134..9712b2d32e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION 3.2) +if (WIN32) + cmake_minimum_required(VERSION 3.7) +else() + cmake_minimum_required(VERSION 3.2) +endif() if (USE_ANDROID_TOOLCHAIN) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake") @@ -33,6 +37,10 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") find_package( Threads ) if (WIN32) + if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + message( FATAL_ERROR "Only 64 bit builds supported." ) + endif() + add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) if (NOT WINDOW_SDK_PATH) @@ -41,16 +49,13 @@ if (WIN32) # sets path for Microsoft SDKs # if you get build error about missing 'glu32' this path is likely wrong - if (MSVC10) - set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 " CACHE PATH "Windows SDK PATH") - elseif (MSVC12) - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(WINDOW_SDK_FOLDER "x64") - else() - set(WINDOW_SDK_FOLDER "x86") - endif() + if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017 + set(WINDOW_SDK_PATH "C:/Program Files (x86)/Windows Kits/10/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64" CACHE PATH "Windows SDK PATH") + elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013 set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH") - endif () + else() + message( FATAL_ERROR "Visual Studio 2013 or higher required." ) + endif() if (DEBUG_DISCOVERED_SDK_PATH) message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}") @@ -103,7 +108,7 @@ else () endif () if (APPLE) - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++0x") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++") endif () diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 7af13dafa7..d44c9ffde3 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -21,8 +21,8 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip - URL_MD5 2955176048a31262c09259ca8d309d19 + URL https://s3.amazonaws.com/Oculus/quazip-0.7.3.zip + URL_MD5 ed03754d39b9da1775771819b8001d45 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} LOG_DOWNLOAD 1 diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 82a4a7d080..6f35b76f1d 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -126,8 +126,14 @@ macro(SET_PACKAGING_PARAMETERS) # check if we need to find signtool if (PRODUCTION_BUILD OR PR_BUILD) - find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64") - + if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017 + find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/10" PATH_SUFFIXES "bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64") + elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013 + find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64") + else() + message( FATAL_ERROR "Visual Studio 2013 or higher required." ) + endif() + if (NOT SIGNTOOL_EXECUTABLE) message(FATAL_ERROR "Code signing of executables was requested but signtool.exe could not be found.") endif () diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp index 43b48f721f..e8ebcbe05c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp @@ -95,7 +95,7 @@ void GL41Backend::updateInput() { // GLenum perLocationStride = strides[bufferNum]; GLenum perLocationStride = attrib._element.getLocationSize(); GLuint stride = (GLuint)strides[bufferNum]; - GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); + uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]); GLboolean isNormalized = attrib._element.isNormalized(); for (size_t locNum = 0; locNum < locationCount; ++locNum) { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index b07f896df0..7e9f86b049 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -92,9 +92,8 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { FileStorage::~FileStorage() { if (_mapped) { - if (!_file.unmap(_mapped)) { - throw std::runtime_error("Unable to unmap file"); - } + _file.unmap(_mapped); + _mapped = nullptr; } if (_file.isOpen()) { _file.close(); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 7a73c91c7d..b31f55edeb 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -282,7 +282,7 @@ public: static const vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static const vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; - vr::Texture_t texture{ (void*)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto }; + vr::Texture_t texture{ (void*)(uintptr_t)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &texture, &leftBounds); vr::VRCompositor()->Submit(vr::Eye_Right, &texture, &rightBounds); _plugin._presentRate.increment(); @@ -643,7 +643,7 @@ void OpenVrDisplayPlugin::hmdPresent() { _submitThread->waitForPresent(); } else { GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); - vr::Texture_t vrTexture { (void*)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; + vr::Texture_t vrTexture { (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); vr::VRCompositor()->PostPresentHandoff(); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 7e287a16c3..c8a0cb5f8b 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -129,7 +129,7 @@ void releaseOpenVrSystem() { #endif // HACK: workaround openvr crash, call submit with an invalid texture, right before VR_Shutdown. - const GLuint INVALID_GL_TEXTURE_HANDLE = -1; + const void* INVALID_GL_TEXTURE_HANDLE = (void*)(uintptr_t)-1; vr::Texture_t vrTexture{ (void*)INVALID_GL_TEXTURE_HANDLE, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 }; From f515a24568b7f3690f1364111b64849faa1f0fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:00:13 -0700 Subject: [PATCH 073/280] UX improvements --- .../ui/overlays/ContextOverlayInterface.cpp | 51 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 ++-- .../system/controllers/handControllerGrab.js | 7 +-- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..bdbbc3f5e8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,7 +36,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; -void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { +bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); @@ -59,7 +59,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setAlpha(0.85f); _contextOverlay->setPulseMin(0.75f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); @@ -69,32 +69,45 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 contextOverlayPosition; + if (distanceToEntity > 1.5f) { + contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + } else { + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + } + _contextOverlay->setPosition(contextOverlayPosition); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } - } else if (_currentEntityWithContextOverlay == entityItemID) { - destroyContextOverlay(entityItemID, event); + } else { + return destroyContextOverlay(entityItemID, event); } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(QUuid()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; - _entityMarketplaceID.clear(); + if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _contextOverlay = NULL; + _bbOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; + _entityMarketplaceID.clear(); + return true; + } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { - ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 92173de050..25179e99a8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,6 +29,10 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /**jsdoc * @namespace ContextOverlay */ @@ -54,9 +58,9 @@ public: bool getEnabled() { return _enabled; } public slots: - void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID); + bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a1540cc78..fde237e697 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2227,8 +2227,6 @@ function MyController(hand) { if (rayPickInfo.entityID === potentialEntityWithContextOverlay && !entityWithContextOverlay && contextualHand !== -1) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2238,7 +2236,10 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "Secondary" }; - ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; + } } }, 500); contextualHand = this.hand; From f365ce8abd545e0aa9f52c3f1a2dc67e06802fab Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 12:58:06 -0700 Subject: [PATCH 074/280] typo --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 112e1a7fd8..7f0629abaa 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -885,7 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enable = true; + ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From a1a7ab945a048a4b7c4981d5257cb34520a80bab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:56:46 -0700 Subject: [PATCH 075/280] Make dimensions better --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bdbbc3f5e8..d5244140d9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -72,13 +72,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); } else { - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + // If the entity is too close to the camera, rotate the context overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); + _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); return true; @@ -90,7 +95,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 25179e99a8..5ba70d7614 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,10 +29,6 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /**jsdoc * @namespace ContextOverlay */ From 46d09a12fe36535ca8ce3e386491f43fcfb74386 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 13:26:43 -0700 Subject: [PATCH 076/280] Fix bug where overlay would get stuck --- scripts/system/controllers/handControllerGrab.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index fde237e697..b191c6bf89 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3633,8 +3633,6 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); - entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 7ddac54b37f773f9fc96f9da3bb651ad9f2da2c0 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 13:55:04 -0700 Subject: [PATCH 077/280] copy from production for PR builds unchecked for custom and express also over rides deselection --- cmake/templates/NSIS.template.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 1f28d9c1aa..f653002fb6 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -600,7 +600,7 @@ Function PostInstallOptionsPage ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Copy settings and content from production install" Pop $CopyFromProductionCheckbox - ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED} ${EndIf} ; Check if Express is set, if so, abort the post install options page @@ -682,8 +682,8 @@ Function HandleInstallTypes ${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED} ${If} @PR_BUILD@ == 1 - ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} - ${EndIf} + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED} + ${EndIf} ; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected Call ReadPostInstallOptions From 10503c3a16c54b70d87ebf1ce61fdaa94ccd2606 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:18:07 -0700 Subject: [PATCH 078/280] registration point --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 80c5665127..7f20ced1c3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -41,7 +41,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 position = entityProperties.getPosition(); + glm::vec3 bbPosition = entityProperties.getPosition(); + glm::vec3 dimensions = entityProperties.getDimensions(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -58,7 +63,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(position); + _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -71,7 +76,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); From c1de09c7f65635ec81802128a62567c9ca2405d2 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:24:16 -0700 Subject: [PATCH 079/280] whitespace issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7f20ced1c3..c15bfaf9a2 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; From c8015e4eb608d663ebadfb17bc8dc152badb32ff Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:52:58 -0700 Subject: [PATCH 080/280] cleanup, fixed build issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c15bfaf9a2..50fcf50b90 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -79,7 +79,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(position, cameraPosition); + float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { From 5cd05da31e46ec60cfb19f1c87bff9fed460a9d3 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 15:16:40 -0700 Subject: [PATCH 081/280] strict comparisons --- 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 b191c6bf89..f0f1267bcf 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2500,7 +2500,7 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") === "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; } From a8dac0cb79b49b2646d34780a43374b6322737e0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 15:45:50 -0700 Subject: [PATCH 082/280] don't send constant updates for grabbed objects --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 6 -- .../entities/src/EntityScriptingInterface.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 3 +- .../entities/src/SimpleEntitySimulation.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 16 +++-- libraries/shared/src/SpatiallyNestable.cpp | 61 ++++++++++--------- libraries/shared/src/SpatiallyNestable.h | 8 +-- 8 files changed, 48 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f9a4d491c8..c072d3d7f0 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -607,7 +607,7 @@ void MyAvatar::simulate(float deltaTime) { MovingEntitiesOperator moveOperator(entityTree); forEachDescendant([&](SpatiallyNestablePointer object) { // if the queryBox has changed, tell the entity-server - if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { + if (object->getNestableType() == NestableType::Entity && object->checkAndMaybeUpdateQueryAACube()) { EntityItemPointer entity = std::static_pointer_cast(object); bool success; AACube newCube = entity->getQueryAACube(success); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f3ddadcc39..224392825c 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1353,12 +1353,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); - AACube saveQueryAACube = _queryAACube; - checkAndAdjustQueryAACube(); - if (saveQueryAACube != _queryAACube) { - somethingChanged = true; - } - // Now check the sub classes somethingChanged |= setSubClassProperties(properties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c6a8e6bdcf..2ad4fe72d2 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->rememberHasSimulationOwnershipBid(); } } - if (properties.queryAACubeRelatedPropertyChanged() || entity->computePuffedQueryAACube()) { + if (properties.queryAACubeRelatedPropertyChanged() && entity->checkAndMaybeUpdateQueryAACube()) { properties.setQueryAACube(entity->getQueryAACube()); } entity->setLastBroadcast(usecTimestampNow()); @@ -445,7 +445,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // if they've changed. entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { if (descendant->getNestableType() == NestableType::Entity) { - if (descendant->computePuffedQueryAACube()) { + if (descendant->checkAndMaybeUpdateQueryAACube()) { EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5e58736477..f17b991b69 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1675,6 +1675,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen addToNeedsParentFixupList(entity); } entity->forceQueryAACubeUpdate(); + entity->checkAndMaybeUpdateQueryAACube(); moveOperator.addEntityToMoveList(entity, entity->getQueryAACube()); i++; } else { @@ -1693,7 +1694,7 @@ QVector EntityTree::sendEntities(EntityEditPacketSender* packetSen EntityItemPointer entity = localTree->findEntityByEntityItemID(newID); if (entity) { // queue the packet to send to the server - entity->computePuffedQueryAACube(); + entity->updateQueryAACube(); EntityItemProperties properties = entity->getProperties(); properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties); diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index e406926141..d41b22137e 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -136,7 +136,7 @@ void SimpleEntitySimulation::sortEntitiesThatMoved() { SetOfEntities::iterator itemItr = _entitiesToSort.begin(); while (itemItr != _entitiesToSort.end()) { EntityItemPointer entity = *itemItr; - entity->computePuffedQueryAACube(); + entity->checkAndMaybeUpdateQueryAACube(); ++itemItr; } EntitySimulation::sortEntitiesThatMoved(); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 85e51535e0..f02dcee8f6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -484,11 +484,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { return false; } - if (_entity->dynamicDataNeedsTransmit()) { - return true; - } - - if (_entity->queryAABoxNeedsUpdate()) { + if (_entity->dynamicDataNeedsTransmit() || _entity->queryAACubeNeedsUpdate()) { return true; } @@ -577,9 +573,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setActionData(_serverActionData); } - if (properties.transformChanged() && _entity->computePuffedQueryAACube()) { - // due to parenting, the server may not know where something is in world-space, so include the bounding cube. - properties.setQueryAACube(_entity->getQueryAACube()); + if (properties.transformChanged()) { + if (_entity->checkAndMaybeUpdateQueryAACube()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + properties.setQueryAACube(_entity->getQueryAACube()); + } } // set the LastEdited of the properties but NOT the entity itself @@ -643,7 +641,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { if (descendant->getNestableType() == NestableType::Entity) { EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); - if (descendant->computePuffedQueryAACube()) { + if (descendant->checkAndMaybeUpdateQueryAACube()) { EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 7b8751d257..1026f95c40 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -10,6 +10,7 @@ // #include +#include // adebug #include "DependencyManager.h" #include "SharedUtil.h" @@ -946,13 +947,31 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); } -bool SpatiallyNestable::checkAndAdjustQueryAACube() { +bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) { - setQueryAACube(maxAACube); + const float PUFF_COEFFICIENT = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? 3.0f : 1.0f; + float scale = maxAACube.getScale(); + _queryAACube = AACube(maxAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFFICIENT); + + getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { + bool success; + AACube descendantAACube = descendant->getQueryAACube(success); + if (success) { + if (_queryAACube.contains(descendantAACube)) { + return; + } + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + } + }); + _queryAACubeSet = true; + + return true; + } else { + return false; } - return success; } void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { @@ -961,48 +980,31 @@ void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { return; } _queryAACube = queryAACube; - if (queryAACube.getScale() > 0.0f) { - _queryAACubeSet = true; - } + _queryAACubeSet = true; } -bool SpatiallyNestable::queryAABoxNeedsUpdate() const { - bool success; - AACube currentAACube = getMaximumAACube(success); - if (!success) { - qCDebug(shared) << "can't getMaximumAACube for" << getID(); - return false; +bool SpatiallyNestable::queryAACubeNeedsUpdate() const { + if (!_queryAACubeSet) { + return true; } // make sure children are still in their boxes, also. bool childNeedsUpdate = false; getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { - if (!childNeedsUpdate && descendant->queryAABoxNeedsUpdate()) { + if (!childNeedsUpdate && descendant->queryAACubeNeedsUpdate()) { childNeedsUpdate = true; } }); - if (childNeedsUpdate) { - return true; - } - - if (_queryAACubeSet && _queryAACube.contains(currentAACube)) { - return false; - } - - return true; + return childNeedsUpdate; } -bool SpatiallyNestable::computePuffedQueryAACube() { - if (!queryAABoxNeedsUpdate()) { - return false; - } +void SpatiallyNestable::updateQueryAACube() { bool success; AACube currentAACube = getMaximumAACube(success); // make an AACube with edges thrice as long and centered on the object - const float PUFF_COEFFICIENT = _children.size() > 0 ? 3.0f : 1.0f; + const float PUFF_COEFFICIENT = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? 3.0f : 1.0f; float scale = currentAACube.getScale(); _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFFICIENT); - _queryAACubeSet = true; getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { bool success; @@ -1015,8 +1017,7 @@ bool SpatiallyNestable::computePuffedQueryAACube() { _queryAACube += descendantAACube.getMaximumPoint(); } }); - - return true; + _queryAACubeSet = true; } AACube SpatiallyNestable::getQueryAACube(bool& success) const { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index b98ab4c358..78a014d7c1 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -102,11 +102,11 @@ public: virtual glm::vec3 getParentAngularVelocity(bool& success) const; virtual AACube getMaximumAACube(bool& success) const; - virtual bool checkAndAdjustQueryAACube(); - virtual bool computePuffedQueryAACube(); + bool checkAndMaybeUpdateQueryAACube(); + void updateQueryAACube(); virtual void setQueryAACube(const AACube& queryAACube); - virtual bool queryAABoxNeedsUpdate() const; + virtual bool queryAACubeNeedsUpdate() const; void forceQueryAACubeUpdate() { _queryAACubeSet = false; } virtual AACube getQueryAACube(bool& success) const; virtual AACube getQueryAACube() const; @@ -197,7 +197,7 @@ protected: mutable QHash _children; virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed - virtual void dimensionsChanged() { } // called when a this object's dimensions have changed + virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed virtual void parentDeleted() { } // called on children of a deleted parent // _queryAACube is used to decide where something lives in the octree From e1eb2236169524a9c47dc1abfe36b186a5b788a4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 16:12:33 -0700 Subject: [PATCH 083/280] cleanup bad indentation, cruft, and constants --- libraries/entities/src/EntityItemProperties.cpp | 2 +- libraries/shared/src/SpatiallyNestable.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 6b963435f1..20b541f563 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2169,5 +2169,5 @@ bool EntityItemProperties::parentRelatedPropertyChanged() const { } bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { - return parentRelatedPropertyChanged() || dimensionsChanged(); + return parentRelatedPropertyChanged() || dimensionsChanged(); } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 1026f95c40..566ae3b89c 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -10,7 +10,6 @@ // #include -#include // adebug #include "DependencyManager.h" #include "SharedUtil.h" @@ -947,13 +946,16 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); } +const float PARENTED_EXPANSION_FACTOR = 3.0f; + bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) { - const float PUFF_COEFFICIENT = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? 3.0f : 1.0f; + const float expansionFactor = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) + ? PARENTED_EXPANSION_FACTOR : 1.0f; float scale = maxAACube.getScale(); - _queryAACube = AACube(maxAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFFICIENT); + _queryAACube = AACube(maxAACube.getCorner() - glm::vec3(scale), scale * expansionFactor); getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { bool success; @@ -1002,9 +1004,10 @@ void SpatiallyNestable::updateQueryAACube() { bool success; AACube currentAACube = getMaximumAACube(success); // make an AACube with edges thrice as long and centered on the object - const float PUFF_COEFFICIENT = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? 3.0f : 1.0f; + const float expansionFactor = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? + PARENTED_EXPANSION_FACTOR : 1.0f; float scale = currentAACube.getScale(); - _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFFICIENT); + _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * expansionFactor); getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { bool success; From e68f9499fa018884784b6263a577602008036e01 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 16:51:00 -0700 Subject: [PATCH 084/280] Add hover/unhover handlers to context overlays --- interface/src/Application.cpp | 16 ++++++++++--- .../ui/overlays/ContextOverlayInterface.cpp | 23 ++++++++++++++++++- .../src/ui/overlays/ContextOverlayInterface.h | 2 ++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cf5037f57..699ddfcb2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1348,9 +1348,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(overlays, - SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 50fcf50b90..78ca0d591c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -69,7 +69,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMin(0.6f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); _contextOverlay->setIgnoreRayIntersection(false); @@ -130,6 +130,27 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } + +void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(0.0f); + _contextOverlay->setPulsePeriod(0.0f); + _contextOverlay->setAlpha(1.0f); + } +} + +void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setPulsePeriod(1.0f); + _contextOverlay->setAlpha(0.85f); + } +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 5ba70d7614..f06f85eab7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,6 +58,8 @@ public slots: bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; From 649b974927e0deff5156d1333555b40206e4773d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 21 Jul 2017 01:01:23 +0100 Subject: [PATCH 085/280] mods for changes camera views --- interface/src/Application.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dbb94cfdae..885663939b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3020,7 +3020,34 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_F: { - _physicsEngine->dumpNextStats(); + if (isMeta) { + auto menu = Menu::getInstance(); + if (menu->isOptionChecked(MenuOption::FirstPerson)) { + menu->setIsOptionChecked(MenuOption::ThirdPerson, true); + menu->setIsOptionChecked(MenuOption::FirstPerson, false); + } else { + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + menu->setIsOptionChecked(MenuOption::ThirdPerson, false); + } + cameraMenuChanged(); + } else if (isOption) { + _physicsEngine->dumpNextStats(); + } + break; + } + + case Qt::Key_H: { + if (isOption) { + auto menu = Menu::getInstance(); + if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { + menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + } else { + menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); + menu->setIsOptionChecked(MenuOption::FirstPerson, false); + } + } + cameraMenuChanged(); break; } From 5f3b68d544d6d4e03fd54e6d9a09a28e9dd7840f Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Thu, 20 Jul 2017 17:36:02 -0700 Subject: [PATCH 086/280] Use ctrl+H instead of alt+H --- 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 885663939b..92199f8774 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3037,7 +3037,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } case Qt::Key_H: { - if (isOption) { + if (isMeta) { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); From 9c0026bf697c95f2bb210d900b1e739a9008859c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 08:40:12 -0700 Subject: [PATCH 087/280] make sure context overlays are on after pal is dismissed --- scripts/system/pal.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7f0629abaa..58a54ef859 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -718,10 +718,12 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; + ContextOverlay.enabled = true; } function tabletVisibilityChanged() { if (!tablet.tabletShown) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -865,6 +867,7 @@ function avatarDisconnected(nodeID) { function clearLocalQMLDataAndClosePAL() { sendToQml({ method: 'clearLocalQMLData' }); if (onPalScreen) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -885,7 +888,6 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From 7d125f44e417d13a39e411767c2f520011820570 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 09:35:49 -0700 Subject: [PATCH 088/280] Set parentID even when overlay already exists --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 78ca0d591c..de1c4156a6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -58,9 +58,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } + _bbOverlay->setParentID(entityItemID); _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); From 1e6fc85e3e271fd5ebab64aa1124b5d7ed21b148 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 10:17:10 -0700 Subject: [PATCH 089/280] fix math for expanded bounding box --- libraries/shared/src/SpatiallyNestable.cpp | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 566ae3b89c..38f18c5024 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -952,10 +952,13 @@ bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) { - const float expansionFactor = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) - ? PARENTED_EXPANSION_FACTOR : 1.0f; - float scale = maxAACube.getScale(); - _queryAACube = AACube(maxAACube.getCorner() - glm::vec3(scale), scale * expansionFactor); + if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { + // make an expanded AACube centered on the object + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + } else { + _queryAACube = maxAACube; + } getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { bool success; @@ -1002,12 +1005,14 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { void SpatiallyNestable::updateQueryAACube() { bool success; - AACube currentAACube = getMaximumAACube(success); - // make an AACube with edges thrice as long and centered on the object - const float expansionFactor = (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) ? - PARENTED_EXPANSION_FACTOR : 1.0f; - float scale = currentAACube.getScale(); - _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * expansionFactor); + AACube maxAACube = getMaximumAACube(success); + if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { + // make an expanded AACube centered on the object + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + } else { + _queryAACube = maxAACube; + } getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { bool success; From 17323ba42acf0d15a694184023bdadd5baa3497b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 10:25:03 -0700 Subject: [PATCH 090/280] always update the bounding box --- libraries/entities/src/EntityItem.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 224392825c..a44a6b3904 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1353,6 +1353,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); + AACube saveQueryAACube = _queryAACube; + if (checkAndMaybeUpdateQueryAACube() && saveQueryAACube != _queryAACube) { + somethingChanged = true; + } + // Now check the sub classes somethingChanged |= setSubClassProperties(properties); From d86ae98211ae6b5fafdc0b9537dbd6e47006e79f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 10:57:09 -0700 Subject: [PATCH 091/280] always update bounding box on script edit --- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2ad4fe72d2..a4ba9e30fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->rememberHasSimulationOwnershipBid(); } } - if (properties.queryAACubeRelatedPropertyChanged() && entity->checkAndMaybeUpdateQueryAACube()) { + if (properties.queryAACubeRelatedPropertyChanged()) { properties.setQueryAACube(entity->getQueryAACube()); } entity->setLastBroadcast(usecTimestampNow()); From 29daac83a419a2338ad8ae7687b1d35a5fb35790 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 10:59:25 -0700 Subject: [PATCH 092/280] optimize cube update only for connected things --- libraries/shared/src/SpatiallyNestable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 38f18c5024..0a480d2d71 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -951,7 +951,7 @@ const float PARENTED_EXPANSION_FACTOR = 3.0f; bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); - if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) { + if (success && (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube))) { if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { // make an expanded AACube centered on the object float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); From a800f6c05a6e57be7371e979c6817132e43d6d5f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:44:44 -0700 Subject: [PATCH 093/280] No magic numbers; Move (i) based on laser hand; Tablet to Tab key --- .../resources/controllers/keyboardMouse.json | 2 +- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++++------- .../src/input-plugins/KeyboardMouseDevice.cpp | 1 + 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8baf56684a..a4f7cd8db1 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -116,6 +116,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } ] } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index de1c4156a6..7d1131218a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,10 +36,22 @@ ContextOverlayInterface::ContextOverlayInterface() { } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; +static const uint32_t LEFT_HAND_HW_ID = 1; +static const uint32_t RIGHT_HAND_HW_ID = 2; +static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters +static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; +static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); glm::vec3 bbPosition = entityProperties.getPosition(); glm::vec3 dimensions = entityProperties.getDimensions(); @@ -68,10 +80,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.6f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); + _contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN); + _contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); @@ -82,14 +94,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > 1.5f) { + if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); @@ -134,20 +150,20 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(0.0f); - _contextOverlay->setPulsePeriod(0.0f); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(0.0f); // pulse off + _contextOverlay->setPulsePeriod(0.0f); // pulse off + _contextOverlay->setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA); } } void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setPulsePeriod(1.0f); - _contextOverlay->setAlpha(0.85f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); + _contextOverlay->setPulsePeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index b5a2fc6b3c..e9ec6d8910 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -236,6 +236,7 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); From a346e5ccf6b42a6a43f45e5e29e125f91aa11b07 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:56:06 -0700 Subject: [PATCH 094/280] New inspect icon --- interface/resources/images/inspect-icon.png | Bin 0 -> 27911 bytes .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 interface/resources/images/inspect-icon.png diff --git a/interface/resources/images/inspect-icon.png b/interface/resources/images/inspect-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9259de23e9f5e966975da9652a5ecb1a15b8c248 GIT binary patch literal 27911 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 literal 0 HcmV?d00001 diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7d1131218a..53b767525f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -86,7 +86,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png"); _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } From 9e31024a0e3839756a9bd333cd1121dbbe441120 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 13:16:41 -0700 Subject: [PATCH 095/280] merged with new stuff too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 53b767525f..6eec65a7d4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; +static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { @@ -95,7 +96,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 796292dd4c6efffedff555f5756d8aaaca705f0d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 13:21:13 -0700 Subject: [PATCH 096/280] Fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 3f175ffcc7..b0aa94b884 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -827,6 +827,9 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } glm::vec2 dimensions = overlay->getSize(); From 811be15bbcd03fe757f02a2003e9e3aeccca5b11 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 14:02:19 -0700 Subject: [PATCH 097/280] Another attmept to fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b0aa94b884..40ab4309b5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -822,14 +822,14 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } if (!overlay) { return PointerEvent(); } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); - if (getOverlayType(overlayID) == "web3d") { - overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); - } glm::vec2 dimensions = overlay->getSize(); From e7c6ba9ae2a4183d32d9542d6236adb1977cf0f5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 14:44:45 -0700 Subject: [PATCH 098/280] unused var --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6eec65a7d4..83c86bfd82 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -37,7 +37,6 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; -static const uint32_t RIGHT_HAND_HW_ID = 2; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims From 06b4c53d28f0c843ab8b927bbe8ba94695dc15b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Sat, 22 Jul 2017 00:15:26 +0100 Subject: [PATCH 099/280] only desktop mode --- 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 92199f8774..03f62221ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3037,7 +3037,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } case Qt::Key_H: { - if (isMeta) { + if (isMeta && !isHMDMode()) { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); From 630922dd95d39a3c6feeb6bf442f917ad847f92e Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 05:25:52 +0100 Subject: [PATCH 100/280] Dominant Hands Branch Initial Commit Adds a new option in the Avatar Basics section of the Avatar Settings. API Accessible Functions: MyAvatar.getUseAlternativeHand() MyAvatar.setUseAlternativeHand() Defaults to false (Right Hand). Will return True if set to Left Hand. --- .../qml/controls-uit/RadioButton.qml | 71 ++++++++++++ .../preferences/PrimaryHandPreference.qml | 102 ++++++++++++++++++ .../qml/dialogs/preferences/Section.qml | 5 + interface/src/avatar/MyAvatar.cpp | 3 +- interface/src/avatar/MyAvatar.h | 5 +- interface/src/ui/PreferencesDialog.cpp | 6 ++ libraries/shared/src/Preferences.h | 9 ++ 7 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 interface/resources/qml/controls-uit/RadioButton.qml create mode 100644 interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml new file mode 100644 index 0000000000..76b7902435 --- /dev/null +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -0,0 +1,71 @@ +// +// RadioButton.qml +// +// Created by Cain Kilgore on 20th July 2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Original +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" +import "../controls-uit" as HifiControls + +Original.RadioButton { + id: radioButton + HifiConstants { id: hifi } + + property int colorScheme: hifi.colorSchemes.light + readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light + + readonly property int boxSize: 14 + readonly property int boxRadius: 3 + readonly property int checkSize: 10 + readonly property int checkRadius: 2 + + style: RadioButtonStyle { + indicator: Rectangle { + id: box + width: boxSize + height: boxSize + radius: boxRadius + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish) + : (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: hifi.colors.checkBoxChecked + border.width: 1 + border.color: hifi.colors.checkBoxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + } + + label: RalewaySemiBold { + text: control.text + size: hifi.fontSizes.inputLabel + color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText + x: radioButton.boxSize / 2 + } + } +} diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml new file mode 100644 index 0000000000..74044ea27d --- /dev/null +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -0,0 +1,102 @@ +// +// PrimaryHandPreference.qml +// +// Created by Cain Kilgore on 20th July 2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../../controls-uit" + +Preference { + id: root + property alias box1: box1 + property alias box2: box2 + + height: control.height + hifi.dimensions.controlInterlineHeight + + Component.onCompleted: { + if(preference.value) { + box1.checked = true; + } else { + box2.checked = true; + } + } + + function save() { + // Box1 = True, Box2 = False (Right Hand for Default) + if(box1.checked && !box2.checked) { + preference.value = true; + } + if(!box1.checked && box2.checked) { + preference.value = false; + } + preference.save(); + } + + Item { + id: control + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: Math.max(labelName.height, box1.height, box2.height) + + Label { + id: labelName + text: root.label + ":" + colorScheme: hifi.colorSchemes.dark + anchors { + left: parent.left + right: box1.left + rightMargin: hifi.dimensions.labelPadding + verticalCenter: parent.verticalCenter + } + horizontalAlignment: Text.AlignRight + wrapMode: Text.Wrap + } + + RadioButton { + id: box1 + text: "Left" + width: 60 + anchors { + right: box2.left + verticalCenter: parent.verticalCenter + } + onClicked: { + if(box2.checked) { + box2.checked = false; + } + if(!box1.checked && !box2.checked) { + box2.checked = true; + } + } + colorScheme: hifi.colorSchemes.dark + } + + RadioButton { + id: box2 + text: "Right" + width: 60 + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + onClicked: { + if(box1.checked) { + box1.checked = false; + } + if(!box1.checked && !box2.checked) { + box2.checked = true; + } + } + colorScheme: hifi.colorSchemes.dark + } + } +} diff --git a/interface/resources/qml/dialogs/preferences/Section.qml b/interface/resources/qml/dialogs/preferences/Section.qml index 3985c7d6f6..af2e58c875 100644 --- a/interface/resources/qml/dialogs/preferences/Section.qml +++ b/interface/resources/qml/dialogs/preferences/Section.qml @@ -73,6 +73,7 @@ Preference { property var buttonBuilder: Component { ButtonPreference { } } property var comboBoxBuilder: Component { ComboBoxPreference { } } property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } } + property var primaryHandBuilder: Component { PrimaryHandPreference { } } property var preferences: [] property int checkBoxCount: 0 @@ -134,6 +135,10 @@ Preference { checkBoxCount = 0; builder = spinnerSliderBuilder; break; + case Preference.PrimaryHand: + checkBoxCount++; + builder = primaryHandBuilder; + break; }; if (builder) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f9a4d491c8..0a3d9f4c74 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -926,6 +926,7 @@ void MyAvatar::saveData() { Settings settings; settings.beginGroup("Avatar"); + settings.setValue("useAlternativeHand", _useAlternativeHand); settings.setValue("headPitch", getHead()->getBasePitch()); settings.setValue("scale", _targetScale); @@ -1122,7 +1123,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool()); - + setUseAlternativeHand(settings.value("useAlternativeHand", _useAlternativeHand).toBool()); settings.endGroup(); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 648a5b5f29..8ff616b36b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -339,6 +339,9 @@ public: Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; } Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; } + Q_INVOKABLE void setUseAlternativeHand(bool hand) { _useAlternativeHand = hand; } + Q_INVOKABLE bool getUseAlternativeHand() const { return _useAlternativeHand; } + Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } @@ -424,7 +427,6 @@ public: Q_INVOKABLE QString getFullAvatarModelName() const { return _fullAvatarModelName; } void resetFullAvatarURL(); - virtual void setAttachmentData(const QVector& attachmentData) override; MyCharacterController* getCharacterController() { return &_characterController; } @@ -720,6 +722,7 @@ private: QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + bool _useAlternativeHand{ false }; // False defaults to right hand, true to left const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 87131e4f5c..b962b456af 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -67,6 +67,12 @@ void setupPreferences() { auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); }; preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter)); } + { + auto getter = [=]()->bool { return myAvatar->getUseAlternativeHand(); }; + auto setter = [=](bool value) { myAvatar->setUseAlternativeHand(value); }; + preferences->addPreference(new PrimaryHandPreference(AVATAR_BASICS, "Dominant Hand", getter, setter)); + + } // UI static const QString UI_CATEGORY { "UI" }; diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 6093cd3c8a..73cce1e909 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -56,6 +56,7 @@ public: Checkbox, Button, ComboBox, + PrimaryHand, // Special casing for an unusual preference Avatar }; @@ -339,6 +340,14 @@ public: Type getType() override { return Checkbox; } }; +class PrimaryHandPreference : public BoolPreference { + Q_OBJECT +public: + PrimaryHandPreference(const QString& category, const QString& name, Getter getter, Setter setter) + : BoolPreference(category, name, getter, setter) { } + Type getType() override { return PrimaryHand; } +}; + #endif From a4cf27402dcf541374c1b13d3e938517b3740b75 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 06:27:14 +0100 Subject: [PATCH 101/280] Tabs and Cleanup of Code --- .../preferences/PrimaryHandPreference.qml | 20 +++++++++---------- .../qml/dialogs/preferences/Section.qml | 3 ++- interface/src/ui/PreferencesDialog.cpp | 1 - 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index 74044ea27d..af9999ae7a 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -20,7 +20,7 @@ Preference { height: control.height + hifi.dimensions.controlInterlineHeight Component.onCompleted: { - if(preference.value) { + if (preference.value) { box1.checked = true; } else { box2.checked = true; @@ -28,13 +28,13 @@ Preference { } function save() { - // Box1 = True, Box2 = False (Right Hand for Default) - if(box1.checked && !box2.checked) { - preference.value = true; - } - if(!box1.checked && box2.checked) { - preference.value = false; - } + // Box1 = True, Box2 = False (Right Hand for Default) + if (box1.checked && !box2.checked) { + preference.value = true; + } + if (!box1.checked && box2.checked) { + preference.value = false; + } preference.save(); } @@ -70,10 +70,10 @@ Preference { verticalCenter: parent.verticalCenter } onClicked: { - if(box2.checked) { + if (box2.checked) { box2.checked = false; } - if(!box1.checked && !box2.checked) { + if (!box1.checked && !box2.checked) { box2.checked = true; } } diff --git a/interface/resources/qml/dialogs/preferences/Section.qml b/interface/resources/qml/dialogs/preferences/Section.qml index af2e58c875..4a16036a69 100644 --- a/interface/resources/qml/dialogs/preferences/Section.qml +++ b/interface/resources/qml/dialogs/preferences/Section.qml @@ -73,7 +73,7 @@ Preference { property var buttonBuilder: Component { ButtonPreference { } } property var comboBoxBuilder: Component { ComboBoxPreference { } } property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } } - property var primaryHandBuilder: Component { PrimaryHandPreference { } } + property var primaryHandBuilder: Component { PrimaryHandPreference { } } property var preferences: [] property int checkBoxCount: 0 @@ -135,6 +135,7 @@ Preference { checkBoxCount = 0; builder = spinnerSliderBuilder; break; + case Preference.PrimaryHand: checkBoxCount++; builder = primaryHandBuilder; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index b962b456af..fe1663b98d 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -71,7 +71,6 @@ void setupPreferences() { auto getter = [=]()->bool { return myAvatar->getUseAlternativeHand(); }; auto setter = [=](bool value) { myAvatar->setUseAlternativeHand(value); }; preferences->addPreference(new PrimaryHandPreference(AVATAR_BASICS, "Dominant Hand", getter, setter)); - } // UI From 42742ba1f9fda98db0fd11281a5b68b5a471fa09 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 18:31:16 +0100 Subject: [PATCH 102/280] Changed the Return to a String "left/right" instead. --- .../qml/dialogs/preferences/PrimaryHandPreference.qml | 6 +++--- interface/src/avatar/MyAvatar.cpp | 4 ++-- interface/src/avatar/MyAvatar.h | 11 ++++++++--- interface/src/ui/PreferencesDialog.cpp | 4 ++-- libraries/shared/src/Preferences.h | 4 ++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index af9999ae7a..c89756f678 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -20,7 +20,7 @@ Preference { height: control.height + hifi.dimensions.controlInterlineHeight Component.onCompleted: { - if (preference.value) { + if (preference.value == "left") { box1.checked = true; } else { box2.checked = true; @@ -30,10 +30,10 @@ Preference { function save() { // Box1 = True, Box2 = False (Right Hand for Default) if (box1.checked && !box2.checked) { - preference.value = true; + preference.value = "left"; } if (!box1.checked && box2.checked) { - preference.value = false; + preference.value = "right"; } preference.save(); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0a3d9f4c74..9b42310c3c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -926,7 +926,7 @@ void MyAvatar::saveData() { Settings settings; settings.beginGroup("Avatar"); - settings.setValue("useAlternativeHand", _useAlternativeHand); + settings.setValue("dominantHand", _dominantHand); settings.setValue("headPitch", getHead()->getBasePitch()); settings.setValue("scale", _targetScale); @@ -1123,7 +1123,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool()); - setUseAlternativeHand(settings.value("useAlternativeHand", _useAlternativeHand).toBool()); + setDominantHand(settings.value("dominantHand", _dominantHand).toString().toLower()); settings.endGroup(); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8ff616b36b..05af26416c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -339,8 +339,13 @@ public: Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; } Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; } - Q_INVOKABLE void setUseAlternativeHand(bool hand) { _useAlternativeHand = hand; } - Q_INVOKABLE bool getUseAlternativeHand() const { return _useAlternativeHand; } + Q_INVOKABLE void setDominantHand(const QString& hand) { + if (hand == "left" || hand == "right") { + _dominantHand = hand; + } + } + + Q_INVOKABLE QString getDominantHand() const { return _dominantHand; } Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } @@ -722,7 +727,7 @@ private: QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; - bool _useAlternativeHand{ false }; // False defaults to right hand, true to left + QString _dominantHand{ "right" }; const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index fe1663b98d..fd0847c945 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -68,8 +68,8 @@ void setupPreferences() { preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter)); } { - auto getter = [=]()->bool { return myAvatar->getUseAlternativeHand(); }; - auto setter = [=](bool value) { myAvatar->setUseAlternativeHand(value); }; + auto getter = [=]()->QString { return myAvatar->getDominantHand(); }; + auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); }; preferences->addPreference(new PrimaryHandPreference(AVATAR_BASICS, "Dominant Hand", getter, setter)); } diff --git a/libraries/shared/src/Preferences.h b/libraries/shared/src/Preferences.h index 73cce1e909..6fa2cb9b1f 100644 --- a/libraries/shared/src/Preferences.h +++ b/libraries/shared/src/Preferences.h @@ -340,11 +340,11 @@ public: Type getType() override { return Checkbox; } }; -class PrimaryHandPreference : public BoolPreference { +class PrimaryHandPreference : public StringPreference { Q_OBJECT public: PrimaryHandPreference(const QString& category, const QString& name, Getter getter, Setter setter) - : BoolPreference(category, name, getter, setter) { } + : StringPreference(category, name, getter, setter) { } Type getType() override { return PrimaryHand; } }; From e6fd85f45ac4e748d54af6dbf749668e4c9b3a21 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 19:02:40 +0100 Subject: [PATCH 103/280] Some code cleanup --- .../dialogs/preferences/PrimaryHandPreference.qml | 4 ++-- interface/src/avatar/MyAvatar.cpp | 12 ++++++++++++ interface/src/avatar/MyAvatar.h | 12 +++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index c89756f678..d230f7e0da 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -89,10 +89,10 @@ Preference { verticalCenter: parent.verticalCenter } onClicked: { - if(box1.checked) { + if (box1.checked) { box1.checked = false; } - if(!box1.checked && !box2.checked) { + if (!box1.checked && !box2.checked) { box2.checked = true; } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9b42310c3c..90870f83c2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -72,6 +72,9 @@ const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // s const float YAW_SPEED_DEFAULT = 120.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec +const QString DOMINANT_HAND_LEFT = "left"; +const QString DOMINANT_HAND_RIGHT = "right"; + // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. float MAX_AVATAR_SPEED = 30.0f; @@ -87,6 +90,9 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; +const QString& DOMINANT_LEFT_HAND = "left"; +const QString& DOMINANT_RIGHT_HAND = "right"; + // default values, used when avatar is missing joints... (avatar space) static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; @@ -255,6 +261,12 @@ MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } +void MyAvatar::setDominantHand(const QString& hand) { + if (hand == DOMINANT_HAND_LEFT || hand == DOMINANT_HAND_RIGHT) { + _dominantHand = hand; + } +} + void MyAvatar::registerMetaTypes(QScriptEngine* engine) { QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); engine->globalObject().setProperty("MyAvatar", value); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 05af26416c..0f4d2c7464 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -43,6 +43,12 @@ enum AudioListenerMode { FROM_CAMERA, CUSTOM }; + +enum DominantHand { + LEFT_HAND, + RIGHT_HAND +}; + Q_DECLARE_METATYPE(AudioListenerMode); class MyAvatar : public Avatar { @@ -339,11 +345,7 @@ public: Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; } Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; } - Q_INVOKABLE void setDominantHand(const QString& hand) { - if (hand == "left" || hand == "right") { - _dominantHand = hand; - } - } + Q_INVOKABLE void setDominantHand(const QString& hand); Q_INVOKABLE QString getDominantHand() const { return _dominantHand; } From fd2264f7c92d170b4ce34714e01e2d922e3f629a Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 19:05:49 +0100 Subject: [PATCH 104/280] removed duplicate --- interface/src/avatar/MyAvatar.cpp | 3 --- interface/src/avatar/MyAvatar.h | 5 ----- 2 files changed, 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 90870f83c2..a0086723ae 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -72,9 +72,6 @@ const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // s const float YAW_SPEED_DEFAULT = 120.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec -const QString DOMINANT_HAND_LEFT = "left"; -const QString DOMINANT_HAND_RIGHT = "right"; - // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. float MAX_AVATAR_SPEED = 30.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0f4d2c7464..1239989c05 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -44,11 +44,6 @@ enum AudioListenerMode { CUSTOM }; -enum DominantHand { - LEFT_HAND, - RIGHT_HAND -}; - Q_DECLARE_METATYPE(AudioListenerMode); class MyAvatar : public Avatar { From 64ba159adf325f5a00fc71b7e87d97242a34ce7a Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 19:12:14 +0100 Subject: [PATCH 105/280] Updating Small Reference --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1239989c05..dc306bd09a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -724,7 +724,7 @@ private: QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; - QString _dominantHand{ "right" }; + QString _dominantHand{ DOMINANT_RIGHT_HAND }; const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg From 9ff7891c880e94f593eba9db9d468c68fe188928 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 19:56:02 +0100 Subject: [PATCH 106/280] Fixed reference error, Gustavo should build now. --- interface/src/avatar/MyAvatar.cpp | 5 +---- interface/src/avatar/MyAvatar.h | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a0086723ae..081ab8e8f1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -87,9 +87,6 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -const QString& DOMINANT_LEFT_HAND = "left"; -const QString& DOMINANT_RIGHT_HAND = "right"; - // default values, used when avatar is missing joints... (avatar space) static const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; static const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f }; @@ -259,7 +256,7 @@ MyAvatar::~MyAvatar() { } void MyAvatar::setDominantHand(const QString& hand) { - if (hand == DOMINANT_HAND_LEFT || hand == DOMINANT_HAND_RIGHT) { + if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) { _dominantHand = hand; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index dc306bd09a..1657f78b26 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -142,6 +142,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) + const QString DOMINANT_LEFT_HAND = "left"; + const QString DOMINANT_RIGHT_HAND = "right"; + public: enum DriveKeys { TRANSLATE_X = 0, @@ -341,7 +344,6 @@ public: Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; } Q_INVOKABLE void setDominantHand(const QString& hand); - Q_INVOKABLE QString getDominantHand() const { return _dominantHand; } Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; } @@ -724,7 +726,7 @@ private: QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; - QString _dominantHand{ DOMINANT_RIGHT_HAND }; + QString _dominantHand { DOMINANT_RIGHT_HAND }; const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg From de6975b22754c6deb8438d234fbb77eb972cb728 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 24 Jul 2017 17:16:13 -0400 Subject: [PATCH 107/280] add missing cursor-*.pngs --- interface/resources/images/cursor-arrow.png | Bin 0 -> 3562 bytes interface/resources/images/cursor-link.png | Bin 0 -> 44403 bytes interface/resources/images/cursor-none.png | Bin 0 -> 133 bytes interface/resources/images/cursor-reticle.png | Bin 0 -> 9234 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/images/cursor-arrow.png create mode 100644 interface/resources/images/cursor-link.png create mode 100644 interface/resources/images/cursor-none.png create mode 100644 interface/resources/images/cursor-reticle.png diff --git a/interface/resources/images/cursor-arrow.png b/interface/resources/images/cursor-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..408881b5856eb47a30875e3e56d5864721161587 GIT binary patch literal 3562 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 R^nCyTaI$x`E7(oB_+R=!c8UN1 literal 0 HcmV?d00001 diff --git a/interface/resources/images/cursor-link.png b/interface/resources/images/cursor-link.png new file mode 100644 index 0000000000000000000000000000000000000000..67ff4c7b6793a8f2f5107d9710bc553f7ff6d10a GIT binary patch literal 44403 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ó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 literal 0 HcmV?d00001 diff --git a/interface/resources/images/cursor-reticle.png b/interface/resources/images/cursor-reticle.png new file mode 100644 index 0000000000000000000000000000000000000000..2d9ba8fd659c9172c1333b96ee8e3a7deb273b9c GIT binary patch literal 9234 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 literal 0 HcmV?d00001 From 223ed5b59a11a41890b89e234bb6015775e96513 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 14:11:45 -0700 Subject: [PATCH 108/280] Hack to mod tablet orientation when clicking context overlay --- .../src/ui/overlays/ContextOverlayInterface.cpp | 13 +++++++++++++ interface/src/ui/overlays/ContextOverlayInterface.h | 2 ++ scripts/system/tablet-ui/tabletUI.js | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83c86bfd82..6069ecf6e4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,6 +33,18 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { + if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { + QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); + QVariantMap props; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); + props.insert("position", vec3toVariant(position)); + props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + qApp->getOverlays().editOverlay(tabletFrameID, props); + _contextOverlayJustClicked = false; + } + }); } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; @@ -149,6 +161,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); + _contextOverlayJustClicked = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f06f85eab7..812914a82e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -19,6 +19,7 @@ #include #include #include +#include "avatar/AvatarManager.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" @@ -66,6 +67,7 @@ private: bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; + bool _contextOverlayJustClicked { false }; void openMarketplace(); }; diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 9188f39a2e..257a56bf09 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -104,7 +104,6 @@ function showTabletUI() { checkTablet() - gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -123,6 +122,7 @@ Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); } + gTablet.tabletShown = true; } function hideTabletUI() { From 9c152ddc6f7d0c86c88b8423a8ee64f295351a29 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 22:30:43 +0100 Subject: [PATCH 109/280] Ticking left will keep it on left, not right --- .../resources/qml/dialogs/preferences/PrimaryHandPreference.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index d230f7e0da..07b9c958dd 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -74,7 +74,7 @@ Preference { box2.checked = false; } if (!box1.checked && !box2.checked) { - box2.checked = true; + box1.checked = true; } } colorScheme: hifi.colorSchemes.dark From 507368726f21561af92fd917c9938a4a8fb5a868 Mon Sep 17 00:00:00 2001 From: Rob Kayson Date: Mon, 24 Jul 2017 14:38:22 -0700 Subject: [PATCH 110/280] added dart scripts --- .../marketplace/dart/createDart.js | 91 +++++++++++++++++++ unpublishedScripts/marketplace/dart/dart.js | 81 +++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 unpublishedScripts/marketplace/dart/createDart.js create mode 100644 unpublishedScripts/marketplace/dart/dart.js diff --git a/unpublishedScripts/marketplace/dart/createDart.js b/unpublishedScripts/marketplace/dart/createDart.js new file mode 100644 index 0000000000..ff13a695a8 --- /dev/null +++ b/unpublishedScripts/marketplace/dart/createDart.js @@ -0,0 +1,91 @@ +"use strict"; +// +// createDart.js +// +// Created by MrRoboman on 17/05/04 +// Copyright 2017 High Fidelity, Inc. +// +// Creates five throwing darts. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var MODEL_URL = "https://hifi-content.s3.amazonaws.com/wadewatts/dart.fbx"; +var SCRIPT_URL = Script.resolvePath("./dart.js?v=" + Date.now()); +var START_POSITION = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 0.75)); +var START_ROTATION = MyAvatar.orientation +var SCALE_FACTOR = 1; + +var dart = { + type: "Model", + shapeType: "box", + name: "Dart", + description: "Throw it at something!", + script: SCRIPT_URL, + modelURL: MODEL_URL, + position: START_POSITION, + rotation: START_ROTATION, + lifetime: 300, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.1, + y: 0.1, + z: 0.3 + }, + dynamic: true, + owningAvatarID: MyAvatar.sessionUUID, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + invertSolidWhileHeld: true, + ignoreIK: false + } + }) +}; + +var avatarUp = Quat.getUp(MyAvatar.orientation); +var platformPosition = Vec3.sum(START_POSITION, Vec3.multiply(avatarUp, -0.05)); +var platform = { + type: "Box", + name: "Dart Platform", + description: "Holds darts", + position: platformPosition, + rotation: START_ROTATION, + lifetime: 60, + dimensions: { + x: 0.15 * 5, + y: 0.01, + z: 0.3 + }, + color: { + red: 129, + green: 92, + blue: 11 + }, + owningAvatarID: MyAvatar.sessionUUID, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + invertSolidWhileHeld: true, + ignoreIK: false + } + }) +}; + + +Entities.addEntity(platform); + +var dartCount = 5; +var avatarRight = Quat.getRight(MyAvatar.orientation); +for (var i = 0; i < dartCount; i++) { + var j = i - Math.floor(dartCount / 2); + var position = Vec3.sum(START_POSITION, Vec3.multiply(avatarRight, 0.15 * j)); + dart.position = position; + Entities.addEntity(dart); +} + +Script.stop(); diff --git a/unpublishedScripts/marketplace/dart/dart.js b/unpublishedScripts/marketplace/dart/dart.js new file mode 100644 index 0000000000..9debe910e3 --- /dev/null +++ b/unpublishedScripts/marketplace/dart/dart.js @@ -0,0 +1,81 @@ +"use strict"; +// +// dart.js +// +// Created by MrRoboman on 17/05/13 +// Copyright 2017 High Fidelity, Inc. +// +// Simple throwing dart. Sticks to static objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +(function() { + var THROW_FACTOR = 3; + var DART_SOUND_URL = Script.resolvePath('https://hifi-content.s3.amazonaws.com/wadewatts/dart.wav?v=' + Date.now()); + + var Dart = function() {}; + + Dart.prototype = { + + preload: function(entityID) { + this.entityID = entityID; + this.actionID = null; + this.soundHasPlayed = true; + this.dartSound = SoundCache.getSound(DART_SOUND_URL); + }, + + playDartSound: function(sound) { + if (this.soundHasPlayed) { + return; + } + this.soundHasPlayed = true; + var position = Entities.getEntityProperties(this.entityID, 'position').position; + var audioProperties = { + volume: 0.15, + position: position + }; + Audio.playSound(this.dartSound, audioProperties); + }, + + releaseGrab: function() { + this.soundHasPlayed = false; + var velocity = Entities.getEntityProperties(this.entityID, 'velocity').velocity; + + var newVelocity = {}; + Object.keys(velocity).forEach(function(key) { + newVelocity[key] = velocity[key] * THROW_FACTOR; + }); + + Entities.editEntity(this.entityID, { + velocity: newVelocity + }); + + if (this.actionID) { + Entities.deleteAction(this.entityID, this.actionID); + } + this.actionID = Entities.addAction("travel-oriented", this.entityID, { + forward: { x: 0, y: 0, z: 1 }, + angularTimeScale: 0.1, + tag: "throwing dart", + ttl: 3600 + }); + }, + + collisionWithEntity: function(myID, otherID, collisionInfo) { + this.playDartSound(); + + Entities.editEntity(myID, { + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0} + }); + + if (this.actionID) { + Entities.deleteAction(myID, this.actionID); + this.actionID = null; + } + } + }; + + return new Dart(); +}); From 7de164f1ab6804dedec413320415bcde8784da55 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 15:06:45 -0700 Subject: [PATCH 111/280] moved below a check that returns. --- scripts/system/libraries/entitySelectionTool.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index f9081080fd..91ddf2a2d1 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -4064,7 +4064,6 @@ SelectionDisplay = (function() { // if another mouse button than left is pressed ignore it return false; } - entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); var somethingClicked = false; var pickRay = generalComputePickRay(event.x, event.y); @@ -4075,6 +4074,8 @@ SelectionDisplay = (function() { return false; } + entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); + // before we do a ray test for grabbers, disable the ray intersection for our selection box Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true From 84609f27e36e161d4fb36039823396ec659e5fdf Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 23:53:03 +0100 Subject: [PATCH 112/280] Final Change - They're now Radio Boxes. --- interface/resources/qml/controls-uit/RadioButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index 76b7902435..3cc3db4e46 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -32,7 +32,7 @@ Original.RadioButton { id: box width: boxSize height: boxSize - radius: boxRadius + radius: 7 gradient: Gradient { GradientStop { position: 0.2 @@ -52,7 +52,7 @@ Original.RadioButton { id: check width: checkSize height: checkSize - radius: checkRadius + radius: 7 anchors.centerIn: parent color: hifi.colors.checkBoxChecked border.width: 1 From e53badf88b01c0d8b69551337a0105e31fddebef Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 15:56:37 -0700 Subject: [PATCH 113/280] use alt+f for 1st person camera and alt+o for 3rd --- interface/src/Application.cpp | 13 +------------ interface/src/Menu.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 03f62221ef..8113001cda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3020,20 +3020,9 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_F: { - if (isMeta) { - auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FirstPerson)) { - menu->setIsOptionChecked(MenuOption::ThirdPerson, true); - menu->setIsOptionChecked(MenuOption::FirstPerson, false); - } else { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - menu->setIsOptionChecked(MenuOption::ThirdPerson, false); - } - cameraMenuChanged(); - } else if (isOption) { + if (isOption) { _physicsEngine->dumpNextStats(); } - break; } case Qt::Key_H: { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c7223be208..f1ed0497c6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -222,14 +222,14 @@ Menu::Menu() { cameraModeGroup->setExclusive(true); // View > First Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::FirstPerson, 0, - true, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + viewMenu, MenuOption::FirstPerson, Qt::ALT | Qt::Key_F, + true, qApp, SLOT(cameraMenuChanged()))); // View > Third Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::ThirdPerson, 0, - false, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_O, + false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, From f0871c6878ff614b5b2f3d775ea161e0edb8ae91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:06:15 -0700 Subject: [PATCH 114/280] avoid unnecessary and expensive gets --- interface/src/avatar/MyAvatar.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b32ef4024e..7910df90ec 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1643,10 +1643,13 @@ void MyAvatar::prepareForPhysicsSimulation() { } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { - glm::vec3 position = getPosition(); - glm::quat orientation = getOrientation(); + glm::vec3 position; + glm::quat orientation; if (_characterController.isEnabledAndReady()) { _characterController.getPositionAndOrientation(position, orientation); + } else { + position = getPosition(); + orientation = getOrientation(); } nextAttitude(position, orientation); _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); From 29be9aee65a14e314905dead6dc8229f6667b1f2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:34:44 -0700 Subject: [PATCH 115/280] move nextAttitude() from AvatarData to MyAvatar --- interface/src/avatar/MyAvatar.cpp | 17 +++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + .../src/avatars-renderer/Avatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 17 ----------------- libraries/avatars/src/AvatarData.h | 3 --- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7910df90ec..2e3b9a584e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1642,6 +1642,23 @@ void MyAvatar::prepareForPhysicsSimulation() { _prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix); } +// There are a number of possible strategies for this set of tools through endRender, below. +void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { + bool success; + Transform trans = getTransform(success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + return; + } + trans.setTranslation(position); + trans.setRotation(orientation); + SpatiallyNestable::setTransform(trans, success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + } + updateAttitude(); +} + void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { glm::vec3 position; glm::quat orientation; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 648a5b5f29..86321137d4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -432,6 +432,7 @@ public: void updateMotors(); void prepareForPhysicsSimulation(); + void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 89db519abc..db9d36be7a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -184,7 +184,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() override { _skeletonModel->updateAttitude(); } + virtual void updateAttitude() { _skeletonModel->updateAttitude(); } // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6fdb4d1ef6..85228af941 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -110,23 +110,6 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() { return _defaultFullAvatarModelUrl; } -// There are a number of possible strategies for this set of tools through endRender, below. -void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { - bool success; - Transform trans = getTransform(success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - return; - } - trans.setTranslation(position); - trans.setRotation(orientation); - SpatiallyNestable::setTransform(trans, success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - } - updateAttitude(); -} - void AvatarData::setTargetScale(float targetScale) { auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); if (_targetScale != newValue) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 16768ec62a..1cf7d7dd91 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -445,9 +445,6 @@ public: using SpatiallyNestable::setOrientation; virtual void setOrientation(const glm::quat& orientation) override; - void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. - virtual void updateAttitude() {} // Tell skeleton mesh about changes - glm::quat getHeadOrientation() const { lazyInitHeadData(); return _headData->getOrientation(); From f598b1f1f581761010f45ca0f7907a71943c484f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:56:05 -0700 Subject: [PATCH 116/280] fix spelling typo in comment --- interface/src/avatar/MyHead.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 9f2d080cd6..7fc6b9fa26 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -29,7 +29,7 @@ MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) { glm::quat MyHead::getHeadOrientation() const { // NOTE: Head::getHeadOrientation() is not used for orienting the camera "view" while in Oculus mode, so // you may wonder why this code is here. This method will be called while in Oculus mode to determine how - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // to change the driving direction while in Oculus mode. It is used to support driving toward where your // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not // always the same. @@ -39,7 +39,7 @@ glm::quat MyHead::getHeadOrientation() const { return headPose.rotation * Quaternions::Y_180; } - return myAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); + return myAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); } void MyHead::simulate(float deltaTime) { From 49942832474dd2638100d4114ac7d0ab68466f9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:57:18 -0700 Subject: [PATCH 117/280] remove unnecessary state check --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index bd4d1201c7..9a7abc4e98 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -445,7 +445,7 @@ void CharacterController::handleChangedCollisionGroup() { void CharacterController::updateUpAxis(const glm::quat& rotation) { _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (_state != State::Hover && _rigidBody) { + if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); } } From 8c55476c65feb3762141403cc6225bd9f8b85699 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:58:39 -0700 Subject: [PATCH 118/280] fix motor direction when walking upside down also maintain worldUp and remove unnecessary cruft --- interface/src/Application.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 6 +-- interface/src/avatar/MyAvatar.h | 1 - .../src/avatars-renderer/Avatar.cpp | 38 ++++++------------- .../src/avatars-renderer/Avatar.h | 13 ++----- .../src/avatars-renderer/SkeletonModel.cpp | 6 +-- .../src/avatars-renderer/SkeletonModel.h | 2 +- libraries/avatars/src/AvatarData.cpp | 14 +++---- libraries/avatars/src/AvatarData.h | 12 +++--- libraries/shared/src/shared/Camera.h | 2 +- 10 files changed, 38 insertions(+), 60 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dbb94cfdae..166b4fee72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2314,7 +2314,7 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); + auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); // Mirror HMD yaw and roll @@ -2336,7 +2336,7 @@ void Application::paintGL() { + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); } else { - _myCamera.setOrientation(myAvatar->getWorldAlignedOrientation() + _myCamera.setOrientation(myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2e3b9a584e..b644defde2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1291,7 +1291,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() { } glm::vec3 MyAvatar::getDefaultEyePosition() const { - return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); + return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = 1.0f + 1.0f; @@ -1588,7 +1588,7 @@ void MyAvatar::updateMotors() { // non-hovering = walking: follow camera twist about vertical but not lift // so we decompose camera's rotation and store the twist part in motorRotation glm::quat liftRotation; - swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation); + motorRotation = getOrientation(); } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; @@ -1656,7 +1656,7 @@ void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { if (!success) { qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; } - updateAttitude(); + updateAttitude(orientation); } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 86321137d4..2f8f838cf9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -552,7 +552,6 @@ public: Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; - public slots: void increaseSize(); void decreaseSize(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4016592d0a..06814c2707 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -151,11 +151,6 @@ glm::vec3 Avatar::getNeckPosition() const { return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition(); } - -glm::quat Avatar::getWorldAlignedOrientation () const { - return computeRotationFromBodyToWorldUp() * getOrientation(); -} - AABox Avatar::getBounds() const { if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) { // approximately 2m tall, scaled to user request. @@ -436,6 +431,11 @@ void Avatar::slamPosition(const glm::vec3& newPosition) { _lastVelocity = glm::vec3(0.0f); } +void Avatar::updateAttitude(const glm::quat& orientation) { + _skeletonModel->updateAttitude(orientation); + _worldUpDirection = orientation * Vectors::UNIT_Y; +} + void Avatar::applyPositionDelta(const glm::vec3& delta) { setPosition(getPosition() + delta); _positionDeltaAccumulator += delta; @@ -628,22 +628,6 @@ void Avatar::render(RenderArgs* renderArgs) { } } -glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { - glm::quat orientation = getOrientation(); - glm::vec3 currentUp = orientation * IDENTITY_UP; - float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)); - if (angle < EPSILON) { - return glm::quat(); - } - glm::vec3 axis; - if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis - axis = orientation * IDENTITY_RIGHT; - } else { - axis = glm::normalize(glm::cross(currentUp, _worldUpDirection)); - } - return glm::angleAxis(angle * proportion, axis); -} - void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _attachmentsToDelete.clear(); @@ -1401,14 +1385,14 @@ glm::quat Avatar::getUncachedRightPalmRotation() const { return rightPalmRotation; } -void Avatar::setPosition(const glm::vec3& position) { - AvatarData::setPosition(position); - updateAttitude(); +void Avatar::setPositionViaScript(const glm::vec3& position) { + setPosition(position); + updateAttitude(getOrientation()); } -void Avatar::setOrientation(const glm::quat& orientation) { - AvatarData::setOrientation(orientation); - updateAttitude(); +void Avatar::setOrientationViaScript(const glm::quat& orientation) { + setOrientation(orientation); + updateAttitude(orientation); } void Avatar::updatePalms() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index db9d36be7a..2c75012209 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -112,8 +112,6 @@ public: const Head* getHead() const { return static_cast(_headData); } Head* getHead() { return static_cast(_headData); } - glm::quat getWorldAlignedOrientation() const; - AABox getBounds() const; /// Returns the distance to use as a LOD parameter. @@ -184,7 +182,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() { _skeletonModel->updateAttitude(); } + virtual void updateAttitude(const glm::quat& orientation) override; // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity @@ -197,10 +195,8 @@ public: void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); float computeMass(); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + void setPositionViaScript(const glm::vec3& position) override; + void setOrientationViaScript(const glm::quat& orientation) override; // these call through to the SpatiallyNestable versions, but they are here to expose these to javascript. Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } @@ -240,7 +236,7 @@ public: bool hasNewJointData() const { return _hasNewJointData; } float getBoundingRadius() const; - + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); bool isInScene() const { return render::Item::isValidID(_renderItemID); } @@ -303,7 +299,6 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); float getSkeletonHeight() const; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 9651951b46..c0d5fc07d7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -118,16 +118,16 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig.updateFromEyeParameters(eyeParams); } -void SkeletonModel::updateAttitude() { +void SkeletonModel::updateAttitude(const glm::quat& orientation) { setTranslation(_owningAvatar->getSkeletonPosition()); - setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180); + setRotation(orientation * Quaternions::Y_180); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); } // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), // but just before head has been simulated. void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { - updateAttitude(); + updateAttitude(_owningAvatar->getOrientation()); if (fullUpdate) { setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients()); diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index e48884c581..919e82825c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -35,7 +35,7 @@ public: void simulate(float deltaTime, bool fullUpdate = true) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateAttitude(); + void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 85228af941..9570056353 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -91,9 +91,6 @@ AvatarData::AvatarData() : _targetVelocity(0.0f), _density(DEFAULT_AVATAR_DENSITY) { - setBodyPitch(0.0f); - setBodyYaw(-90.0f); - setBodyRoll(0.0f); } AvatarData::~AvatarData() { @@ -2083,6 +2080,7 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { currentBasis = std::make_shared(Transform::fromJson(json[JSON_AVATAR_BASIS])); } + glm::quat orientation; if (json.contains(JSON_AVATAR_RELATIVE)) { // During playback you can either have the recording basis set to the avatar current state // meaning that all playback is relative to this avatars starting position, or @@ -2094,12 +2092,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]); auto worldTransform = currentBasis->worldTransform(relativeTransform); setPosition(worldTransform.getTranslation()); - setOrientation(worldTransform.getRotation()); + orientation = worldTransform.getRotation(); } else { // We still set the position in the case that there is no movement. setPosition(currentBasis->getTranslation()); - setOrientation(currentBasis->getRotation()); + orientation = currentBasis->getRotation(); } + setOrientation(orientation); + updateAttitude(orientation); // Do after avatar orientation because head look-at needs avatar orientation. if (json.contains(JSON_AVATAR_HEAD)) { @@ -2217,11 +2217,11 @@ void AvatarData::setBodyRoll(float bodyRoll) { setOrientation(glm::quat(glm::radians(eulerAngles))); } -void AvatarData::setPosition(const glm::vec3& position) { +void AvatarData::setPositionViaScript(const glm::vec3& position) { SpatiallyNestable::setPosition(position); } -void AvatarData::setOrientation(const glm::quat& orientation) { +void AvatarData::setOrientationViaScript(const glm::quat& orientation) { SpatiallyNestable::setOrientation(orientation); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1cf7d7dd91..0e936c49e0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -351,14 +351,14 @@ public: class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale) Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientationViaScript) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) @@ -440,10 +440,10 @@ public: float getBodyRoll() const; void setBodyRoll(float bodyRoll); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + virtual void setPositionViaScript(const glm::vec3& position); + virtual void setOrientationViaScript(const glm::quat& orientation); + + virtual void updateAttitude(const glm::quat& orientation) {} glm::quat getHeadOrientation() const { lazyInitHeadData(); diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 5f2162ff6e..c7b943f0dd 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -127,7 +127,7 @@ private: glm::mat4 _projection; // derived - glm::vec3 _position; + glm::vec3 _position { 0.0f, 0.0f, 0.0f }; glm::quat _orientation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; From 82a787a753ddefaa19c3531c37c719a9fcc07320 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 16:04:11 -0700 Subject: [PATCH 119/280] use alt+h for mirror-mode --- interface/src/Application.cpp | 31 ++++++++++--------------------- interface/src/Menu.cpp | 6 +++--- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8113001cda..4194ac9ecb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3025,21 +3025,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } } - case Qt::Key_H: { - if (isMeta && !isHMDMode()) { - auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { - menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - } else { - menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); - menu->setIsOptionChecked(MenuOption::FirstPerson, false); - } - } - cameraMenuChanged(); - break; - } - case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; @@ -4476,27 +4461,31 @@ void Application::cameraModeChanged() { void Application::cameraMenuChanged() { - if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { - if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { + auto menu = Menu::getInstance(); + if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { + if (isHMDMode()) { + menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + } else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { + } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::ThirdPerson)) { + } else if (menu->isOptionChecked(MenuOption::ThirdPerson)) { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) { + } else if (menu->isOptionChecked(MenuOption::IndependentMode)) { if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { _myCamera.setMode(CAMERA_MODE_INDEPENDENT); } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { + } else if (menu->isOptionChecked(MenuOption::CameraEntityMode)) { if (_myCamera.getMode() != CAMERA_MODE_ENTITY) { _myCamera.setMode(CAMERA_MODE_ENTITY); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f1ed0497c6..c6679f3bf6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -232,9 +232,9 @@ Menu::Menu() { false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::FullscreenMirror, 0, - false, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + viewMenu, MenuOption::FullscreenMirror, Qt::ALT | Qt::Key_H, + false, qApp, SLOT(cameraMenuChanged()))); // View > Independent [advanced] cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, From 97756e6140dc1677282c114bc008ae927ac5c5ea Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 16:12:03 -0700 Subject: [PATCH 120/280] accidently deleted a break --- interface/src/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4194ac9ecb..1ed6c5078a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3023,6 +3023,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isOption) { _physicsEngine->dumpNextStats(); } + break; } case Qt::Key_Asterisk: From 4c78fba2ccb81c4e29185d40ea7568a250c402a3 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 16:50:33 -0700 Subject: [PATCH 121/280] Removed line that caused colflict. --- scripts/system/libraries/entitySelectionTool.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 91ddf2a2d1..725803f824 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -4074,8 +4074,6 @@ SelectionDisplay = (function() { return false; } - entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); - // before we do a ray test for grabbers, disable the ray intersection for our selection box Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true From 036a16c0b384f69cf5949645315f0e266b24a0fc Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 16:51:23 -0700 Subject: [PATCH 122/280] readded line that caused colflict. --- scripts/system/libraries/entitySelectionTool.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2d1853fae2..77b62913bf 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -4060,6 +4060,8 @@ SelectionDisplay = (function() { return false; } + entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); + // ignore ray intersection for our selection box and yaw/pitch/roll result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] ); if (result.intersects) { From c22798a8e64adfbd0682b5be512b8c06a5b7dd25 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 17:16:07 -0700 Subject: [PATCH 123/280] Offset tablet position to left of screen per feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6069ecf6e4..a9efc8e6f6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -38,9 +38,8 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); - props.insert("position", vec3toVariant(position)); - props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } From 9f6641ed1087d5926ea4d95378af20ab8501b60a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 24 Jul 2017 17:22:48 -0700 Subject: [PATCH 124/280] Shoulder puck calibration work in progress * AnimInverseKinematics: debug draw for secondary targets * AnimInverseKienmatics: better clean up of ik target debug draw * GeometryUtil: added findPlaneFromPoints() * ViveControllerManager: external dependency on eigen * ViveControllerManager: record history of left/right hand controllers * ViveControllerManager: use history to determine user shoulder location for better calibration * ViveControllerManager: pass defaultToReferenceMat by const ref to calibrate functions. * CMake: added external depenency to eigen linear algebra library. --- cmake/externals/eigen/CMakeLists.txt | 20 +++ cmake/modules/FindEigen.cmake | 26 ++++ .../resources/avatar/avatar-animation.json | 18 --- interface/src/Application.cpp | 2 +- .../animation/src/AnimInverseKinematics.cpp | 29 +++- libraries/shared/src/GeometryUtil.cpp | 52 ++++++++ libraries/shared/src/GeometryUtil.h | 2 + plugins/openvr/CMakeLists.txt | 6 + plugins/openvr/src/ViveControllerManager.cpp | 126 ++++++++++++++---- plugins/openvr/src/ViveControllerManager.h | 27 ++-- 10 files changed, 252 insertions(+), 56 deletions(-) create mode 100644 cmake/externals/eigen/CMakeLists.txt create mode 100644 cmake/modules/FindEigen.cmake diff --git a/cmake/externals/eigen/CMakeLists.txt b/cmake/externals/eigen/CMakeLists.txt new file mode 100644 index 0000000000..15d94576cc --- /dev/null +++ b/cmake/externals/eigen/CMakeLists.txt @@ -0,0 +1,20 @@ +set(EXTERNAL_NAME eigen) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://bitbucket.org/eigen/eigen/get/3.3.4.zip + URL_MD5 e337acc279874bc6a56da4d973a723fb + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR} CACHE PATH "List of eigen include directories") \ No newline at end of file diff --git a/cmake/modules/FindEigen.cmake b/cmake/modules/FindEigen.cmake new file mode 100644 index 0000000000..4b09ed8f2b --- /dev/null +++ b/cmake/modules/FindEigen.cmake @@ -0,0 +1,26 @@ +# +# FindEigen.cmake +# +# Try to find Eigen include path. +# Once done this will define +# +# EIGEN_INCLUDE_DIRS +# +# Created on 7/14/2017 by Anthony Thibault +# Copyright 2017 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +# setup hints for Eigen search +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("eigen") + +# locate dir +string(CONCAT EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} "/src/eigen") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(EIGEN DEFAULT_MSG EIGEN_INCLUDE_DIRS) + +mark_as_advanced(EIGEN_INCLUDE_DIRS EIGEN_SEARCH_DIRS) \ No newline at end of file diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index a179f452c1..ee2b916d1e 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -61,24 +61,6 @@ "weight": 1.0, "flexCoefficients": [1] }, - { - "jointName": "LeftArm", - "positionVar": "leftArmPosition", - "rotationVar": "leftArmRotation", - "typeVar": "leftArmType", - "weightVar": "leftArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, - { - "jointName": "RightArm", - "positionVar": "rightArmPosition", - "rotationVar": "rightArmRotation", - "typeVar": "rightArmType", - "weightVar": "rightArmWeight", - "weight": 0.75, - "flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0] - }, { "jointName": "RightHand", "positionVar": "rightHandPosition", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 37001b55bb..7b10c9a364 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4811,10 +4811,10 @@ void Application::update(float deltaTime) { controller::Action::RIGHT_TOE_BASE }; + // copy controller poses from userInputMapper to myAvatar. glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix()); glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; - for (auto& action : avatarControllerActions) { controller::Pose pose = userInputMapper->getPoseState(action); myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix)); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 86ebe5fc91..908519880d 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -23,6 +23,8 @@ #include "CubicHermiteSpline.h" #include "AnimUtil.h" +static const int MAX_TARGET_MARKERS = 30; + static void lookupJointChainInfo(AnimInverseKinematics::JointChainInfo* jointChainInfos, size_t numJointChainInfos, int indexA, int indexB, AnimInverseKinematics::JointChainInfo** jointChainInfoA, @@ -94,6 +96,12 @@ AnimInverseKinematics::~AnimInverseKinematics() { _rotationAccumulators.clear(); _translationAccumulators.clear(); _targetVarVec.clear(); + + // remove markers + for (int i = 0; i < MAX_TARGET_MARKERS; i++) { + QString name = QString("ikTarget%1").arg(i); + DebugDraw::getInstance().removeMyAvatarMarker(name); + } } void AnimInverseKinematics::loadDefaultPoses(const AnimPoseVec& poses) { @@ -898,19 +906,30 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // debug render ik targets if (context.getEnableDebugDrawIKTargets()) { const vec4 WHITE(1.0f); + const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f); glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); + int targetNum = 0; for (auto& target : targets) { glm::mat4 geomTargetMat = createMatFromQuatAndPos(target.getRotation(), target.getTranslation()); glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat; - QString name = QString("ikTarget%1").arg(target.getIndex()); + QString name = QString("ikTarget%1").arg(targetNum); DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), WHITE); + targetNum++; + } + + // draw secondary ik targets + for (auto& iter : _secondaryTargetsInRigFrame) { + glm::mat4 avatarTargetMat = rigToAvatarMat * (glm::mat4)iter.second; + QString name = QString("ikTarget%1").arg(targetNum); + DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), GREEN); + targetNum++; } } else if (context.getEnableDebugDrawIKTargets() != _previousEnableDebugIKTargets) { // remove markers if they were added last frame. - for (auto& target : targets) { - QString name = QString("ikTarget%1").arg(target.getIndex()); + for (int i = 0; i < MAX_TARGET_MARKERS; i++) { + QString name = QString("ikTarget%1").arg(i); DebugDraw::getInstance().removeMyAvatarMarker(name); } } @@ -1744,12 +1763,16 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { AnimPose absPose = rigToGeometryPose * iter.second; + absPose.scale() = glm::vec3(1.0f); AnimPose parentAbsPose; int parentIndex = _skeleton->getParentIndex(iter.first); if (parentIndex >= 0) { parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); } + // AJT: for now ignore translation on secondary poses. + glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; + _relativePoses[iter.first].trans() = origTrans; } } diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index f853240fe3..4ae907eb3b 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -605,3 +605,55 @@ float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirectio return glm::max(0.0f, theta - phi); } + +// given a set of points, compute a best fit plane that passes as close as possible through all the points. +// http://www.ilikebigbits.com/blog/2015/3/2/plane-from-points +bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& planeNormalOut, glm::vec3& pointOnPlaneOut) { + if (numPoints < 3) { + return false; + } + glm::vec3 sum; + for (size_t i = 0; i < numPoints; i++) { + sum += points[i]; + } + glm::vec3 centroid = sum * (1.0f / (float)numPoints); + float xx = 0.0f, xy = 0.0f, xz = 0.0f; + float yy = 0.0f, yz = 0.0f, zz = 0.0f; + + for (size_t i = 0; i < numPoints; i++) { + glm::vec3 r = points[i] - centroid; + xx += r.x * r.x; + xy += r.x * r.y; + xz += r.x * r.z; + yy += r.y * r.y; + yz += r.y * r.z; + zz += r.z * r.z; + } + + float det_x = yy * zz - yz * yz; + float det_y = xx * zz - xz * xz; + float det_z = xx * yy - xy * xy; + float det_max = std::max(std::max(det_x, det_y), det_z); + + if (det_max == 0.0f) { + return false; // The points don't span a plane + } + + glm::vec3 dir; + if (det_max == det_x) { + float a = (xz * yz - xy * zz) / det_x; + float b = (xy * yz - xz * yy) / det_x; + dir = glm::vec3(1.0f, a, b); + } else if (det_max == det_y) { + float a = (yz * xz - xy * zz) / det_y; + float b = (xy * xz - yz * xx) / det_y; + dir = glm::vec3(a, 1.0f, b); + } else { + float a = (yz * xy - xz * yy) / det_z; + float b = (xz * xy - yz * xx) / det_z; + dir = glm::vec3(a, b, 1.0f); + } + pointOnPlaneOut = centroid; + planeNormalOut = glm::normalize(dir); + return true; +} diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 857d423896..a5ee67748b 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -163,5 +163,7 @@ private: static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB); }; +// given a set of points, compute a best fit plane that passes as close as possible through all the points. +bool findPlaneFromPoints(const glm::vec3* points, size_t numPoints, glm::vec3& planeNormalOut, glm::vec3& pointOnPlaneOut); #endif // hifi_GeometryUtil_h diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 6a95ef6d76..6633b99b31 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -18,8 +18,14 @@ if (WIN32) include_hifi_library_headers(octree) add_dependency_external_projects(OpenVR) + add_dependency_external_projects(eigen) + find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) + + # header only library + find_package(eigen REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${EIGEN_INCLUDE_DIRS}) endif() diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index ddafa5cb5b..02d3afd713 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -35,6 +35,7 @@ #include #include +#include extern PoseData _nextSimPoseData; @@ -282,7 +283,13 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu } } -ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) { +static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; + +ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : + controller::InputDevice("Vive"), + _system(system), + _leftControllerHistory(CONTROLLER_HISTORY_SIZE), + _rightControllerHistory(CONTROLLER_HISTORY_SIZE) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); @@ -534,7 +541,7 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr } } -bool ViveControllerManager::InputDevice::configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureHands(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksXPosition); int puckCount = (int)_validTrackedObjects.size(); if (_handConfig == HandConfig::Pucks && puckCount >= MIN_PUCK_COUNT) { @@ -569,7 +576,7 @@ bool ViveControllerManager::InputDevice::configureHands(glm::mat4& defaultToRefe return false; } -bool ViveControllerManager::InputDevice::configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition); int puckCount = (int)_validTrackedObjects.size(); if (_headConfig == HeadConfig::Puck && puckCount >= MIN_HEAD) { @@ -583,7 +590,7 @@ bool ViveControllerManager::InputDevice::configureHead(glm::mat4& defaultToRefer return false; } -bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +bool ViveControllerManager::InputDevice::configureBody(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition); int puckCount = (int)_validTrackedObjects.size(); glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat); @@ -637,8 +644,17 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(controller::RIGHT_FOOT); _poseStateMap[controller::HIPS] = addOffsetToPuckPose(controller::HIPS); _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); - _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); - _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); + controller::Pose rightArmPose = addOffsetToPuckPose(controller::RIGHT_ARM); + _poseStateMap[controller::RIGHT_ARM] = rightArmPose; + if (rightArmPose.isValid()) { + // AJT: TODO: compute rotation for RIGHT_SHOULDER AS WELL. + } + + controller::Pose leftArmPose = addOffsetToPuckPose(controller::LEFT_ARM); + _poseStateMap[controller::LEFT_ARM] = leftArmPose; + if (leftArmPose.isValid()) { + // AJT: TODO: compute rotation for LEFT_SHOULDER AS WELL. + } if (_overrideHead) { _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); @@ -899,6 +915,16 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); + + // AJT: TODO ONLY DO THIS IF CALIBRATING! + // record controller history + if (isLeftHand) { + _leftControllerHistory[_leftControllerHistoryCursor] = pose.translation; + _leftControllerHistoryCursor = (_leftControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; + } else { + _rightControllerHistory[_rightControllerHistoryCursor] = pose.translation; + _rightControllerHistoryCursor = (_rightControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; + } } bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { @@ -950,7 +976,7 @@ void ViveControllerManager::InputDevice::hapticsHelper(float deltaTime, bool lef } } -void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { +void ViveControllerManager::InputDevice::calibrateLeftHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { controller::Pose& handPose = handPair.second; glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation()); glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat); @@ -981,7 +1007,7 @@ void ViveControllerManager::InputDevice::calibrateLeftHand(glm::mat4& defaultToR _pucksPostOffset[handPair.first] = offsetMat; } -void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { +void ViveControllerManager::InputDevice::calibrateRightHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair) { controller::Pose& handPose = handPair.second; glm::mat4 handPoseAvatarMat = createMatFromQuatAndPos(handPose.getRotation(), handPose.getTranslation()); glm::vec3 handPoseTranslation = extractTranslation(handPoseAvatarMat); @@ -1013,7 +1039,7 @@ void ViveControllerManager::InputDevice::calibrateRightHand(glm::mat4& defaultTo } -void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateFeet(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { glm::vec3 headXAxis = getReferenceHeadXAxis(defaultToReferenceMat, inputCalibration.defaultHeadMat); glm::vec3 headPosition = getReferenceHeadPosition(defaultToReferenceMat, inputCalibration.defaultHeadMat); auto& firstFoot = _validTrackedObjects[FIRST_FOOT]; @@ -1030,7 +1056,7 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer } } -void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot){ +void ViveControllerManager::InputDevice::calibrateFoot(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot){ controller::Pose footPose = footPair.second; glm::mat4 puckPoseAvatarMat = createMatFromQuatAndPos(footPose.getRotation(), footPose.getTranslation()); glm::mat4 defaultFoot = isLeftFoot ? inputCalibration.defaultLeftFoot : inputCalibration.defaultRightFoot; @@ -1052,17 +1078,75 @@ void ViveControllerManager::InputDevice::calibrateFoot(glm::mat4& defaultToRefer } } -void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateHips(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _pucksPostOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); } -void ViveControllerManager::InputDevice::calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateChest(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first; _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } -void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, +// assuming the user has kept his arms straight to his sides and has rotated his hand controllers +// up and down laterally about his shoulders. This will attempt to locate the users actual shoulders +// by calculating a best fit circle around the points, in the plane of the head's forward normal. +static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat, const std::vector& history) { + glm::mat4 invHeadMat = glm::inverse(headMat); + + // AJT: TODO: we could perhaps reject the approximation if the radius is larger or smaller then we expect. + // AJT: TODO: abort if history is too small or invalid... + + // Take equation for a circle: (x - h)^2 + (y - k)^2 = r^2 + // And put it into the linear form: A * x = b + // + // where A = | (2 * x0) (2 * y0) 1 | + // | (2 * x1) (2 * y1) 1 | + // | . . . | + // | (2 * xn) (2 * yn) 1 | + // + // and b = | x0^2 + y0^2 | + // | x1^2 + y1^2 | + // | . | + // | xn^2 + yn^2 | + // + // and x = | h | + // | k | + // | r^2 - h^2 - k^2 | + // + + // Build aMat and bMat + Eigen::MatrixXf aMat(CONTROLLER_HISTORY_SIZE, 3); + Eigen::MatrixXf bMat(CONTROLLER_HISTORY_SIZE, 1); + for (int r = 0; r < CONTROLLER_HISTORY_SIZE; r++) { + // transform history[r] into local head coordinates + glm::vec3 p = transformPoint(invHeadMat, history[r]); + + // update the aMat with the appropriate 2d history values + aMat(r, 0) = 2.0f * p.x; + aMat(r, 1) = 2.0f * p.y; + aMat(r, 2) = 1.0f; + + // update the bMat with the appropriate 2d history values + bMat(r, 0) = p.x * p.x + p.y * p.y; + } + + // Then use least squares approximation to solve for the best x. + // http://math.mit.edu/~gs/linearalgebra/ila0403.pdf + Eigen::MatrixXf aTransMat = aMat.transpose(); + Eigen::MatrixXf xMat = (aTransMat * aMat).inverse() * aTransMat * bMat; + + // extract the 2d center of the circle from the xMat + glm::vec3 center(xMat(0, 0), xMat(1, 0), 0.0f); + + // we don't need the radius, but it's included here for completeness. + // float radius = center.x * center.x + center.y * center.y - xMat(2, 0); + + // transform center back into sensor coordinates + return transformPoint(headMat, center); +} + +void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex) { const PuckPosePair& firstShoulder = _validTrackedObjects[firstShoulderIndex]; const PuckPosePair& secondShoulder = _validTrackedObjects[secondShoulderIndex]; @@ -1071,18 +1155,18 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo glm::mat4 refLeftArm = defaultToReferenceMat * inputCalibration.defaultLeftArm; glm::mat4 refRightArm = defaultToReferenceMat * inputCalibration.defaultRightArm; + glm::mat4 userRefLeftArm = refLeftArm; glm::mat4 userRefRightArm = refRightArm; - const float PUCK_TO_USER_REF_ARM_Y_OFFSET = -0.06f; + + glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; - // move the userRefArm to the same height as the puck. - userRefLeftArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - userRefRightArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); @@ -1095,10 +1179,6 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; - // move the userRefArm to the same height as the puck - userRefLeftArm[3][1] = secondShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - userRefRightArm[3][1] = firstShoulderPose.translation.y + PUCK_TO_USER_REF_ARM_Y_OFFSET; - // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); @@ -1110,7 +1190,7 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo } } -void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { +void ViveControllerManager::InputDevice::calibrateHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { size_t headIndex = _validTrackedObjects.size() - 1; const PuckPosePair& head = _validTrackedObjects[headIndex]; _jointToPuckMap[controller::HEAD] = head.first; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index d82b895579..329901cd25 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -93,18 +93,18 @@ private: void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton); void printDeviceTrackingResultChange(uint32_t deviceIndex); void setConfigFromString(const QString& value); - bool configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - bool configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - bool configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateLeftHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); - void calibrateRightHand(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); - void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateFoot(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot); - void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, + bool configureHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + bool configureHands(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + bool configureBody(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateLeftHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); + void calibrateRightHand(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& handPair); + void calibrateFeet(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateFoot(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, PuckPosePair& footPair, bool isLeftFoot); + void calibrateHips(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateChest(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex); - void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateHead(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData); void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData); void emitCalibrationStatus(); @@ -190,6 +190,11 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; + std::vector _leftControllerHistory; + size_t _leftControllerHistoryCursor { 0 }; + std::vector _rightControllerHistory; + size_t _rightControllerHistoryCursor { 0 }; + QString configToString(Config config); friend class ViveControllerManager; }; From 766c6f48c3b9acf016aae10b8e342eb8b1d39e61 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 17:43:01 -0700 Subject: [PATCH 125/280] third person camera is alt+G rather than alt+O --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c6679f3bf6..72a3eb6bfc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -228,7 +228,7 @@ Menu::Menu() { // View > Third Person cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_O, + viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_G, false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror From 280a7a7e42e5dabf8c6b284bcce4595c660ccd5d Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Tue, 25 Jul 2017 01:48:55 +0100 Subject: [PATCH 126/280] Fixing Styling, added Preference to Tablet, Moved Location to Avatar Tuning --- interface/resources/qml/controls-uit/RadioButton.qml | 4 ++-- .../qml/dialogs/preferences/PrimaryHandPreference.qml | 1 - interface/src/ui/PreferencesDialog.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls-uit/RadioButton.qml b/interface/resources/qml/controls-uit/RadioButton.qml index 3cc3db4e46..ab11ec68b1 100644 --- a/interface/resources/qml/controls-uit/RadioButton.qml +++ b/interface/resources/qml/controls-uit/RadioButton.qml @@ -54,9 +54,9 @@ Original.RadioButton { height: checkSize radius: 7 anchors.centerIn: parent - color: hifi.colors.checkBoxChecked + color: "#00B4EF" border.width: 1 - border.color: hifi.colors.checkBoxCheckedBorder + border.color: "#36CDFF" visible: checked && !pressed || !checked && pressed } } diff --git a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml index 07b9c958dd..cfc2e94ed9 100644 --- a/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml +++ b/interface/resources/qml/dialogs/preferences/PrimaryHandPreference.qml @@ -28,7 +28,6 @@ Preference { } function save() { - // Box1 = True, Box2 = False (Right Hand for Default) if (box1.checked && !box2.checked) { preference.value = "left"; } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index fd0847c945..bf99daab67 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -67,11 +67,6 @@ void setupPreferences() { auto setter = [=](bool value) { myAvatar->setClearOverlayWhenMoving(value); }; preferences->addPreference(new CheckPreference(AVATAR_BASICS, "Clear overlays when moving", getter, setter)); } - { - auto getter = [=]()->QString { return myAvatar->getDominantHand(); }; - auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); }; - preferences->addPreference(new PrimaryHandPreference(AVATAR_BASICS, "Dominant Hand", getter, setter)); - } // UI static const QString UI_CATEGORY { "UI" }; @@ -186,6 +181,11 @@ void setupPreferences() { preference->setStep(1); preferences->addPreference(preference); } + { + auto getter = [=]()->QString { return myAvatar->getDominantHand(); }; + auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); }; + preferences->addPreference(new PrimaryHandPreference(AVATAR_TUNING, "Dominant Hand", getter, setter)); + } { auto getter = [=]()->float { return myAvatar->getUniformScale(); }; auto setter = [=](float value) { myAvatar->setTargetScale(value); }; From e900dae70879852c2db0bf9133b8e1fd52d52ede Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 18:34:58 -0700 Subject: [PATCH 127/280] Created script for spherical gravity. --- unpublishedScripts/gravity.js | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 unpublishedScripts/gravity.js diff --git a/unpublishedScripts/gravity.js b/unpublishedScripts/gravity.js new file mode 100644 index 0000000000..f468bfc717 --- /dev/null +++ b/unpublishedScripts/gravity.js @@ -0,0 +1,55 @@ +// +// gravity.js +// scripts/system/ +// +// Created by Alan-Michael Moody on 7/24/17 +// 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 +// + +(function () { + + var _entityID; + + this.preload = function (entityID) { + _entityID = entityID; + }; + + function update(deltatime) { + var planet = Entities.getEntityProperties(_entityID); + var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, planet.position)); + var localUp = Quat.getUp(MyAvatar.orientation); + + MyAvatar.orientation = Quat.multiply(Quat.rotationBetween(localUp, direction), MyAvatar.orientation); + } + + function init() { + Script.update.connect(update); + } + + function clean() { + Script.update.disconnect(update); + MyAvatar.orientation = Quat.fromVec3Degrees({ + x: 0, + y: 0, + z: 0 + }); + } + + var _switch = true; + + this.clickDownOnEntity = function(uuid, mouseEvent) { + + if (_switch) { + init(); + } else { + clean(); + } + _switch = !_switch; + }; + + Script.scriptEnding.connect(clean); + +}); \ No newline at end of file From 62efb32671652a9f6629c2fda1f80d396f8dc2c2 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 18:39:09 -0700 Subject: [PATCH 128/280] Created script for spherical gravity. --- unpublishedScripts/gravity.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/gravity.js b/unpublishedScripts/gravity.js index f468bfc717..a7ad5c889b 100644 --- a/unpublishedScripts/gravity.js +++ b/unpublishedScripts/gravity.js @@ -1,6 +1,5 @@ // // gravity.js -// scripts/system/ // // Created by Alan-Michael Moody on 7/24/17 // Copyright 2016 High Fidelity, Inc. From 1bf2855e9b5a4960333e5c777816a0dfadc8bd58 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 18:47:24 -0700 Subject: [PATCH 129/280] Created script for spherical gravity. --- unpublishedScripts/gravity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpublishedScripts/gravity.js b/unpublishedScripts/gravity.js index a7ad5c889b..ea7ec83d67 100644 --- a/unpublishedScripts/gravity.js +++ b/unpublishedScripts/gravity.js @@ -2,7 +2,7 @@ // gravity.js // // Created by Alan-Michael Moody on 7/24/17 -// Copyright 2016 High Fidelity, Inc. +// Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html From 2f857a6d1a8b58332eabc4e4329e30753abd17a3 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Tue, 25 Jul 2017 06:05:06 +0100 Subject: [PATCH 130/280] Forgot a file --- .../resources/qml/controls/RadioButton.qml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 interface/resources/qml/controls/RadioButton.qml diff --git a/interface/resources/qml/controls/RadioButton.qml b/interface/resources/qml/controls/RadioButton.qml new file mode 100644 index 0000000000..6bd9c860d2 --- /dev/null +++ b/interface/resources/qml/controls/RadioButton.qml @@ -0,0 +1,17 @@ +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 +import "../styles" +import "." +Original.RadioButton { + text: "Radio Button" + style: RadioButtonStyle { + indicator: FontAwesome { + text: control.checked ? "\uf046" : "\uf096" + } + label: Text { + text: control.text + renderType: Text.QtRendering + } + } + +} From dc53017dd4fb68dc63fe7b6c4854057facd63cb6 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 23:17:13 -0700 Subject: [PATCH 131/280] Created script to let Snails eat brussel sprouts. --- .../DomainContent/Cupcake/eatable.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 unpublishedScripts/DomainContent/Cupcake/eatable.js diff --git a/unpublishedScripts/DomainContent/Cupcake/eatable.js b/unpublishedScripts/DomainContent/Cupcake/eatable.js new file mode 100644 index 0000000000..8b261b7ea2 --- /dev/null +++ b/unpublishedScripts/DomainContent/Cupcake/eatable.js @@ -0,0 +1,36 @@ +// +// eatable.js +// +// Created by Alan-Michael Moody on 7/24/2017 +// Copyright 2017 High Fidelity, Inc. +// +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { + var NOMNOM_SOUND = SoundCache.getSound('http://hifi-content.s3.amazonaws.com/DomainContent/production/audio/vegcrunch.wav'); + + var _entityID; + + this.preload = function (entityID) { + _entityID = entityID; + }; + + this.collisionWithEntity = function(entityUuid, collisionEntityID, collisionInfo) { + var entity = Entities.getEntityProperties(collisionEntityID, ['userData', 'name']); + var isClone = (entity.name.substring(1).split('-')[0] === 'clone'); + var isEatable = (JSON.parse(entity.userData).eatable); + + if (isEatable && isClone) { + Audio.playSound(NOMNOM_SOUND, { + position: Entities.getEntityProperties(_entityID, ['position']).position, + volume: 0.2, + localOnly: false + }); + + Entities.deleteEntity(collisionEntityID); + } + }; +}); \ No newline at end of file From 34594b914da0a3679878c47fc885769b910edeef Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 23:55:44 -0700 Subject: [PATCH 132/280] Added and removed different normalizations. --- unpublishedScripts/gravity.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/gravity.js b/unpublishedScripts/gravity.js index ea7ec83d67..0ef7b0caa9 100644 --- a/unpublishedScripts/gravity.js +++ b/unpublishedScripts/gravity.js @@ -18,10 +18,12 @@ function update(deltatime) { var planet = Entities.getEntityProperties(_entityID); - var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, planet.position)); + + //normalization happens in rotationBetween. + var direction = Vec3.subtract(MyAvatar.position, planet.position); var localUp = Quat.getUp(MyAvatar.orientation); - MyAvatar.orientation = Quat.multiply(Quat.rotationBetween(localUp, direction), MyAvatar.orientation); + MyAvatar.orientation = Quat.normalize(Quat.multiply(Quat.rotationBetween(localUp, direction), MyAvatar.orientation)); } function init() { From f9204a7034d06a25391a74caf79cfb4ab272ab62 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Tue, 25 Jul 2017 09:03:47 +0100 Subject: [PATCH 133/280] Now it should appear.. --- .../qml/hifi/tablet/tabletWindows/preferences/Section.qml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml index af1fbd0070..8cf254809d 100644 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml +++ b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/Section.qml @@ -82,6 +82,7 @@ Preference { property var buttonBuilder: Component { ButtonPreference { } } property var comboBoxBuilder: Component { ComboBoxPreference { } } property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } } + property var primaryHandBuilder: Component { PrimaryHandPreference { } } property var preferences: [] property int checkBoxCount: 0 @@ -144,10 +145,16 @@ Preference { //to be not overlapped when drop down is active zpos = root.z + 1000 - itemNum break; + case Preference.SpinnerSlider: checkBoxCount = 0; builder = spinnerSliderBuilder; break; + + case Preference.PrimaryHand: + checkBoxCount++; + builder = primaryHandBuilder; + break; }; if (builder) { From 6817cf9db4572fb22dc79efd2a267826ee118d97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 25 Jul 2017 10:59:03 -0700 Subject: [PATCH 134/280] Fix entity properties sometimes being resent When writing entity properties to a packet, we start off with a list of requested properties (`requestedProperties`) and keep track of which properties didn't fit (`propertiesDidntFit`) the packet, which is intialized to requestedProperties. As we pack the properties, we remove them from propertiesDidntFit if they didn't need to be written or were able to be packed. At the end we store propertiesDidntFit, and use it in the future as our requestedProperties when we try to pack more data into a packet. The bug: because of the order in which propertiesDidntFit is initialized, it ended up always being the list of all properties for that entity. This typically wasn't an issue because we usually go through and try to append all of the properties, and if we don't need to append them (because they aren't in requestedProperties) we remove them from our propertiesDidntFit list. When we don't have enough remaining space in the current packet for even the entity header, which is fairly small, we don't bother trying to append any of the properties. When this happens, propertiesDidntFit contains the full list of properties, which we save for the next pass through the entity, and use as our requestedProperties, causing us to resend entity data again. In the worst case we never end up sending all of the entity's data. --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..378f344d26 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -182,7 +182,6 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); - EntityPropertyFlags propertiesDidntFit = requestedProperties; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. @@ -190,6 +189,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); } + EntityPropertyFlags propertiesDidntFit = requestedProperties; + LevelDetails entityLevel = packetData->startLevel(); quint64 lastEdited = getLastEdited(); From 256b7efdd2f9bdef50e1dad58550e64645f050b8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:06:54 -0700 Subject: [PATCH 135/280] Handle the case when the camera is inside the bounding box of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index a9efc8e6f6..cc4f645c0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -14,6 +14,9 @@ #include +static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable @@ -38,8 +41,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); - props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); + glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } @@ -68,7 +72,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -84,7 +88,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setDimensions(dimensions); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); @@ -105,15 +109,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { + if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box, position the context overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; @@ -122,6 +122,14 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); From c71dca5b4b44e2c6764094e727c0b362ba42155a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:15:44 -0700 Subject: [PATCH 136/280] Missed a magic number --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cc4f645c0a..95bdd7186c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -53,6 +53,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims @@ -111,7 +112,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec2 contextOverlayDimensions; if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 2d0c5ff37a8624bc75f7b94a5409ff841c8b1df7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 11:41:34 -0700 Subject: [PATCH 137/280] remove preloading of allowed user keys since it happens on connect --- domain-server/src/DomainGatekeeper.cpp | 15 ++------------- domain-server/src/DomainGatekeeper.h | 2 -- domain-server/src/DomainServer.cpp | 4 +--- libraries/networking/src/NodeList.cpp | 2 +- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index da31fed387..957830bc7b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -604,19 +604,6 @@ bool DomainGatekeeper::isWithinMaxCapacity() { return true; } - -void DomainGatekeeper::preloadAllowedUserPublicKeys() { - QStringList allowedUsers = _server->_settingsManager.getAllNames(); - - if (allowedUsers.size() > 0) { - // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not - // going to create > 100 requests - foreach(const QString& username, allowedUsers) { - requestUserPublicKey(username); - } - } -} - void DomainGatekeeper::requestUserPublicKey(const QString& username) { // don't request public keys for the standard psuedo-account-names if (NodePermissions::standardNames.contains(username, Qt::CaseInsensitive)) { @@ -667,6 +654,8 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; + qDebug() << "Extracted public key for" << username.toLower(); + _userPublicKeys[username.toLower()] = QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); } diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 163f255411..03a6292c55 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -39,8 +39,6 @@ public: const QUuid& walletUUID, const QString& nodeVersion); QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID); - void preloadAllowedUserPublicKeys(); - void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); } static void sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c5171620de..163bd48f1b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -160,9 +160,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : getTemporaryName(); } - _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request - - //send signal to DomainMetadata when descriptors changed + // send signal to DomainMetadata when descriptors changed _metadata = new DomainMetadata(this); connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated, _metadata, &DomainMetadata::descriptorsChanged); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 75c97cc205..48e5c8a62c 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -327,7 +327,7 @@ void NodeList::sendDomainServerCheckIn() { << "but no keypair is present. Waiting for keypair generation to complete."; accountManager->generateNewUserKeypair(); - // don't send the check in packet - wait for the keypair first + // don't send the check in packet - wait for the new public key to be available to the domain-server first return; } From 5bc38bd7f07869f3392a8babbda49b75a63cc773 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Fri, 7 Jul 2017 11:15:42 -0400 Subject: [PATCH 138/280] [WL21389] Collision Shapes need to be updated (details below). Revised approach involves creating a helper function within ShapeFactory to aid in devising the ShapeType to be used by an ShapeEntityItem for collision. The ShapeFactory is currently doing this for creating the actual Bullet Library collision shapes. ShapeEntityItem overrides its virtually inherited computeShapeInfo which in turn calls the new ShapeFactory helper function. ShapeEntityItem has a new memvar _collisionShapeType to cache its actual ShapeType used by the physics system. This memvar is returned via the getShapeType accessor which is expected to return an object's ShapeType. Note(s): This is similar to the original approach save translation between entity::Shape and ShapeType isn't tied to the EntityItemProperties shapeTypeNames or shapeType. This approach more directly solves the issue of getting the actual ShapeType used by the time it's needed to determine the bullet collision object type created when initializing the physic information. Translation of the ShapeEntityItem's entity::Shape to its ShapeType is handled by ShapeFactory which handles creating the bullet collision objects when setting up physics on the ShapeEntityItems. Known Issue(s): This doesn't compile. It appears that the Entity Library needs to know about the Physics Library. The naive attempt at providing that link failed to resolve all compilation issues. Current Error: C1083: Cannot open include file: btBulletDynamicsCommon.h: No such file or directory (C:\projects\cusack\libraries\entities\src\ShapeEntityItem.cpp) C:\projects\cusack\libraries\physics\src\ShapeFactory.h 15 1 entities modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/physics/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js new file: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 10 +- libraries/entities/CMakeLists.txt | 3 +- libraries/entities/src/ShapeEntityItem.cpp | 54 ++++- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 203 ++++++++++++++++-- libraries/physics/src/ShapeFactory.h | 3 + libraries/shared/src/ShapeInfo.cpp | 5 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- .../tests/basicEntityTest/shapeSpawner.js | 33 +++ 9 files changed, 286 insertions(+), 33 deletions(-) create mode 100644 scripts/developer/tests/basicEntityTest/shapeSpawner.js diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 62ab3377a8..b197e0b9e6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -48,18 +48,22 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Cube); + result->setShape(entity::Shape::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Sphere); + result->setShape(entity::Shape::Sphere); return result; } diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 19341ec3e2..90de43b5f8 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars) +link_hifi_libraries(shared networking octree avatars physics) + diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 018d8c568a..d9c48f8593 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -58,7 +59,11 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -101,6 +106,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } +//TODO_CUSACK: Move back to header prior to PN +void ShapeEntityItem::setShape( const QString &shape ) { + setShape(entity::shapeFromString(shape)); +} + bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -160,10 +170,46 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } +void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} + + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + { + //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + return SHAPE_TYPE_ELLIPSOID; + } + + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { @@ -223,10 +269,12 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " shape:" << stringFromShape(_shape); + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 96f69deb0c..f021fcf957 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } + void setShape(const QString& shape); float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -84,6 +84,7 @@ public: QColor getQColor() const; void setColor(const QColor& value); + void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } @@ -100,6 +101,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index d209667966..a9bc8b2e05 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -17,7 +17,7 @@ #include "BulletUtil.h" // These are the same normalized directions used by the btShapeHull class. -// 12 points for the face centers of a duodecohedron plus another 30 points +// 12 points for the face centers of a dodecahedron plus another 30 points // for the midpoints the edges, for a total of 42. const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42; static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = { @@ -247,6 +247,124 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +{ + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + outCollisionShape = new btSphereShape(radius); + } + + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + outCollisionShape = createConvexHull(points); + } + + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShape(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x > halfExtents.z) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeX(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeZ(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } + + //Got here, then you are what you are along with outCollisionShape + return type; +} + +ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { + if ( shape == entity::Shape::NUM_SHAPES ) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } + + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } + + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } + + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { + + //TODO_CUSACK: Implement the hedrons + return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); + } + + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { + + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } + +} + const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -255,30 +373,33 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SHAPE_TYPE_SPHERE: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - shape = new btSphereShape(radius); - } - break; + //case SHAPE_TYPE_SPHERE: { + // glm::vec3 halfExtents = info.getHalfExtents(); + // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + // shape = new btSphereShape(radius); + //} + //break; + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - shape = new btSphereShape(radius); - } else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - shape = createConvexHull(points); - } + //float radius = halfExtents.x; + //const float MIN_RADIUS = 0.001f; + //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + //if (radius > MIN_RADIUS + // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // // close enough to true sphere + // shape = new btSphereShape(radius); + //} else { + // ShapeInfo::PointList points; + // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + // } + // shape = createConvexHull(points); + //} + + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); } break; case SHAPE_TYPE_CAPSULE_Y: { @@ -288,6 +409,42 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); + } + break; case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..52b448ee1d 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,11 +15,14 @@ #include #include +#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { + + ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 496e94f8bd..e40d91379c 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -136,7 +136,10 @@ float ShapeInfo::computeVolume() const { } case SHAPE_TYPE_CAPSULE_Y: { float radius = _halfExtents.x; - volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f); + // Need to offset halfExtents.y by x to account for the system treating + // the y extent of the capsule as the cylindrical height + spherical radius. + float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x); + volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f); break; } default: diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index 538e9145f5..fa5c9291cb 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,7 +8,9 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - type: "Sphere", + name: "Cusack_Testing", + type: "Shape", + shapeType: "Cylinder", color: { red: 200, green: 10, diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js new file mode 100644 index 0000000000..f93072a582 --- /dev/null +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -0,0 +1,33 @@ + var orientation = Camera.getOrientation(); + orientation = Quat.safeEulerAngles(orientation); + orientation.x = 0; + orientation = Quat.fromVec3Degrees(orientation); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); + + // Math.random ensures no caching of script + var SCRIPT_URL = Script.resolvePath("myEntityScript.js") + + var myEntity = Entities.addEntity({ + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + color: { + red: 200, + green: 10, + blue: 200 + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + script: SCRIPT_URL + }) + + + function cleanup() { + // Entities.deleteEntity(myEntity); + } + + Script.scriptEnding.connect(cleanup); From 75403124b621835b1bb2c4b9a060980324ab7e1b Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 18 Jul 2017 17:15:02 -0400 Subject: [PATCH 139/280] [WL21389] Addresses physics library dependency and has some other fixes (details below). * Addresses physics library dependency by moving computeShapeInfo override from ShapeEntityItem (which is within Entities Library) to RenderableShapeEntityItem (which is in Entities-Renderer Library). ** Entities-Renderer library already links against the physic library. ** Per discussion with Andrew Meadows: In order to ShapeEntityItem to be utilized the library dependency between the Entity and Physics library would need to be resolved to avoid the cyclical reliance which isn't in the scope of this ticket. * Updates shapeSpawner test script from the default clone of basicEntityTest\entitySpawner.js ** Objects now have a finite lifetime ** Script now cleans up the objects created when the script ends ** Also moved some adjustable properties out into var aliases at the top of the file for easier/less error prone tweaking. Should probably add one for the shapeType. * Fixes some issues with validateShapeType helper function * Removed naive attempt at including physics library within entities library. * Transferred some todos from notes * Fixed some formatting NOTE(s): This compiles and runs. Cylinder is spawned and treated as CYLINDER_Y. TODO(s): * Add tweakable var for shapeType within shapeSpawner.js * Vet and verify other shapes. * Add in edge case handling. * Add in support for other shapes to ShapeInfo infrastructure. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 25 +- .../src/RenderableShapeEntityItem.h | 2 + libraries/entities/CMakeLists.txt | 2 +- libraries/entities/src/ShapeEntityItem.cpp | 61 ++-- libraries/entities/src/ShapeEntityItem.h | 1 - libraries/physics/src/ShapeFactory.cpp | 277 +++++++++--------- libraries/shared/src/ShapeInfo.cpp | 14 +- .../tests/basicEntityTest/shapeSpawner.js | 53 ++-- 8 files changed, 226 insertions(+), 209 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b197e0b9e6..f071518b42 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -48,11 +49,12 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -86,6 +88,23 @@ bool RenderableShapeEntityItem::isTransparent() { } } +void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 0cc6a54f81..96f450f523 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,6 +28,8 @@ public: bool isTransparent() override; + void computeShapeInfo(ShapeInfo& info); + private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 90de43b5f8..84906a48df 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars physics) +link_hifi_libraries(shared networking octree avatars) diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index d9c48f8593..854be7df1e 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,7 +12,6 @@ #include #include -#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -59,11 +58,12 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -96,9 +96,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; + _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -170,46 +172,29 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - - if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); - - //--EARLY EXIT--( allow default handling to process ) - return; - } - - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); - } - - return EntityItem::computeShapeInfo(info); -} - // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - { - //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - return SHAPE_TYPE_ELLIPSOID; - } + //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + //{ + // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + // return SHAPE_TYPE_ELLIPSOID; + //} - return _collisionShapeType; + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index f021fcf957..435d22a6b2 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index a9bc8b2e05..74aff45e55 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,121 +247,120 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) { - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - outCollisionShape = new btSphereShape(radius); - } + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + (*outCollisionShape) = new btSphereShape(radius); + } - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - outCollisionShape = createConvexHull(points); - } + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + (*outCollisionShape) = createConvexHull(points); + } - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShape(btHalfExtents); - } + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + // TODO_CUSACK: Should allow for minor variance along axes? + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShape(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x > halfExtents.z) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeX(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x >= halfExtents.z) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeZ(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } - //Got here, then you are what you are along with outCollisionShape - return type; + //Got here, then you are what you are along with outCollisionShape + return type; } ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if ( shape == entity::Shape::NUM_SHAPES ) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } + if (shape == entity::Shape::NUM_SHAPES) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } + //TODO_CUSACK: Implement the hedrons + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { - //TODO_CUSACK: Implement the hedrons - return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } } @@ -379,7 +378,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = new btSphereShape(radius); //} //break; - case SHAPE_TYPE_SPHERE: + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); //float radius = halfExtents.x; @@ -399,9 +398,11 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = createConvexHull(points); //} - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); } break; + //TODO_CUSACK: Add Capsules to vetting/validation process for + // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -409,42 +410,44 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; - case SHAPE_TYPE_CAPSULE_X: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.y; - float height = 2.0f * halfExtents.x; - shape = new btCapsuleShapeX(radius, height); - } - break; - case SHAPE_TYPE_CAPSULE_Z: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - float height = 2.0f * halfExtents.z; - shape = new btCapsuleShapeZ(radius, height); - } - break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. - const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); - } - break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + } + break; + //TODO_CUSACK: Add compound and simple hull to vetting/validation + // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index e40d91379c..57e927eaf3 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { @@ -55,6 +56,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -62,6 +65,8 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -70,12 +75,17 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { + //TODO_CUSACK: Should this have protection against inadvertant clearance and type + // resetting? If for some reason this was called and point list was and is emtpy + // would we still wish to clear out everything? _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -117,6 +127,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { + //TODO_CUSACK: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -150,6 +161,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { + //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js index f93072a582..9fcaf3a3db 100644 --- a/scripts/developer/tests/basicEntityTest/shapeSpawner.js +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -1,33 +1,30 @@ - var orientation = Camera.getOrientation(); - orientation = Quat.safeEulerAngles(orientation); - orientation.x = 0; - orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); +// compute a position to create the object relative to avatar +var forwardOffset = Vec3.multiply(2.0, Quat.getFront(MyAvatar.orientation)); +var objectPosition = Vec3.sum(MyAvatar.position, forwardOffset); - // Math.random ensures no caching of script - var SCRIPT_URL = Script.resolvePath("myEntityScript.js") +var LIFETIME = 1800; //seconds +var DIM_HEIGHT = 1, DIM_WIDTH = 1, DIM_DEPTH = 1; +var COLOR_R = 100, COLOR_G = 10, COLOR_B = 200; - var myEntity = Entities.addEntity({ - name: "ShapeSpawnTest", - type: "Shape", - shape: "Cylinder", - color: { - red: 200, - green: 10, - blue: 200 - }, - position: center, - dimensions: { - x: 1, - y: 1, - z: 1 - }, - script: SCRIPT_URL - }) +var properties = { + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + dimensions: {x: DIM_WIDTH, y: DIM_HEIGHT, z: DIM_DEPTH}, + color: {red: COLOR_R, green: COLOR_G, blue: COLOR_B}, + position: objectPosition, + lifetime: LIFETIME, +}; + +// create the object +var entityId = Entities.addEntity(properties); + +function cleanup() { + Entities.deleteEntity(entityId); +} + +// delete the object when this script is stopped +Script.scriptEnding.connect(cleanup); - function cleanup() { - // Entities.deleteEntity(myEntity); - } - Script.scriptEnding.connect(cleanup); From 9e25c3b7d8d6446b7e678accf06d1da945868de4 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Tue, 25 Jul 2017 14:47:34 -0400 Subject: [PATCH 140/280] Renaming 'not' route to 'logicalNot'. Build was failing on non-windows platforms because 'not' is a reserved word in C++ (which Visual Studio happily ignores). --- interface/resources/controllers/vive.json | 4 ++-- libraries/controllers/src/controllers/impl/Filter.cpp | 2 +- .../controllers/src/controllers/impl/RouteBuilderProxy.cpp | 2 +- .../controllers/src/controllers/impl/RouteBuilderProxy.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index cc8e2a437a..73ab5cb2ae 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -24,7 +24,7 @@ { "from": "Vive.LS", "to": "Standard.LS" }, { "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp", "peek": true, - "filters": [ { "type": "not" } ] + "filters": [ { "type": "logicalNot" } ] }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, @@ -42,7 +42,7 @@ { "from": "Vive.RS", "to": "Standard.RS" }, { "from": "Vive.RSTouch", "to": "Standard.RightThumbUp", "peek": true, - "filters": [ { "type": "not" } ] + "filters": [ { "type": "logicalNot" } ] }, { "from": "Vive.RSTouch", "to": "Standard.RSTouch" }, diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 08cba9c296..b6c3ed4d27 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -41,7 +41,7 @@ REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPos REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") -REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "not") +REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "logicalNot") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index c6abc2d402..bc1ef55725 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -149,7 +149,7 @@ QObject* RouteBuilderProxy::pulse(float interval) { return this; } -QObject* RouteBuilderProxy::not() { +QObject* RouteBuilderProxy::logicalNot() { addFilter(std::make_shared()); return this; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index d70bdb649f..75f3747566 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -53,7 +53,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* postTransform(glm::mat4 transform); Q_INVOKABLE QObject* rotate(glm::quat rotation); Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant); - Q_INVOKABLE QObject* not(); + Q_INVOKABLE QObject* logicalNot(); private: void to(const Endpoint::Pointer& destination); From d155c0264030c3eec0c231d7f80072b6ab63662b Mon Sep 17 00:00:00 2001 From: Leander Hasty <1p-cusack@1stplayable.com> Date: Mon, 24 Jul 2017 17:02:40 -0400 Subject: [PATCH 141/280] [WL21389] wip and modifications based on comments https://github.com/highfidelity/hifi/pull/11024#pullrequestreview-51611518 Cleans up tabs, moves new functionality out of ShapeFactory directly to RenderableShapeEntityItem's computeShapeInfo override, begins to break down where we will need pointlists. Still need to determine how rotation is handled for pointlists, and check for axis alignment on cylinders before deciding on a shape. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js --- .../src/RenderableShapeEntityItem.cpp | 92 ++++++-- .../src/RenderableShapeEntityItem.h | 2 +- libraries/entities/CMakeLists.txt | 1 - libraries/entities/src/ShapeEntityItem.cpp | 37 +--- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 202 ++++-------------- libraries/physics/src/ShapeFactory.h | 3 - libraries/shared/src/ShapeInfo.cpp | 4 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- 9 files changed, 122 insertions(+), 227 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index f071518b42..a305b201d6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -49,23 +49,18 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Cube); + result->setShape(entity::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Sphere); + result->setShape(entity::Sphere); return result; } @@ -90,19 +85,84 @@ bool RenderableShapeEntityItem::isTransparent() { void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); + // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) + // is set. - //--EARLY EXIT--( allow default handling to process ) - return; + const glm::vec3 entityDimensions = getDimensions(); + + switch (_shape){ + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; + } + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO_CUSACK: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); +#if 0 + // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y + // TODO_CUSACK: Should allow for minor variance along axes? + if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { + } + else if (entityDimensions.x >= entityDimensions.z) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_X; + } + else if (entityDimensions.z >= entityDimensions.x) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a hull + { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } +#endif + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_NONE; + //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + } + break; + default:{ + //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. + } + break; } - return EntityItem::computeShapeInfo(info); + EntityItem::computeShapeInfo(info); } void RenderableShapeEntityItem::render(RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 96f450f523..2a841df57e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info); private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 84906a48df..19341ec3e2 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,3 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) link_hifi_libraries(shared networking octree avatars) - diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 854be7df1e..58799de807 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -58,12 +58,7 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -93,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { + switch (_shape) { // TODO_CUSACK fill out? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? break; default: _type = EntityTypes::Shape; @@ -108,11 +103,6 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } -//TODO_CUSACK: Move back to header prior to PN -void ShapeEntityItem::setShape( const QString &shape ) { - setShape(entity::shapeFromString(shape)); -} - bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -175,25 +165,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} - - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - - //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - //{ - // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - // return SHAPE_TYPE_ELLIPSOID; - //} - return _collisionShapeType; } @@ -254,7 +225,7 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 435d22a6b2..42a92f7bd7 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape); + void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -100,7 +100,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; + ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 74aff45e55..87580c6726 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,123 +247,6 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) -{ - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - (*outCollisionShape) = new btSphereShape(radius); - } - - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - (*outCollisionShape) = createConvexHull(points); - } - - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - // TODO_CUSACK: Should allow for minor variance along axes? - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShape(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x >= halfExtents.z) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } - - //Got here, then you are what you are along with outCollisionShape - return type; -} - -ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if (shape == entity::Shape::NUM_SHAPES) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } - - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } - - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } - - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { - - //TODO_CUSACK: Implement the hedrons - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } - -} - const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -372,37 +255,32 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - //case SHAPE_TYPE_SPHERE: { - // glm::vec3 halfExtents = info.getHalfExtents(); - // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - // shape = new btSphereShape(radius); - //} - //break; - case SHAPE_TYPE_SPHERE: - case SHAPE_TYPE_ELLIPSOID: { + case SHAPE_TYPE_SPHERE: { glm::vec3 halfExtents = info.getHalfExtents(); - //float radius = halfExtents.x; - //const float MIN_RADIUS = 0.001f; - //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - //if (radius > MIN_RADIUS - // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // // close enough to true sphere - // shape = new btSphereShape(radius); - //} else { - // ShapeInfo::PointList points; - // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - // } - // shape = createConvexHull(points); - //} - - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); + float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + shape = new btSphereShape(radius); + } + break; + case SHAPE_TYPE_ELLIPSOID: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + shape = new btSphereShape(radius); + } else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + shape = createConvexHull(points); + } } break; - //TODO_CUSACK: Add Capsules to vetting/validation process for - // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -424,30 +302,22 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShapeZ(radius, height); } break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. + case SHAPE_TYPE_CYLINDER_X: { const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeX(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Z: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeZ(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Y: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShape(btHalfExtents); } break; - //TODO_CUSACK: Add compound and simple hull to vetting/validation - // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 52b448ee1d..2bf79f390c 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,14 +15,11 @@ #include #include -#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { - - ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 57e927eaf3..5d6b4ece7a 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,8 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index fa5c9291cb..538e9145f5 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,9 +8,7 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - name: "Cusack_Testing", - type: "Shape", - shapeType: "Cylinder", + type: "Sphere", color: { red: 200, green: 10, From ef1e426273b3412e63ee511495f8013375250245 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 25 Jul 2017 14:09:46 -0400 Subject: [PATCH 142/280] [WL21389] Some code and todo cleanup in prep for PR1. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 29 +++++-------------- libraries/entities/src/ShapeEntityItem.cpp | 7 ++--- libraries/physics/src/ShapeFactory.cpp | 2 ++ libraries/shared/src/ShapeInfo.cpp | 20 ++++++------- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a305b201d6..5fa2354bd5 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -114,26 +114,13 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { break; case entity::Shape::Cylinder: { _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO_CUSACK: determine if rotation is axis-aligned + // TODO WL21389: determine if rotation is axis-aligned //const Transform::Quat & rot = _transform.getRotation(); -#if 0 - // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y - // TODO_CUSACK: Should allow for minor variance along axes? - if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { - } - else if (entityDimensions.x >= entityDimensions.z) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_X; - } - else if (entityDimensions.z >= entityDimensions.x) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a hull - { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection - } -#endif + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + } break; case entity::Shape::Triangle: @@ -145,15 +132,15 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Dodecahedron: case entity::Shape::Icosahedron: case entity::Shape::Cone: { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; } break; case entity::Shape::Torus: { // Not in GeometryCache::buildShapes, unsupported. _collisionShapeType = SHAPE_TYPE_NONE; - //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) } break; default:{ diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 58799de807..157e3afab3 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO_CUSACK fill out? + switch (_shape) { // TODO WL21389: fill out with other shapes? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,8 +162,7 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -// For now, all polys will act as spheres +// This value specifes how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 87580c6726..97174b2216 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -307,11 +307,13 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeX(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Z: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeZ(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Y: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5d6b4ece7a..5930880859 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,7 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? + //TODO WL21389: Does this need additional cases and handling added? _url = ""; _type = type; setHalfExtents(halfExtents); @@ -56,8 +56,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { - //TODO_CUSACK: Should this pointlist clearance added in case + //TODO WL21389: Should this pointlist clearance added in case // this is a re-purposed instance? + // See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491 _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -65,8 +66,7 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -75,17 +75,14 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { - //TODO_CUSACK: Should this have protection against inadvertant clearance and type - // resetting? If for some reason this was called and point list was and is emtpy - // would we still wish to clear out everything? + //TODO WL21389: May need to skip resetting type here. _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -127,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO_CUSACK: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -161,7 +158,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { - //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. + //TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; @@ -206,6 +203,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const { } const DoubleHashKey& ShapeInfo::getHash() const { + //TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; From b1aa6e19625f6e17c0f2c28cca494832cef2d96e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 13:42:29 -0700 Subject: [PATCH 143/280] Move hosting for quazip back to hifi S3 --- cmake/externals/quazip/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index d44c9ffde3..01650a432d 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -21,7 +21,7 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL https://s3.amazonaws.com/Oculus/quazip-0.7.3.zip + URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip URL_MD5 ed03754d39b9da1775771819b8001d45 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} From 6b5cabf00af08e4278828da0ac3fceec45a6f717 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Tue, 25 Jul 2017 18:12:07 -0400 Subject: [PATCH 144/280] [WL21389] Minor: Mark RenderableShapeEntityItem::computeShapeInfo as an override. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h --- libraries/entities-renderer/src/RenderableShapeEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 2a841df57e..f93e4b991e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - virtual void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info) override; private: std::unique_ptr _procedural { nullptr }; From e9845784d5a5a6012b1b10c8729f5e5e6798ae94 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 13:43:30 -0700 Subject: [PATCH 145/280] don't return error decrypting for optimistic public keys --- domain-server/src/DomainGatekeeper.cpp | 34 +++++++++++++++++--------- domain-server/src/DomainGatekeeper.h | 15 +++++++++--- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 957830bc7b..fc595be67e 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -385,7 +385,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // user is attempting to prove their identity to us, but we don't have enough information sendConnectionTokenPacket(username, nodeConnection.senderSockAddr); // ask for their public key right now to make sure we have it - requestUserPublicKey(username); + requestUserPublicKey(username, true); getGroupMemberships(username); // optimistically get started on group memberships #ifdef WANT_DEBUG qDebug() << "stalling login because we have no username-signature:" << username; @@ -521,7 +521,10 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, const HifiSockAddr& senderSockAddr) { // it's possible this user can be allowed to connect, but we need to check their username signature auto lowerUsername = username.toLower(); - QByteArray publicKeyArray = _userPublicKeys.value(lowerUsername); + KeyFlagPair publicKeyPair = _userPublicKeys.value(lowerUsername); + + QByteArray publicKeyArray = publicKeyPair.first; + bool isOptimisticKey = publicKeyPair.second; const QUuid& connectionToken = _connectionTokenHash.value(lowerUsername); @@ -555,10 +558,16 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username, return true; } else { - if (!senderSockAddr.isNull()) { - qDebug() << "Error decrypting username signature for " << username << "- denying connection."; + // we only send back a LoginError if this wasn't an "optimistic" key + // (a key that we hoped would work but is probably stale) + + if (!senderSockAddr.isNull() && !isOptimisticKey) { + qDebug() << "Error decrypting username signature for" << username << "- denying connection."; sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr, DomainHandler::ConnectionRefusedReason::LoginError); + } else if (!senderSockAddr.isNull()) { + qDebug() << "Error decrypting username signature for" << username << "with optimisitic key -" + << "re-requesting public key and delaying connection"; } // free up the public key, we don't need it anymore @@ -604,7 +613,7 @@ bool DomainGatekeeper::isWithinMaxCapacity() { return true; } -void DomainGatekeeper::requestUserPublicKey(const QString& username) { +void DomainGatekeeper::requestUserPublicKey(const QString& username, bool isOptimistic) { // don't request public keys for the standard psuedo-account-names if (NodePermissions::standardNames.contains(username, Qt::CaseInsensitive)) { return; @@ -615,7 +624,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { // public-key request for this username is already flight, not rerequesting return; } - _inFlightPublicKeyRequests += lowerUsername; + _inFlightPublicKeyRequests.insert(lowerUsername, isOptimistic); // even if we have a public key for them right now, request a new one in case it has just changed JSONCallbackParameters callbackParams; @@ -627,7 +636,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) { const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key"; - qDebug() << "Requesting public key for user" << username; + qDebug().nospace() << "Requesting " << (isOptimistic ? "optimistic " : " ") << "public key for user " << username; DependencyManager::get()->sendRequest(USER_PUBLIC_KEY_PATH.arg(username), AccountManagerAuth::None, @@ -649,18 +658,21 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QString username = extractUsernameFromPublicKeyRequest(requestReply); + bool isOptimisticKey = _inFlightPublicKeyRequests.take(username); + if (jsonObject["status"].toString() == "success" && !username.isEmpty()) { // pull the public key as a QByteArray from this response const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; - qDebug() << "Extracted public key for" << username.toLower(); + qDebug().nospace() << "Extracted " << (isOptimisticKey ? "optimistic " : " ") << "public key for " << username.toLower(); _userPublicKeys[username.toLower()] = - QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); + { + QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()), + isOptimisticKey + }; } - - _inFlightPublicKeyRequests.remove(username); } void DomainGatekeeper::publicKeyJSONErrorCallback(QNetworkReply& requestReply) { diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 03a6292c55..36df77815a 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -91,7 +91,7 @@ private: void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); - void requestUserPublicKey(const QString& username); + void requestUserPublicKey(const QString& username, bool isOptimistic = false); DomainServer* _server; @@ -100,8 +100,17 @@ private: QHash _icePeers; QHash _connectionTokenHash; - QHash _userPublicKeys; - QSet _inFlightPublicKeyRequests; // keep track of which we've already asked for + + // the word "optimistic" below is used for keys that we request during user connection before the user has + // had a change to upload a new public key + + // we don't send back user signature decryption errors for those keys so that there isn't a thrasing of key re-generation + // and connection refusal + + using KeyFlagPair = QPair; + + QHash _userPublicKeys; // keep track of keys and flag them as optimistic or not + QHash _inFlightPublicKeyRequests; // keep track of keys we've asked for (and if it was optimistic) QSet _domainOwnerFriends; // keep track of friends of the domain owner QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for From dd3755596c48cf66b6f6ea5b80537959c0d56fac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 15:43:34 -0700 Subject: [PATCH 146/280] fix typo in comment in DomainGatekeeper header --- domain-server/src/DomainGatekeeper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 36df77815a..09db075e07 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -102,7 +102,7 @@ private: QHash _connectionTokenHash; // the word "optimistic" below is used for keys that we request during user connection before the user has - // had a change to upload a new public key + // had a chance to upload a new public key // we don't send back user signature decryption errors for those keys so that there isn't a thrasing of key re-generation // and connection refusal From c81875a2808ada23429daa191aef5789842097ed Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Jul 2017 15:24:09 -0700 Subject: [PATCH 147/280] Improved shoulder calibration using hard-coded measured shoulder width --- .../animation/src/AnimInverseKinematics.cpp | 25 ++++++++++++ libraries/animation/src/AnimUtil.cpp | 11 +++++ libraries/animation/src/AnimUtil.h | 2 + plugins/openvr/src/ViveControllerManager.cpp | 40 ++++++++++++++----- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index b9eb1c2912..10c4011f52 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -1862,15 +1862,40 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC // overwrites _relativePoses with secondary poses. void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { + + if (_secondaryTargetsInRigFrame.empty()) { + return; + } + + // shoulder joint should look-at position of arm joint. + const int leftArmIndex = _skeleton->nameToJointIndex("LeftArm"); + const int rightArmIndex = _skeleton->nameToJointIndex("RightArm"); + AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { AnimPose absPose = rigToGeometryPose * iter.second; absPose.scale() = glm::vec3(1.0f); + AnimPose parentAbsPose; int parentIndex = _skeleton->getParentIndex(iter.first); if (parentIndex >= 0) { parentAbsPose = _skeleton->getAbsolutePose(parentIndex, _relativePoses); } + + // if parent should "look-at" child joint position. + if (iter.first == leftArmIndex || iter.first == rightArmIndex) { + + AnimPose grandParentAbsPose; + int grandParentIndex = _skeleton->getParentIndex(parentIndex); + if (parentIndex >= 0) { + grandParentAbsPose = _skeleton->getAbsolutePose(grandParentIndex, _relativePoses); + } + + // the shoulder should rotate toward the arm joint via "look-at" constraint + parentAbsPose = boneLookAt(absPose.trans(), parentAbsPose); + _relativePoses[parentIndex] = grandParentAbsPose.inverse() * parentAbsPose; + } + // AJT: for now ignore translation on secondary poses. glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index bcf30642e8..65c605b5ba 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -96,3 +96,14 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu return frame; } +// rotate bone's y-axis with target. +AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone) { + glm::vec3 u, v, w; + generateBasisVectors(target - bone.trans(), bone.rot() * Vectors::UNIT_X, u, v, w); + glm::mat4 lookAt(glm::vec4(v, 0.0f), + glm::vec4(u, 0.0f), + // AJT: TODO REVISIT THIS, this could be -w. + glm::vec4(glm::normalize(glm::cross(v, u)), 0.0f), + glm::vec4(bone.trans(), 1.0f)); + return AnimPose(lookAt); +} diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index d215fdc654..f2cceb361b 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -31,4 +31,6 @@ inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) { return glm::normalize(glm::lerp(a, bTemp, alpha)); } +AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone); + #endif diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 02d3afd713..4c34a4cd27 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -644,17 +644,8 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::RIGHT_FOOT] = addOffsetToPuckPose(controller::RIGHT_FOOT); _poseStateMap[controller::HIPS] = addOffsetToPuckPose(controller::HIPS); _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); - controller::Pose rightArmPose = addOffsetToPuckPose(controller::RIGHT_ARM); - _poseStateMap[controller::RIGHT_ARM] = rightArmPose; - if (rightArmPose.isValid()) { - // AJT: TODO: compute rotation for RIGHT_SHOULDER AS WELL. - } - - controller::Pose leftArmPose = addOffsetToPuckPose(controller::LEFT_ARM); - _poseStateMap[controller::LEFT_ARM] = leftArmPose; - if (leftArmPose.isValid()) { - // AJT: TODO: compute rotation for LEFT_SHOULDER AS WELL. - } + _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); + _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); if (_overrideHead) { _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); @@ -1146,6 +1137,25 @@ static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat return transformPoint(headMat, center); } +// y axis comes out of puck usb port/green light +// -z axis comes out of puck center/vive logo +static glm::vec3 computeUserShoulderPositionFromMeasurements(const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { + // AJT: TODO measurments are hard coded! + const float ARM_CIRC = 0.33f; // 13 inches + const float SHOULDER_SPAN = 0.4826; // 19 inches + + float armRadius = ARM_CIRC / TWO_PI; + + float sign = isLeftHand ? 1.0f : -1.0f; + float localArmX = sign * SHOULDER_SPAN / 2.0f; + + controller::Pose localPuck = armPuck.transform(glm::inverse(headMat)); + glm::mat4 localPuckMat = localPuck.getMatrix(); + glm::vec3 localArmCenter = extractTranslation(localPuckMat) + armRadius * transformVectorFast(localPuckMat, Vectors::UNIT_Z); + + return transformPoint(headMat, glm::vec3(localArmX, localArmCenter.y, localArmCenter.z)); +} + void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, int firstShoulderIndex, int secondShoulderIndex) { const PuckPosePair& firstShoulder = _validTrackedObjects[firstShoulderIndex]; @@ -1160,13 +1170,18 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def glm::mat4 userRefRightArm = refRightArm; glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; + /* AJT: TODO REMOVE userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); + */ if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, true), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, false), 1.0f); + // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, secondShoulderPose); @@ -1179,6 +1194,9 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; + userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, true), 1.0f); + userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, false), 1.0f); + // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefRightArm, firstShoulderPose); From 7702bfceaa49cc555d39b1800ba7f9adea593a27 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 16:12:32 -0700 Subject: [PATCH 148/280] Big cleanup and preparation; Actually fix desktop tablet --- .../ui/overlays/ContextOverlayInterface.cpp | 138 +++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 3 + interface/src/ui/overlays/Planar3DOverlay.h | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.h | 2 +- 5 files changed, 92 insertions(+), 55 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 95bdd7186c..18cb41fda5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,9 +39,9 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); - QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + QVariantMap props; props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); @@ -68,32 +68,57 @@ static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - glm::vec3 dimensions = entityProperties.getDimensions(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); - } - if (entityProperties.getMarketplaceID().length() != 0) { + if (contextOverlayFilterPassed(entityItemID)) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + + // Add all necessary variables to the stack + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 entityDimensions = entityProperties.getDimensions(); + glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; + + // Draw the outline overlay + // This also gives us the centerpoint of the entity even if + // the the entity doesn't use the default registration point. + glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + + // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); + // Here, we determine the position and dimensions of the Context Overlay. + if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box... + // ...position the Context Overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { + // Else if the entity is too close to the camera... + // ...rotate the Context Overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + // Else, place the Context Overlay some offset away from the entity's bounding + // box in the direction of the camera. + auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(dimensions); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); + // Finally, setup and draw the Context Overlay if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); @@ -106,36 +131,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(bbPosition, cameraPosition); - glm::vec3 contextOverlayPosition; - glm::vec2 contextOverlayDimensions; - if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { - // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { - // If the entity is too close to the camera, rotate the context overlay to the right of the entity. - // This makes it easy to inspect things you're holding. - float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; - if (event.getID() == LEFT_HAND_HW_ID) { - offsetAngle *= -1; - } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } } else { @@ -144,17 +144,51 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return false; } +bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + return (entityProperties.getMarketplaceID().length() != 0); +} + +glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 bbPosition = entityProperties.getPosition(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + // Setup and draw the bounding box around the entity + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setParentID(entityItemID); + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(bbPosition); + _bbOverlay->setVisible(true); + // This returned position should always be the center of the entity + // even if the registration point isn't the default. + return bbPosition; +} + bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); + // Destroy the Context Overlay + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + // Destroy the outline overlay + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _bbOverlay = NULL; + _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 812914a82e..623eb85401 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -70,6 +70,9 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 2ccf2c4513..8127d4ebb9 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,7 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; - glm::vec2 getSize() const { return _dimensions; }; + virtual glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..1dd3389352 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -613,7 +613,7 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { } } -glm::vec2 Web3DOverlay::getSize() { +glm::vec2 Web3DOverlay::getSize() const { return _resolution / _dpi * INCHES_TO_METERS * getDimensions(); }; diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 1e3706ed25..3d2f9dd514 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -50,7 +50,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - glm::vec2 getSize(); + glm::vec2 getSize() const override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; From e5ddded464ed54a4f942807e090fa5408eaf6129 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 16:16:15 -0700 Subject: [PATCH 149/280] Moved Express/Custom install window just after EULA window --- cmake/templates/NSIS.template.in | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 42642c2d05..5417220ef1 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -49,7 +49,7 @@ Var STR_CONTAINS_VAR_3 Var STR_CONTAINS_VAR_4 Var STR_RETURN_VAR - + Function StrContains Exch $STR_NEEDLE Exch 1 @@ -343,23 +343,29 @@ SectionEnd ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME - + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" + + Page custom InstallTypesPage ReadInstallTypes + + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction !insertmacro MUI_PAGE_DIRECTORY - + ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" - !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER - - @CPACK_NSIS_PAGE_COMPONENTS@ - Page custom InstallTypesPage ReadInstallTypes + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction + @CPACK_NSIS_PAGE_COMPONENTS@ + Page custom PostInstallOptionsPage ReadPostInstallOptions !insertmacro MUI_PAGE_INSTFILES - + !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES @@ -510,6 +516,14 @@ Function ChangeCustomLabel Pop $R1 FunctionEnd +Function AbortFunction + ; Check if Express is set, if so, abort the post install options page + Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults + StrCmp $Express "1" 0 end + Abort + end: +FunctionEnd + Function PostInstallOptionsPage !insertmacro MUI_HEADER_TEXT "Setup Options" "" @@ -628,11 +642,11 @@ Var ExpressInstallState Var CustomInstallState Function ReadInstallTypes -${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for express/custom install ${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState ${NSD_GetState} $CustomInstallRadioButton $CustomInstallState -${EndIf} + ${EndIf} FunctionEnd Function ReadPostInstallOptions @@ -712,6 +726,7 @@ Function HandlePostInstallOptions ${Else} !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} From 572213daaf6ac3c4a90c87f51d95ab82a63614cf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 25 Jul 2017 17:05:07 -0700 Subject: [PATCH 150/280] Added shoulder config to Controller Settings UI All offset values in Controller Settings UI are now in cm. --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 88 +++++++++++++++---- plugins/openvr/src/ViveControllerManager.cpp | 38 +++++--- plugins/openvr/src/ViveControllerManager.h | 2 + 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 90d6ba7022..36424e22b4 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -145,12 +145,13 @@ Rectangle { visible: headPuckBox.checked HifiControls.SpinBox { id: headYOffset - decimals: 4 + decimals: 1 width: 112 - label: "Y: offset" + label: "Y Offset" + suffix: " cm" minimumValue: -10 - stepSize: 0.0254 - value: -0.05 + stepSize: 1 + value: -5 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -162,11 +163,12 @@ Rectangle { HifiControls.SpinBox { id: headZOffset width: 112 - label: "Z: offset" + label: "Z Offset" minimumValue: -10 - stepSize: 0.0254 - decimals: 4 - value: -0.05 + stepSize: 1 + decimals: 1 + suffix: " cm" + value: -5 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -175,7 +177,6 @@ Rectangle { } } - RalewayBold { id: hands @@ -254,11 +255,12 @@ Rectangle { HifiControls.SpinBox { id: handYOffset - decimals: 4 + decimals: 1 width: 112 - label: "Y: offset" + suffix: " cm" + label: "Y Offset" minimumValue: -10 - stepSize: 0.0254 + stepSize: 1 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -270,10 +272,11 @@ Rectangle { HifiControls.SpinBox { id: handZOffset width: 112 - label: "Z: offset" + label: "Z Offset" + suffix: " cm" minimumValue: -10 - stepSize: 0.0254 - decimals: 4 + stepSize: 1 + decimals: 1 colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -488,15 +491,55 @@ Rectangle { } } + Row { + id: shoulderAdditionalConfig + visible: shoulderBox.checked + anchors.top: shoulderConfig.bottom + anchors.topMargin: 5 + anchors.left: openVrConfiguration.left + anchors.leftMargin: leftMargin + 20 + spacing: 10 + + HifiControls.SpinBox { + id: armCircumference + decimals: 1 + width: 160 + suffix: " cm" + label: "Arm Circumference" + minimumValue: 0 + stepSize: 1.0 + colorScheme: hifi.colorSchemes.dark + value: 33.0 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + + HifiControls.SpinBox { + id: shoulderWidth + width: 160 + label: "Shoulder Width" + suffix: " cm" + minimumValue: 0 + stepSize: 1.0 + decimals: 1 + colorScheme: hifi.colorSchemes.dark + value: 48 + + onEditingFinished: { + sendConfigurationSettings(); + } + } + } + Separator { id: bottomSeperator width: parent.width - anchors.top: shoulderConfig.bottom - anchors.topMargin: 10 + anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom + anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10) } - - Rectangle { id: calibrationButton property int color: hifi.buttons.blue @@ -1006,10 +1049,17 @@ Rectangle { "Z": handZOffset.value } + var shoulderObject = { + "override": shouldersChecked, + "armCircumference": armCircumference.value, + "shoulderWidth": shoulderWidth.value + } + var settingsObject = { "bodyConfiguration": trackerConfiguration, "headConfiguration": headObject, "handConfiguration": handObject, + "shoulderConfiguration": shoulderObject, "desktopMode": viveInDesktop.checked } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 4c34a4cd27..395e131124 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -378,6 +378,8 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input } } +static const float CM_TO_M = 0.01f; + void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); if (!configurationSettings.empty()) { @@ -391,8 +393,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; - _headPuckYOffset = headObject["Y"].toDouble(); - _headPuckZOffset = headObject["Z"].toDouble(); + _headPuckYOffset = headObject["Y"].toDouble() * CM_TO_M; + _headPuckZOffset = headObject["Z"].toDouble() * CM_TO_M; } else { _headConfig = HeadConfig::HMD; } @@ -401,11 +403,18 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; - _handPuckYOffset = handsObject["Y"].toDouble(); - _handPuckZOffset = handsObject["Z"].toDouble(); + _handPuckYOffset = handsObject["Y"].toDouble() * CM_TO_M; + _handPuckZOffset = handsObject["Z"].toDouble() * CM_TO_M; } else { _handConfig = HandConfig::HandController; } + } else if (iter.key() == "shoulderConfiguration") { + QJsonObject shoulderObj = iter.value().toObject(); + bool shouldersChecked = shoulderObj["override"].toBool(); + if (shouldersChecked) { + _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; + _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; + } } iter++; } @@ -1139,15 +1148,12 @@ static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat // y axis comes out of puck usb port/green light // -z axis comes out of puck center/vive logo -static glm::vec3 computeUserShoulderPositionFromMeasurements(const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { - // AJT: TODO measurments are hard coded! - const float ARM_CIRC = 0.33f; // 13 inches - const float SHOULDER_SPAN = 0.4826; // 19 inches +static glm::vec3 computeUserShoulderPositionFromMeasurements(float armCirc, float shoulderSpan, const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { - float armRadius = ARM_CIRC / TWO_PI; + float armRadius = armCirc / TWO_PI; float sign = isLeftHand ? 1.0f : -1.0f; - float localArmX = sign * SHOULDER_SPAN / 2.0f; + float localArmX = sign * shoulderSpan / 2.0f; controller::Pose localPuck = armPuck.transform(glm::inverse(headMat)); glm::mat4 localPuckMat = localPuck.getMatrix(); @@ -1179,8 +1185,10 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = secondShoulder.first; - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, true), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, false), 1.0f); + glm::vec3 leftPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, firstShoulderPose, true); + userRefLeftArm[3] = glm::vec4(leftPos, 1.0f); + glm::vec3 rightPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, secondShoulderPose, false); + userRefRightArm[3] = glm::vec4(rightPos, 1.0f); // compute the post offset from the userRefArm _pucksPostOffset[firstShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, firstShoulderPose); @@ -1194,8 +1202,10 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def _jointToPuckMap[controller::LEFT_ARM] = secondShoulder.first; _jointToPuckMap[controller::RIGHT_ARM] = firstShoulder.first; - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, secondShoulderPose, true), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromMeasurements(headMat, firstShoulderPose, false), 1.0f); + glm::vec3 leftPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, secondShoulderPose, true); + userRefLeftArm[3] = glm::vec4(leftPos, 1.0f); + glm::vec3 rightPos = computeUserShoulderPositionFromMeasurements(_armCircumference, _shoulderWidth, headMat, firstShoulderPose, false); + userRefRightArm[3] = glm::vec4(rightPos, 1.0f); // compute the post offset from the userRefArm _pucksPostOffset[secondShoulder.first] = computeOffset(Matrices::IDENTITY, userRefLeftArm, secondShoulderPose); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 329901cd25..41ed494ffb 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -182,6 +182,8 @@ private: float _headPuckZOffset { -0.05f }; float _handPuckYOffset { 0.0f }; float _handPuckZOffset { 0.0f }; + float _armCircumference { 0.33f }; + float _shoulderWidth { 0.48f }; bool _triggersPressedHandled { false }; bool _calibrated { false }; bool _timeTilCalibrationSet { false }; From 65192aa620c13d1fe65c4dc032a42d6f5e9da148 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 15:17:15 -0700 Subject: [PATCH 151/280] added additional collision types to asset browser and new model dialog --- interface/resources/qml/AssetServer.qml | 17 +++++++++++++++++ .../qml/hifi/tablet/NewModelDialog.qml | 5 ++++- scripts/system/edit.js | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index eb47afc951..11cab8b8de 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -151,12 +151,20 @@ ScrollingWindow { var SHAPE_TYPE_SIMPLE_HULL = 1; var SHAPE_TYPE_SIMPLE_COMPOUND = 2; var SHAPE_TYPE_STATIC_MESH = 3; + var SHAPE_TYPE_BOX = 4; + var SHAPE_TYPE_SPHERE = 5; + var SHAPE_TYPE_COMPOUND = 6; + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; + SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; + SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; @@ -196,6 +204,15 @@ ScrollingWindow { case SHAPE_TYPE_STATIC_MESH: shapeType = "static-mesh"; break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + case SHAPE_TYPE_COMPOUND: + shapeType = "compound"; + break; default: shapeType = "none"; } diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index d47c981440..f0c9c3c14a 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -145,7 +145,10 @@ Rectangle { model: ["No Collision", "Basic - Whole model", "Good - Sub-meshes", - "Exact - All polygons"] + "Exact - All polygons", + "Box", + "Sphere", + "Compound"] } Row { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6bb0675bc8..fbd1732b0b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -337,6 +337,9 @@ var toolBar = (function () { var SHAPE_TYPE_SIMPLE_HULL = 1; var SHAPE_TYPE_SIMPLE_COMPOUND = 2; var SHAPE_TYPE_STATIC_MESH = 3; + var SHAPE_TYPE_BOX = 4; + var SHAPE_TYPE_SPHERE = 5; + var SHAPE_TYPE_COMPOUND = 6; var DYNAMIC_DEFAULT = false; function handleNewModelDialogResult(result) { @@ -353,6 +356,15 @@ var toolBar = (function () { case SHAPE_TYPE_STATIC_MESH: shapeType = "static-mesh"; break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + case SHAPE_TYPE_COMPOUND: + shapeType = "compound"; + break; default: shapeType = "none"; } @@ -450,6 +462,9 @@ var toolBar = (function () { SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; + SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; + SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; // tablet version of new-model dialog From 24f4468fd5be0811ceea4d95436110467c9fc6f5 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 17:09:46 -0700 Subject: [PATCH 152/280] added additional collision types to asset browser in tablet mode --- .../qml/hifi/dialogs/TabletAssetServer.qml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 0256805422..15f72cfbcb 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -152,12 +152,19 @@ Rectangle { var SHAPE_TYPE_SIMPLE_HULL = 1; var SHAPE_TYPE_SIMPLE_COMPOUND = 2; var SHAPE_TYPE_STATIC_MESH = 3; + var SHAPE_TYPE_BOX = 4; + var SHAPE_TYPE_SPHERE = 5; + var SHAPE_TYPE_COMPOUND = 6; + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes"; SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; + SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; + SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; + SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; @@ -197,6 +204,15 @@ Rectangle { case SHAPE_TYPE_STATIC_MESH: shapeType = "static-mesh"; break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + case SHAPE_TYPE_COMPOUND: + shapeType = "compound"; + break; default: shapeType = "none"; } From 29e778323d2584b04e6c68e3912da07a35b9812d Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 17:24:06 -0700 Subject: [PATCH 153/280] Update AssetServer.qml --- interface/resources/qml/AssetServer.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 11cab8b8de..dc2c7bc8f6 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -154,8 +154,7 @@ ScrollingWindow { var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; var SHAPE_TYPE_COMPOUND = 6; - - + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; From 88838da6e584dd2375909173dd3da250c494e932 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Tue, 25 Jul 2017 17:46:08 -0700 Subject: [PATCH 154/280] cleaning up for mac and linux --- libraries/midi/src/Midi.cpp | 112 +++++++++++++++++++++++++++++++----- libraries/midi/src/Midi.h | 11 ++-- 2 files changed, 103 insertions(+), 20 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 5b5376c6d3..ca6c67ad81 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -98,19 +98,6 @@ void Midi::sendNote(int status, int note, int vel) { } } -void Midi::noteReceived(int status, int note, int velocity) { - if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && - ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) { - return; // NOTE: only sending note-on and note-off to Javascript - } - - QVariantMap eventData; - eventData["status"] = status; - eventData["note"] = note; - eventData["velocity"] = velocity; - emit midiNote(eventData); -} - void Midi::MidiSetup() { midihin.clear(); @@ -187,14 +174,32 @@ void Midi::MidiCleanup() { } #endif +void Midi::noteReceived(int status, int note, int velocity) { + if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && + ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) { + return; // NOTE: only sending note-on and note-off to Javascript + } + + QVariantMap eventData; + eventData["status"] = status; + eventData["note"] = note; + eventData["velocity"] = velocity; +// emit midiNote(eventData); +} + // Midi::Midi() { instance = this; +#if defined Q_OS_WIN32 midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing +#endif MidiSetup(); } +Midi::~Midi() { +} + void Midi::playMidiNote(int status, int note, int velocity) { sendNote(status, note, velocity); } @@ -237,14 +242,14 @@ QStringList Midi::listMidiDevices(bool output) { void Midi::unblockMidiDevice(QString name, bool output) { if (output) { - for (int i = 0; i < midioutexclude.size(); i++) { + for (unsigned long i = 0; i < midioutexclude.size(); i++) { if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { midioutexclude.erase(midioutexclude.begin() + i); break; } } } else { - for (int i = 0; i < midiinexclude.size(); i++) { + for (unsigned long i = 0; i < midiinexclude.size(); i++) { if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { midiinexclude.erase(midiinexclude.begin() + i); break; @@ -266,3 +271,80 @@ void Midi::thruModeEnable(bool enable) { thruModeEnabled = enable; } +/* +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch x86_64 -isysroot +/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -L/Users/burt/hifi/assignment-client/Debug -F/Users/burt/hifi/assignment-client/Debug -F/Users/burt/Qt5.6.2/5.6/clang_64/lib -filelist +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client.LinkFileList -Xlinker -rpath -Xlinker +/Users/burt/Qt5.6.2/5.6/clang_64/lib -mmacosx-version-min=10.8 -Xlinker -object_path_lto -Xlinker +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_lto.o -Xlinker -no_deduplicate -stdlib=libc++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names +/Users/burt/hifi/libraries/audio/Debug/libaudio.a +/Users/burt/hifi/libraries/avatars/Debug/libavatars.a +/Users/burt/hifi/libraries/octree/Debug/liboctree.a +/Users/burt/hifi/libraries/gpu/Debug/libgpu.a +/Users/burt/hifi/libraries/model/Debug/libmodel.a +/Users/burt/hifi/libraries/fbx/Debug/libfbx.a +/Users/burt/hifi/libraries/entities/Debug/libentities.a +/Users/burt/hifi/libraries/networking/Debug/libnetworking.a +/Users/burt/hifi/libraries/animation/Debug/libanimation.a +/Users/burt/hifi/libraries/recording/Debug/librecording.a +/Users/burt/hifi/libraries/shared/Debug/libshared.a +/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a +/Users/burt/hifi/libraries/embedded-webserver/Debug/libembedded-webserver.a +/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a +/Users/burt/hifi/libraries/physics/Debug/libphysics.a +/Users/burt/hifi/libraries/plugins/Debug/libplugins.a +/Users/burt/hifi/libraries/ui/Debug/libui.a +/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a +/Users/burt/hifi/libraries/ui/Debug/libui.a +/Users/burt/hifi/libraries/recording/Debug/librecording.a +/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a +/Users/burt/hifi/libraries/physics/Debug/libphysics.a +/Users/burt/hifi/libraries/entities/Debug/libentities.a +/Users/burt/hifi/libraries/audio/Debug/libaudio.a +/Users/burt/hifi/libraries/plugins/Debug/libplugins.a +/Users/burt/hifi/libraries/avatars/Debug/libavatars.a +/Users/burt/hifi/libraries/octree/Debug/liboctree.a +/Users/burt/hifi/libraries/animation/Debug/libanimation.a +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletDynamics.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletCollision.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libLinearMath.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletSoftBody.dylib +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScriptTools.framework/QtScriptTools +/Users/burt/hifi/ext/Xcode/quazip/project/lib/libquazip5d.1.0.0.dylib +/Users/burt/hifi/libraries/procedural/Debug/libprocedural.a +/Users/burt/hifi/libraries/model-networking/Debug/libmodel-networking.a +/Users/burt/hifi/libraries/fbx/Debug/libfbx.a +/Users/burt/hifi/libraries/model/Debug/libmodel.a +/Users/burt/hifi/libraries/image/Debug/libimage.a +/Users/burt/hifi/ext/Xcode/nvtt/project/lib/libnvtt.dylib +/Users/burt/hifi/libraries/gpu-gl/Debug/libgpu-gl.a +/Users/burt/hifi/libraries/gpu/Debug/libgpu.a +/Users/burt/hifi/libraries/ktx/Debug/libktx.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtConcurrent.framework/QtConcurrent -lpthread +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebSockets.framework/QtWebSockets +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtXmlPatterns.framework/QtXmlPatterns +/Users/burt/hifi/libraries/gl/Debug/libgl.a +/Users/burt/hifi/libraries/networking/Debug/libnetworking.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngine.framework/QtWebEngine +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngineCore.framework/QtWebEngineCore +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQuick.framework/QtQuick +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebChannel.framework/QtWebChannel +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtPositioning.framework/QtPositioning +/usr/local/Cellar/openssl/1.0.2k/lib/libssl.dylib +/usr/local/Cellar/openssl/1.0.2k/lib/libcrypto.dylib +/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbb_debug.dylib +/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbbmalloc_debug.dylib -framework IOKit -framework CoreFoundation +/Users/burt/hifi/libraries/shared/Debug/libshared.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScript.framework/QtScript +/usr/local/lib/libz.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQml.framework/QtQml +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtOpenGL.framework/QtOpenGL +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWidgets.framework/QtWidgets +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtGui.framework/QtGui -framework OpenGL +/Users/burt/hifi/ext/Xcode/glew/project/lib/libglew_d.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtNetwork.framework/QtNetwork +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtCore.framework/QtCore -Xlinker -dependency_info -Xlinker +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_dependency_info.dat -o +/Users/burt/hifi/assignment-client/Debug/assignment-client +*/ + diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index e8ba803ad4..54ffdb58e8 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -22,9 +22,6 @@ class Midi : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - -public: - Midi(); public: void noteReceived(int status, int note, int velocity); // relay a note to Javascript @@ -39,8 +36,8 @@ private: void MidiSetup(); void MidiCleanup(); -signals: - void midiNote(QVariantMap eventData); +//signals: +// void midiNote(QVariantMap eventData); public slots: /// play a note on all connected devices @@ -66,6 +63,10 @@ Q_INVOKABLE void unblockMidiDevice(QString name, bool output); /// repeat all incoming notes to all outputs (default disabled) Q_INVOKABLE void thruModeEnable(bool enable); + +public: + Midi(); + virtual ~Midi(); }; #endif // hifi_Midi_h From 9020607ccfa75fd5ded41d1a08079c299966b20f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 17:54:46 -0700 Subject: [PATCH 155/280] First pass --- interface/src/Application.cpp | 6 + .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 12 + libraries/render/src/render/Args.h | 6 + scripts/system/marketplaces/marketplaces.js | 206 +++++++++--------- 5 files changed, 133 insertions(+), 103 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 699ddfcb2d..978f20d743 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5393,6 +5393,12 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } renderArgs->_debugFlags = renderDebugFlags; //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); + + RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = static_cast(renderOutlineFlags | + static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..7c768dbfa0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -37,7 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) - Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) + Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -53,6 +54,8 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setEnabled(bool enabled) { _enabled = enabled; } bool getEnabled() { return _enabled; } + bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } + void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -70,6 +73,7 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9884debcce..52308499d7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,6 +372,18 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } + bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); + if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); + gpu::Batch& batch = *args->_batch; + bool success; + auto shapeTransform = getTransformToCenter(success); + if (success) { + batch.setModelTransform(shapeTransform); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(args, batch, yellowColor); + } + } + if (!hasModel() || (_model && _model->didVisualGeometryRequestFail())) { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 449a3ac22b..f53454cf75 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -63,6 +63,11 @@ namespace render { public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; + enum OutlineFlags { + RENDER_OUTLINE_NONE = 0, + RENDER_OUTLINE_WIREFRAMES = 1, + RENDER_OUTLINE_SHADER = 2 + }; enum DebugFlags { RENDER_DEBUG_NONE = 0, RENDER_DEBUG_HULLS = 1 @@ -112,6 +117,7 @@ namespace render { int _boundaryLevelAdjust { 0 }; RenderMode _renderMode { DEFAULT_RENDER_MODE }; DisplayMode _displayMode { MONO }; + OutlineFlags _outlineFlags{ RENDER_OUTLINE_NONE }; DebugFlags _debugFlags { RENDER_DEBUG_NONE }; gpu::Batch* _batch = nullptr; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3be8143830..462beac538 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -11,135 +11,137 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE -Script.include("../libraries/WebTablet.js"); + Script.include("../libraries/WebTablet.js"); -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. -var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. + var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); + var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); -var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// Event bridge messages. -var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; -var CLARA_IO_STATUS = "CLARA.IO STATUS"; -var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; -var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; -var GOTO_DIRECTORY = "GOTO_DIRECTORY"; -var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; -var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; -var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + // Event bridge messages. + var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; + var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; + var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; + var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; -var CLARA_DOWNLOAD_TITLE = "Preparing Download"; -var messageBox = null; -var isDownloadBeingCancelled = false; + var CLARA_DOWNLOAD_TITLE = "Preparing Download"; + var messageBox = null; + var isDownloadBeingCancelled = false; -var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel -var NO_BUTTON = 0; // QMessageBox::NoButton + var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel + var NO_BUTTON = 0; // QMessageBox::NoButton -var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; + var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; -function onMessageBoxClosed(id, button) { - if (id === messageBox && button === CANCEL_BUTTON) { - isDownloadBeingCancelled = true; - messageBox = null; - tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; + tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + } } -} -Window.messageBoxClosed.connect(onMessageBoxClosed); + Window.messageBoxClosed.connect(onMessageBoxClosed); -var onMarketplaceScreen = false; + var onMarketplaceScreen = false; -function showMarketplace() { - UserActivityLogger.openedMarketplace(); - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - tablet.webEventReceived.connect(function (message) { + function showMarketplace() { + UserActivityLogger.openedMarketplace(); + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.webEventReceived.connect(function (message) { - if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } + if (message === GOTO_DIRECTORY) { + tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + } - if (message === QUERY_CAN_WRITE_ASSETS) { - tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } + if (message === QUERY_CAN_WRITE_ASSETS) { + tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } + if (message === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + } - if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } return; } - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; } - return; - } - if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; } - return; - } + }); + } - if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var marketplaceButton = tablet.addButton({ + icon: "icons/tablet-icons/market-i.svg", + activeIcon: "icons/tablet-icons/market-a.svg", + text: "MARKET", + sortOrder: 9 }); -} -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var marketplaceButton = tablet.addButton({ - icon: "icons/tablet-icons/market-i.svg", - activeIcon: "icons/tablet-icons/market-a.svg", - text: "MARKET", - sortOrder: 9 -}); - -function onCanWriteAssetsChanged() { - var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - tablet.emitScriptEvent(message); -} - -function onClick() { - if (onMarketplaceScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); - showMarketplace(); + function onCanWriteAssetsChanged() { + var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + tablet.emitScriptEvent(message); } -} -function onScreenChanged(type, url) { - onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL - // for toolbar mode: change button to active when window is first openend, false otherwise. - marketplaceButton.editProperties({isActive: onMarketplaceScreen}); -} - -marketplaceButton.clicked.connect(onClick); -tablet.screenChanged.connect(onScreenChanged); -Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); - -Script.scriptEnding.connect(function () { - if (onMarketplaceScreen) { - tablet.gotoHomeScreen(); + function onClick() { + if (onMarketplaceScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + ContextOverlay.isInMarketplaceInspectionMode = false; + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + showMarketplace(); + ContextOverlay.isInMarketplaceInspectionMode = true; + } } - tablet.removeButton(marketplaceButton); - tablet.screenChanged.disconnect(onScreenChanged); - Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); -}); + + function onScreenChanged(type, url) { + onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL + // for toolbar mode: change button to active when window is first openend, false otherwise. + marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + } + + marketplaceButton.clicked.connect(onClick); + tablet.screenChanged.connect(onScreenChanged); + Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); + + Script.scriptEnding.connect(function () { + if (onMarketplaceScreen) { + tablet.gotoHomeScreen(); + } + tablet.removeButton(marketplaceButton); + tablet.screenChanged.disconnect(onScreenChanged); + Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); + }); }()); // END LOCAL_SCOPE From d2975c79c9b5f495e365a03fcfedd6f1ed37c308 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 08:41:26 -0700 Subject: [PATCH 156/280] add midi to 3 CMakeLists.txt for linking --- assignment-client/CMakeLists.txt | 2 +- plugins/oculusLegacy/CMakeLists.txt | 2 +- tests/controllers/CMakeLists.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 54afabfd21..f891afdbfb 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -11,7 +11,7 @@ endif () link_hifi_libraries( audio avatars octree gpu model fbx entities networking animation recording shared script-engine embedded-webserver - controllers physics plugins + controllers physics plugins midi ) if (WIN32) diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index f4e1bb76a2..12d0236cc2 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -13,7 +13,7 @@ if (APPLE) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() - link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins) + link_hifi_libraries(shared gl gpu gpu-gl plugins ui ui-plugins display-plugins input-plugins midi) include_hifi_library_headers(octree) diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 3aac4db0a8..7be71928be 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -6,7 +6,7 @@ setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers) +link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers midi) if (WIN32) @@ -16,4 +16,4 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) endif() -package_libraries_for_deployment() \ No newline at end of file +package_libraries_for_deployment() From 246a5c9fa4ca91cfa76c7bbaa7fb093395c1031c Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 08:59:48 -0700 Subject: [PATCH 157/280] 1 more --- tests/render-perf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 553e7c06e7..a1ecb7df2a 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural) +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural midi) package_libraries_for_deployment() From 33ff5917be70f26b4b5472c52661fcfd8ddfc9c5 Mon Sep 17 00:00:00 2001 From: "scromie@turnmeupgames.com" Date: Wed, 26 Jul 2017 19:34:20 +0300 Subject: [PATCH 158/280] fix --- scripts/system/html/js/entityProperties.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 777ef54085..7ef1a6974b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -475,6 +475,15 @@ function unbindAllInputs() { } } +function clearSelection() { + if(document.selection && document.selection.empty) { + document.selection.empty(); + } else if(window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + } +} + function loaded() { openEventBridge(function() { @@ -1051,6 +1060,7 @@ function loaded() { activeElement.select(); } } + clearSelection(); } }); } From 215fa7b816c943b656f7033eaf7183feb8f64fcb Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 09:34:46 -0700 Subject: [PATCH 159/280] removed extra comments --- libraries/midi/src/Midi.cpp | 79 +------------------------------------ 1 file changed, 1 insertion(+), 78 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index ca6c67ad81..7716602144 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -184,7 +184,7 @@ void Midi::noteReceived(int status, int note, int velocity) { eventData["status"] = status; eventData["note"] = note; eventData["velocity"] = velocity; -// emit midiNote(eventData); + emit midiNote(eventData); } // @@ -271,80 +271,3 @@ void Midi::thruModeEnable(bool enable) { thruModeEnabled = enable; } -/* -/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch x86_64 -isysroot -/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -L/Users/burt/hifi/assignment-client/Debug -F/Users/burt/hifi/assignment-client/Debug -F/Users/burt/Qt5.6.2/5.6/clang_64/lib -filelist -/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client.LinkFileList -Xlinker -rpath -Xlinker -/Users/burt/Qt5.6.2/5.6/clang_64/lib -mmacosx-version-min=10.8 -Xlinker -object_path_lto -Xlinker -/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_lto.o -Xlinker -no_deduplicate -stdlib=libc++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names -/Users/burt/hifi/libraries/audio/Debug/libaudio.a -/Users/burt/hifi/libraries/avatars/Debug/libavatars.a -/Users/burt/hifi/libraries/octree/Debug/liboctree.a -/Users/burt/hifi/libraries/gpu/Debug/libgpu.a -/Users/burt/hifi/libraries/model/Debug/libmodel.a -/Users/burt/hifi/libraries/fbx/Debug/libfbx.a -/Users/burt/hifi/libraries/entities/Debug/libentities.a -/Users/burt/hifi/libraries/networking/Debug/libnetworking.a -/Users/burt/hifi/libraries/animation/Debug/libanimation.a -/Users/burt/hifi/libraries/recording/Debug/librecording.a -/Users/burt/hifi/libraries/shared/Debug/libshared.a -/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a -/Users/burt/hifi/libraries/embedded-webserver/Debug/libembedded-webserver.a -/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a -/Users/burt/hifi/libraries/physics/Debug/libphysics.a -/Users/burt/hifi/libraries/plugins/Debug/libplugins.a -/Users/burt/hifi/libraries/ui/Debug/libui.a -/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a -/Users/burt/hifi/libraries/ui/Debug/libui.a -/Users/burt/hifi/libraries/recording/Debug/librecording.a -/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a -/Users/burt/hifi/libraries/physics/Debug/libphysics.a -/Users/burt/hifi/libraries/entities/Debug/libentities.a -/Users/burt/hifi/libraries/audio/Debug/libaudio.a -/Users/burt/hifi/libraries/plugins/Debug/libplugins.a -/Users/burt/hifi/libraries/avatars/Debug/libavatars.a -/Users/burt/hifi/libraries/octree/Debug/liboctree.a -/Users/burt/hifi/libraries/animation/Debug/libanimation.a -/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletDynamics.dylib -/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletCollision.dylib -/Users/burt/hifi/ext/Xcode/bullet/project/lib/libLinearMath.dylib -/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletSoftBody.dylib -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScriptTools.framework/QtScriptTools -/Users/burt/hifi/ext/Xcode/quazip/project/lib/libquazip5d.1.0.0.dylib -/Users/burt/hifi/libraries/procedural/Debug/libprocedural.a -/Users/burt/hifi/libraries/model-networking/Debug/libmodel-networking.a -/Users/burt/hifi/libraries/fbx/Debug/libfbx.a -/Users/burt/hifi/libraries/model/Debug/libmodel.a -/Users/burt/hifi/libraries/image/Debug/libimage.a -/Users/burt/hifi/ext/Xcode/nvtt/project/lib/libnvtt.dylib -/Users/burt/hifi/libraries/gpu-gl/Debug/libgpu-gl.a -/Users/burt/hifi/libraries/gpu/Debug/libgpu.a -/Users/burt/hifi/libraries/ktx/Debug/libktx.a -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtConcurrent.framework/QtConcurrent -lpthread -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebSockets.framework/QtWebSockets -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtXmlPatterns.framework/QtXmlPatterns -/Users/burt/hifi/libraries/gl/Debug/libgl.a -/Users/burt/hifi/libraries/networking/Debug/libnetworking.a -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngine.framework/QtWebEngine -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngineCore.framework/QtWebEngineCore -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQuick.framework/QtQuick -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebChannel.framework/QtWebChannel -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtPositioning.framework/QtPositioning -/usr/local/Cellar/openssl/1.0.2k/lib/libssl.dylib -/usr/local/Cellar/openssl/1.0.2k/lib/libcrypto.dylib -/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbb_debug.dylib -/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbbmalloc_debug.dylib -framework IOKit -framework CoreFoundation -/Users/burt/hifi/libraries/shared/Debug/libshared.a -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScript.framework/QtScript -/usr/local/lib/libz.a -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQml.framework/QtQml -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtOpenGL.framework/QtOpenGL -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWidgets.framework/QtWidgets -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtGui.framework/QtGui -framework OpenGL -/Users/burt/hifi/ext/Xcode/glew/project/lib/libglew_d.a -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtNetwork.framework/QtNetwork -/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtCore.framework/QtCore -Xlinker -dependency_info -Xlinker -/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_dependency_info.dat -o -/Users/burt/hifi/assignment-client/Debug/assignment-client -*/ - From c777384d939ce8c43a67dfd1473f11ef95d6850b Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 10:00:19 -0700 Subject: [PATCH 160/280] another comment removal --- libraries/midi/src/Midi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 54ffdb58e8..0ffa27986d 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -36,8 +36,8 @@ private: void MidiSetup(); void MidiCleanup(); -//signals: -// void midiNote(QVariantMap eventData); +signals: + void midiNote(QVariantMap eventData); public slots: /// play a note on all connected devices From 71ddf1e7c11ffa17c5065bd7a0446dc9daaa6dc4 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Wed, 26 Jul 2017 10:20:17 -0700 Subject: [PATCH 161/280] Removed Compound Collision type --- interface/resources/qml/AssetServer.qml | 11 ++--------- .../resources/qml/hifi/dialogs/TabletAssetServer.qml | 8 +------- .../resources/qml/hifi/tablet/NewModelDialog.qml | 3 +-- scripts/system/edit.js | 5 ----- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 11cab8b8de..a400897276 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -153,9 +153,7 @@ ScrollingWindow { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; - - + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -163,9 +161,7 @@ ScrollingWindow { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; - - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; var prompt = desktop.customInputDialog({ @@ -210,9 +206,6 @@ ScrollingWindow { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 15f72cfbcb..55c2c71c61 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -154,9 +154,7 @@ Rectangle { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; - var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -164,8 +162,7 @@ Rectangle { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; var prompt = tabletRoot.customInputDialog({ @@ -210,9 +207,6 @@ Rectangle { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index f0c9c3c14a..47d28486a9 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -147,8 +147,7 @@ Rectangle { "Good - Sub-meshes", "Exact - All polygons", "Box", - "Sphere", - "Compound"] + "Sphere"] } Row { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index fbd1732b0b..390bd5beb3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -339,7 +339,6 @@ var toolBar = (function () { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; var DYNAMIC_DEFAULT = false; function handleNewModelDialogResult(result) { @@ -362,9 +361,6 @@ var toolBar = (function () { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } @@ -464,7 +460,6 @@ var toolBar = (function () { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; // tablet version of new-model dialog From 9147bc21d0a3dadebf61fcf4220899fe45511c49 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 11:41:27 -0700 Subject: [PATCH 162/280] Getting there. --- .../ui/overlays/ContextOverlayInterface.cpp | 76 ++++++++----------- .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 20 +++++ libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityItemProperties.cpp | 6 ++ libraries/entities/src/EntityItemProperties.h | 1 + .../src/EntityItemPropertiesDefaults.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/marketplaces/marketplaces.js | 7 +- 12 files changed, 76 insertions(+), 53 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..2562d7aa76 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,6 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -76,13 +78,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 entityDimensions = entityProperties.getDimensions(); - glm::vec3 contextOverlayPosition; + glm::vec3 entityPosition = entityProperties.getPosition(); + glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec2 contextOverlayDimensions; - // Draw the outline overlay - // This also gives us the centerpoint of the entity even if - // the the entity doesn't use the default registration point. - glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + // Update the position of the overlay if the registration point of the entity + // isn't default + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -91,7 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& setCurrentEntityWithContextOverlay(entityItemID); // Here, we determine the position and dimensions of the Context Overlay. - if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + if (boundingBox.contains(cameraPosition)) { // If the camera is inside the box... // ...position the Context Overlay 1 meter in front of the camera. contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); @@ -104,17 +111,17 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // Else, place the Context Overlay some offset away from the entity's bounding // box in the direction of the camera. - auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); + float distance; + BoxFace face; + glm::vec3 normal; + bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } @@ -149,33 +156,6 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent return (entityProperties.getMarketplaceID().length() != 0); } -glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); - } - - // Setup and draw the bounding box around the entity - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); - } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); - // This returned position should always be the center of the entity - // even if the registration point isn't the default. - return bbPosition; -} - bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; @@ -185,10 +165,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - // Destroy the outline overlay - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _bbOverlay = NULL; - _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; @@ -227,6 +203,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } +void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + //if (contextOverlayFilterPassed(entityID)) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + //} +} + +void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 7c768dbfa0..c15e4854bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -23,7 +23,6 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -44,9 +43,7 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; - OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; - std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); @@ -64,6 +61,8 @@ public slots: void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; @@ -76,7 +75,6 @@ private: bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); - glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 52308499d7..a03360a0e5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,8 +372,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); - if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; bool success; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..82ddb9c90d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; requestedProperties += PROP_MARKETPLACE_ID; + requestedProperties += PROP_SHOULD_HIGHLIGHT; requestedProperties += PROP_NAME; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; @@ -277,6 +278,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); @@ -828,6 +830,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID); } + if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT) { + READ_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); + } + READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); @@ -2807,6 +2813,20 @@ void EntityItem::setMarketplaceID(const QString& value) { }); } +bool EntityItem::getShouldHighlight() const { + bool result; + withReadLock([&] { + result = _shouldHighlight; + }); + return result; +} + +void EntityItem::setShouldHighlight(const bool value) { + withWriteLock([&] { + _shouldHighlight = value; + }); +} + uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 92c83651aa..36ac6ba1cc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -316,6 +316,9 @@ public: QString getMarketplaceID() const; void setMarketplaceID(const QString& value); + bool getShouldHighlight() const; + void setShouldHighlight(const bool value); + // TODO: get rid of users of getRadius()... float getRadius() const; @@ -532,6 +535,7 @@ protected: QString _userData; SimulationOwner _simulationOwner; QString _marketplaceID; + bool _shouldHighlight { false }; QString _name; QString _href; //Hyperlink href QString _description; //Hyperlink description diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a207902789..aa7075f5e0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -289,6 +289,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); + CHECK_PROPERTY_CHANGE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); @@ -406,6 +407,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); @@ -982,6 +984,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); + ADD_PROPERTY_TO_MAP(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); @@ -1334,6 +1337,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, properties.getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -1632,6 +1636,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); @@ -1746,6 +1751,7 @@ void EntityItemProperties::markAllChanged() { //_alphaFinishChanged = true; _marketplaceIDChanged = true; + _shouldHighlightChanged = true; _keyLight.markAllChanged(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b526ac663c..916532b47c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -170,6 +170,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); + DEFINE_PROPERTY_REF(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool, ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index d52c5d9aab..43d0e33ba6 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -27,6 +27,7 @@ const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f); const bool ENTITY_ITEM_DEFAULT_LOCKED = false; const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString(""); +const bool ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT = false; const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid(); const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b3cfc143c2..9600d0d4fe 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -78,6 +78,7 @@ enum EntityPropertyList { PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_MARKETPLACE_ID, // all entities + PROP_SHOULD_HIGHLIGHT, // all entities PROP_ACCELERATION, // all entities PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID PROP_NAME, // all entities diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 240697d890..d2500196d9 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -62,7 +62,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_BULLET_DYNAMICS; + return VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 848bfd97cf..cb3db791b4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -218,6 +218,7 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; +const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 462beac538..418cf61834 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -116,12 +116,10 @@ if (onMarketplaceScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); - ContextOverlay.isInMarketplaceInspectionMode = false; } else { var entity = HMD.tabletID; Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); showMarketplace(); - ContextOverlay.isInMarketplaceInspectionMode = true; } } @@ -129,6 +127,11 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + if (onMarketplaceScreen) { + ContextOverlay.isInMarketplaceInspectionMode = true; + } else { + ContextOverlay.isInMarketplaceInspectionMode = false; + } } marketplaceButton.clicked.connect(onClick); From 24ddbb4b22353606fb48be2bcae53a9aeed93fe9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:08:55 -0700 Subject: [PATCH 163/280] Silly bug preventing the whole thing from working --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 978f20d743..27848a1193 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5396,9 +5396,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = static_cast(renderOutlineFlags | - static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; } + renderArgs->_outlineFlags = renderOutlineFlags; } } From 62f6c10b2b04306744b0bb87697997d4e8f77258 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:45:10 -0700 Subject: [PATCH 164/280] Renaming things; bugfixes; improvements --- interface/src/Application.cpp | 6 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 30 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 +++---- .../src/EntityTreeRenderer.cpp | 2 +- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27848a1193..d9b014d48d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1350,17 +1350,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 2562d7aa76..3fdd005672 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,8 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverLeaveEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -89,6 +89,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID @@ -159,6 +162,8 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -174,7 +179,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } -void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); @@ -183,7 +188,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co } } -void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -193,7 +198,7 @@ void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -203,16 +208,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - //if (contextOverlayFilterPassed(entityID)) { +void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (contextOverlayFilterPassed(entityID)) { qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); - //} + } } -void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (_currentEntityWithContextOverlay != entityID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + } } static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; @@ -227,5 +234,6 @@ void ContextOverlayInterface::openMarketplace() { QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); + _isInMarketplaceInspectionMode = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c15e4854bd..fc539661c4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,11 +58,11 @@ public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); - void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); - void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a8eca41077..d55352d31e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -675,7 +675,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = false; // for mouse moves we do not do precision picking + bool precisionPicking = true; // for mouse moves we do precision picking RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { From 65c28ca82123234ab22057668925b45e9b581236 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 13:17:51 -0700 Subject: [PATCH 165/280] Bugfixes --- .../src/ui/overlays/ContextOverlayInterface.cpp | 14 +++++++++++--- scripts/system/marketplaces/marketplaces.js | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3fdd005672..4ecf919cfe 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -97,6 +97,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); @@ -162,8 +168,10 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -222,7 +230,7 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; +static const QString MARKETPLACE_BASE_URL = "https://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 418cf61834..7b25589e92 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -127,7 +127,7 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); - if (onMarketplaceScreen) { + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { ContextOverlay.isInMarketplaceInspectionMode = false; From 5bd6dac66c87a3369e2e2360d689ec5ce46aa6ff Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:24:56 -0700 Subject: [PATCH 166/280] wrap text if longer than 1m text entity --- scripts/system/chat.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 58a1849f1f..65c7567b30 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -43,6 +43,7 @@ var speechBubbleOffset = {x: 0, y: 0.3, z: 0.0}; // The offset from the joint to whic the speech bubble is attached. var speechBubbleJointName = 'Head'; // The name of the joint to which the speech bubble is attached. var speechBubbleLineHeight = 0.05; // The height of a line of text in the speech bubble. + var SPEECH_BUBBLE_MAX_WIDTH = 1; // meters // Load the persistent variables from the Settings, with defaults. function loadSettings() { @@ -645,8 +646,16 @@ //print("updateSpeechBubble:", "speechBubbleMessage", speechBubbleMessage, "textSize", textSize.width, textSize.height); var fudge = 0.02; + var width = textSize.width + fudge; - var height = textSize.height + fudge; + var height = speechBubbleLineHeight + fudge; + + if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + var numLines = Math.ceil(width); + height = speechBubbleLineHeight * numLines + fudge; + width = SPEECH_BUBBLE_MAX_WIDTH; + }; + dimensions = { x: width, y: height, @@ -672,6 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); + position.y += height / 2; // offset based on wrapped height of bubble speechBubbleParams.position = position; if (!speechBubbleTextID) { From 256853f79b4d15370540950c3e2041c961ab5d1e Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:28:52 -0700 Subject: [PATCH 167/280] syntax and clarification on magic number 2 as half height --- scripts/system/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 65c7567b30..85de9fd0ac 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -650,7 +650,7 @@ var width = textSize.width + fudge; var height = speechBubbleLineHeight + fudge; - if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + if (textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; @@ -681,7 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); - position.y += height / 2; // offset based on wrapped height of bubble + position.y += height / 2; // offset based on half of bubble height speechBubbleParams.position = position; if (!speechBubbleTextID) { From a960ea5ccbba13fbeba68c55fcbd3de43bcb46c0 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 13:32:23 -0700 Subject: [PATCH 168/280] Linux build --- libraries/script-engine/CMakeLists.txt | 2 +- tests/controllers/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 39338fd767..42b2d51417 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) +link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 7be71928be..5757a3ad4b 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -6,7 +6,7 @@ setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers midi) +link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers) if (WIN32) From 3a05219c59ae3de69f7fe81e5883d2073f63ff97 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 14:10:41 -0700 Subject: [PATCH 169/280] Closer still --- .../ui/overlays/ContextOverlayInterface.cpp | 25 ++++--- .../src/ui/overlays/ContextOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 73 +++++++++++++------ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 4ecf919cfe..aa162e6101 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -89,8 +89,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + enableEntityHighlight(entityItemID); AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); @@ -99,8 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } // Update the cached "Current Entity with Context Overlay" variable @@ -169,8 +167,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); @@ -218,15 +215,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { if (contextOverlayFilterPassed(entityID)) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { if (_currentEntityWithContextOverlay != entityID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + disableEntityHighlight(entityID); } } @@ -245,3 +240,13 @@ void ContextOverlayInterface::openMarketplace() { _isInMarketplaceInspectionMode = true; } } + +void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); +} + +void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index fc539661c4..4b9c67c5d1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -63,6 +63,7 @@ public slots: void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; @@ -74,7 +75,8 @@ private: void openMarketplace(); bool _isInMarketplaceInspectionMode { false }; - bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + void enableEntityHighlight(const EntityItemID& entityItemID); + void disableEntityHighlight(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3268031f8b..247246b11d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var potentialEntityWithContextOverlay = false; +var hoveredEntityID = false; +var contextOverlayTimer = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2231,28 +2232,54 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && !entityWithContextOverlay) { - Script.setTimeout(function () { - if (rayPickInfo.entityID === potentialEntityWithContextOverlay && - !entityWithContextOverlay - && contextualHand !== -1) { - var pointerEvent = { - type: "Move", - id: contextualHand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; - } + if (rayPickInfo.entityID) { + if (hoveredEntityID !== rayPickInfo.entityID) { + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } - }, 500); - contextualHand = this.hand; - potentialEntityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = rayPickInfo.entityID; + Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); + } + + if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { + Script.clearTimeout(contextOverlayTimer); + } + + // If we already have a context overlay, we don't want to move it to + // another entity while we're searching. + if (!entityWithContextOverlay) { + contextOverlayTimer = Script.setTimeout(function () { + if (rayPickInfo.entityID === hoveredEntityID && + !entityWithContextOverlay + && contextualHand !== -1) { + var pointerEvent = { + type: "Move", + id: contextualHand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = false; + } + } + contextOverlayTimer = false; + }, 500); + contextualHand = this.hand; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3503,7 +3530,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; + hoveredEntityID = false; } if (isInEditMode()) { From 17c7e38fcecaf6b6c463d4a4da59724c007d1927 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:02:52 -0700 Subject: [PATCH 170/280] Bugfixes? --- .../system/controllers/handControllerGrab.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 247246b11d..37712ff90c 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2201,6 +2201,10 @@ function MyController(hand) { this.searchExit = function () { contextualHand = -1; + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + } + hoveredEntityID = false; }; this.search = function(deltaTime, timestamp) { @@ -2486,8 +2490,10 @@ function MyController(hand) { button: "None" }; + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); this.hoverEntity = entity; - Entities.sendHoverEnterEntity(entity, pointerEvent); + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); } // send mouse events for button highlights and tooltips. @@ -2551,8 +2557,11 @@ function MyController(hand) { button: "None" }; - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + if (this.hoverOverlay !== overlay) { + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(this.hoverOverlay, pointerEvent); + } // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || @@ -3527,10 +3536,13 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - hoveredEntityID = false; } if (isInEditMode()) { From 1336a59b6c35aaf9cbe21f259460b4e6d123e007 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:14:58 -0700 Subject: [PATCH 171/280] Stupid bracket --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 37712ff90c..a3bf6d4bd8 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2490,10 +2490,11 @@ function MyController(hand) { button: "None" }; - if (this.hoverEntity !== entity) { - Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); - this.hoverEntity = entity; - Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = entity; + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + } } // send mouse events for button highlights and tooltips. From 9c9cfad4c483ad8d626c2721ad8fb245ec76cfff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Jul 2017 15:56:08 -0700 Subject: [PATCH 172/280] use ctrl rather than alt to activate camera-mode shortcuts --- interface/src/Menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 72a3eb6bfc..56bd3eb749 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -223,17 +223,17 @@ Menu::Menu() { // View > First Person cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, Qt::ALT | Qt::Key_F, + viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F, true, qApp, SLOT(cameraMenuChanged()))); // View > Third Person cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_G, + viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G, false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FullscreenMirror, Qt::ALT | Qt::Key_H, + viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); // View > Independent [advanced] From 39cb0bc357e881f2cb7cdb6fff998205dd28a791 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:12:54 -0700 Subject: [PATCH 173/280] WHYYYY DOESN'T THIS WORK --- .../ui/overlays/ContextOverlayInterface.cpp | 12 +++-- .../system/controllers/handControllerGrab.js | 44 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index aa162e6101..e6d1af0af5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -242,11 +242,15 @@ void ContextOverlayInterface::openMarketplace() { } void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + } } void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + } } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index a3bf6d4bd8..82691003e2 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2236,18 +2236,22 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; if (rayPickInfo.entityID) { + print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { - pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } if (hoveredEntityID) { Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } @@ -2255,17 +2259,14 @@ function MyController(hand) { Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); } - if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { - Script.clearTimeout(contextOverlayTimer); - } - // If we already have a context overlay, we don't want to move it to // another entity while we're searching. - if (!entityWithContextOverlay) { + if (!entityWithContextOverlay && !contextOverlayTimer) { contextOverlayTimer = Script.setTimeout(function () { if (rayPickInfo.entityID === hoveredEntityID && - !entityWithContextOverlay - && contextualHand !== -1) { + !entityWithContextOverlay && + contextualHand !== -1 && + contextOverlayTimer) { var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2284,6 +2285,15 @@ function MyController(hand) { }, 500); contextualHand = this.hand; } + } else { + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); From 8a7561d61ba30391147691349c3ecab9416cf46a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:35:55 -0700 Subject: [PATCH 174/280] IT'S WORKING SOB SOB SOB --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++---- scripts/system/controllers/handControllerGrab.js | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e6d1af0af5..668766abbf 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -153,7 +153,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return true; } } else { - return destroyContextOverlay(entityItemID, event); + if (!_currentEntityWithContextOverlay.isNull()) { + return destroyContextOverlay(_currentEntityWithContextOverlay, event); + } + return false; } return false; } @@ -166,9 +169,7 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - disableEntityHighlight(_currentEntityWithContextOverlay); - } + disableEntityHighlight(entityItemID); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 82691003e2..bd8ab26506 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2246,7 +2246,6 @@ function MyController(hand) { button: "None" }; if (rayPickInfo.entityID) { - print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { if (contextOverlayTimer) { Script.clearTimeout(contextOverlayTimer); From c57660c82d3f17b8759f6dd3271d584c361f72f4 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Wed, 26 Jul 2017 16:52:00 -0700 Subject: [PATCH 175/280] Update BUILD_WIN.md --- BUILD_WIN.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 818a176f75..7587cf4512 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,20 +1,25 @@ This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit. ## Building High Fidelity +Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide. -### Step 1. Installing Visual Studio 2013 +Note: The prerequisites will require about 10 GB of space on your drive. -If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer. +### Step 1. Visual Studio 2017 -Note: Newer versions of Visual Studio are not yet compatible. +If you don’t have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/). + +When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right. In the end, your screen should look like this: ### Step 2. Installing CMake -Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. +Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). Make sure to check "Add CMake to system PATH for all users" when prompted during installation. ### Step 3. Installing Qt -Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe). +Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, deselect components so that your window looks like this: + +Note: Installing the Sources is optional but recommended if you have room for them (~2GB). Keep the default components checked when going through the installer. From 2a45c6d3dc170b913cd2c5519c97de4ea2f70aae Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 26 Jul 2017 16:53:18 -0700 Subject: [PATCH 176/280] Removed eigen dependency, fixed Debug Draw IK Chains Also, clarified for special case for secondary shoulder joint look-at constraint. --- cmake/externals/eigen/CMakeLists.txt | 20 ----- cmake/modules/FindEigen.cmake | 26 ------- .../animation/src/AnimInverseKinematics.cpp | 28 +++++-- plugins/openvr/CMakeLists.txt | 5 -- plugins/openvr/src/ViveControllerManager.cpp | 78 +------------------ plugins/openvr/src/ViveControllerManager.h | 5 -- 6 files changed, 21 insertions(+), 141 deletions(-) delete mode 100644 cmake/externals/eigen/CMakeLists.txt delete mode 100644 cmake/modules/FindEigen.cmake diff --git a/cmake/externals/eigen/CMakeLists.txt b/cmake/externals/eigen/CMakeLists.txt deleted file mode 100644 index 15d94576cc..0000000000 --- a/cmake/externals/eigen/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(EXTERNAL_NAME eigen) - -include(ExternalProject) -ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://bitbucket.org/eigen/eigen/get/3.3.4.zip - URL_MD5 e337acc279874bc6a56da4d973a723fb - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 -) - -# Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR} CACHE PATH "List of eigen include directories") \ No newline at end of file diff --git a/cmake/modules/FindEigen.cmake b/cmake/modules/FindEigen.cmake deleted file mode 100644 index 4b09ed8f2b..0000000000 --- a/cmake/modules/FindEigen.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# -# FindEigen.cmake -# -# Try to find Eigen include path. -# Once done this will define -# -# EIGEN_INCLUDE_DIRS -# -# Created on 7/14/2017 by Anthony Thibault -# Copyright 2017 High Fidelity, Inc. -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# - -# setup hints for Eigen search -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("eigen") - -# locate dir -string(CONCAT EIGEN_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS} "/src/eigen") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(EIGEN DEFAULT_MSG EIGEN_INCLUDE_DIRS) - -mark_as_advanced(EIGEN_INCLUDE_DIRS EIGEN_SEARCH_DIRS) \ No newline at end of file diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 10c4011f52..74fbf00067 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -27,9 +27,9 @@ static const int MAX_TARGET_MARKERS = 30; static const float JOINT_CHAIN_INTERP_TIME = 0.25f; static void lookupJointInfo(const AnimInverseKinematics::JointChainInfo& jointChainInfo, - int indexA, int indexB, - const AnimInverseKinematics::JointInfo** jointInfoA, - const AnimInverseKinematics::JointInfo** jointInfoB) { + int indexA, int indexB, + const AnimInverseKinematics::JointInfo** jointInfoA, + const AnimInverseKinematics::JointInfo** jointInfoB) { *jointInfoA = nullptr; *jointInfoB = nullptr; for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) { @@ -1640,8 +1640,10 @@ void AnimInverseKinematics::debugDrawIKChain(const JointChainInfo& jointChainInf // copy debug joint rotations into the relative poses for (size_t i = 0; i < jointChainInfo.jointInfoVec.size(); i++) { const JointInfo& info = jointChainInfo.jointInfoVec[i]; - poses[info.jointIndex].rot() = info.rot; - poses[info.jointIndex].trans() = info.trans; + if (info.jointIndex != _hipsIndex) { + poses[info.jointIndex].rot() = info.rot; + poses[info.jointIndex].trans() = info.trans; + } } // convert relative poses to absolute @@ -1867,9 +1869,19 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { return; } - // shoulder joint should look-at position of arm joint. + // special case for arm secondary poses. + // determine if shoulder joint should look-at position of arm joint. + bool shoulderShouldLookAtArm = false; const int leftArmIndex = _skeleton->nameToJointIndex("LeftArm"); const int rightArmIndex = _skeleton->nameToJointIndex("RightArm"); + const int leftShoulderIndex = _skeleton->nameToJointIndex("LeftShoulder"); + const int rightShoulderIndex = _skeleton->nameToJointIndex("RightShoulder"); + for (auto& iter : _secondaryTargetsInRigFrame) { + if (iter.first == leftShoulderIndex || iter.first == rightShoulderIndex) { + shoulderShouldLookAtArm = true; + break; + } + } AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix())); for (auto& iter : _secondaryTargetsInRigFrame) { @@ -1883,7 +1895,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { } // if parent should "look-at" child joint position. - if (iter.first == leftArmIndex || iter.first == rightArmIndex) { + if (shoulderShouldLookAtArm && (iter.first == leftArmIndex || iter.first == rightArmIndex)) { AnimPose grandParentAbsPose; int grandParentIndex = _skeleton->getParentIndex(parentIndex); @@ -1896,7 +1908,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) { _relativePoses[parentIndex] = grandParentAbsPose.inverse() * parentAbsPose; } - // AJT: for now ignore translation on secondary poses. + // Ignore translation on secondary poses, to prevent them from distorting the skeleton. glm::vec3 origTrans = _relativePoses[iter.first].trans(); _relativePoses[iter.first] = parentAbsPose.inverse() * absPose; _relativePoses[iter.first].trans() = origTrans; diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 6633b99b31..7878ae2d7e 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -18,14 +18,9 @@ if (WIN32) include_hifi_library_headers(octree) add_dependency_external_projects(OpenVR) - add_dependency_external_projects(eigen) find_package(OpenVR REQUIRED) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) - - # header only library - find_package(eigen REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${EIGEN_INCLUDE_DIRS}) endif() diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 395e131124..61a0b33255 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -35,8 +35,6 @@ #include #include -#include - extern PoseData _nextSimPoseData; vr::IVRSystem* acquireOpenVrSystem(); @@ -287,9 +285,7 @@ static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), - _system(system), - _leftControllerHistory(CONTROLLER_HISTORY_SIZE), - _rightControllerHistory(CONTROLLER_HISTORY_SIZE) { + _system(system) { _configStringMap[Config::None] = QString("None"); _configStringMap[Config::Feet] = QString("Feet"); @@ -915,16 +911,6 @@ void ViveControllerManager::InputDevice::handlePoseEvent(float deltaTime, const // transform into avatar frame glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; _poseStateMap[isLeftHand ? controller::LEFT_HAND : controller::RIGHT_HAND] = pose.transform(controllerToAvatar); - - // AJT: TODO ONLY DO THIS IF CALIBRATING! - // record controller history - if (isLeftHand) { - _leftControllerHistory[_leftControllerHistoryCursor] = pose.translation; - _leftControllerHistoryCursor = (_leftControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; - } else { - _rightControllerHistory[_rightControllerHistoryCursor] = pose.translation; - _rightControllerHistoryCursor = (_rightControllerHistoryCursor + 1) % CONTROLLER_HISTORY_SIZE; - } } bool ViveControllerManager::InputDevice::triggerHapticPulse(float strength, float duration, controller::Hand hand) { @@ -1088,64 +1074,6 @@ void ViveControllerManager::InputDevice::calibrateChest(const glm::mat4& default _pucksPostOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second); } -// assuming the user has kept his arms straight to his sides and has rotated his hand controllers -// up and down laterally about his shoulders. This will attempt to locate the users actual shoulders -// by calculating a best fit circle around the points, in the plane of the head's forward normal. -static glm::vec3 computeUserShoulderPositionFromHistory(const glm::mat4& headMat, const std::vector& history) { - glm::mat4 invHeadMat = glm::inverse(headMat); - - // AJT: TODO: we could perhaps reject the approximation if the radius is larger or smaller then we expect. - // AJT: TODO: abort if history is too small or invalid... - - // Take equation for a circle: (x - h)^2 + (y - k)^2 = r^2 - // And put it into the linear form: A * x = b - // - // where A = | (2 * x0) (2 * y0) 1 | - // | (2 * x1) (2 * y1) 1 | - // | . . . | - // | (2 * xn) (2 * yn) 1 | - // - // and b = | x0^2 + y0^2 | - // | x1^2 + y1^2 | - // | . | - // | xn^2 + yn^2 | - // - // and x = | h | - // | k | - // | r^2 - h^2 - k^2 | - // - - // Build aMat and bMat - Eigen::MatrixXf aMat(CONTROLLER_HISTORY_SIZE, 3); - Eigen::MatrixXf bMat(CONTROLLER_HISTORY_SIZE, 1); - for (int r = 0; r < CONTROLLER_HISTORY_SIZE; r++) { - // transform history[r] into local head coordinates - glm::vec3 p = transformPoint(invHeadMat, history[r]); - - // update the aMat with the appropriate 2d history values - aMat(r, 0) = 2.0f * p.x; - aMat(r, 1) = 2.0f * p.y; - aMat(r, 2) = 1.0f; - - // update the bMat with the appropriate 2d history values - bMat(r, 0) = p.x * p.x + p.y * p.y; - } - - // Then use least squares approximation to solve for the best x. - // http://math.mit.edu/~gs/linearalgebra/ila0403.pdf - Eigen::MatrixXf aTransMat = aMat.transpose(); - Eigen::MatrixXf xMat = (aTransMat * aMat).inverse() * aTransMat * bMat; - - // extract the 2d center of the circle from the xMat - glm::vec3 center(xMat(0, 0), xMat(1, 0), 0.0f); - - // we don't need the radius, but it's included here for completeness. - // float radius = center.x * center.x + center.y * center.y - xMat(2, 0); - - // transform center back into sensor coordinates - return transformPoint(headMat, center); -} - // y axis comes out of puck usb port/green light // -z axis comes out of puck center/vive logo static glm::vec3 computeUserShoulderPositionFromMeasurements(float armCirc, float shoulderSpan, const glm::mat4& headMat, const controller::Pose& armPuck, bool isLeftHand) { @@ -1176,10 +1104,6 @@ void ViveControllerManager::InputDevice::calibrateShoulders(const glm::mat4& def glm::mat4 userRefRightArm = refRightArm; glm::mat4 headMat = defaultToReferenceMat * inputCalibration.defaultHeadMat; - /* AJT: TODO REMOVE - userRefLeftArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _leftControllerHistory), 1.0f); - userRefRightArm[3] = glm::vec4(computeUserShoulderPositionFromHistory(headMat, _rightControllerHistory), 1.0f); - */ if (firstShoulderPose.translation.x < secondShoulderPose.translation.x) { _jointToPuckMap[controller::LEFT_ARM] = firstShoulder.first; diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 41ed494ffb..1b466d7329 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -192,11 +192,6 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; - std::vector _leftControllerHistory; - size_t _leftControllerHistoryCursor { 0 }; - std::vector _rightControllerHistory; - size_t _rightControllerHistoryCursor { 0 }; - QString configToString(Config config); friend class ViveControllerManager; }; From 081d8fbec442745aeb14acba95fa05e66bdfbc83 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Wed, 26 Jul 2017 17:00:48 -0700 Subject: [PATCH 177/280] More changes --- BUILD_WIN.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 7587cf4512..e9a4a5fd3b 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -9,7 +9,7 @@ Note: The prerequisites will require about 10 GB of space on your drive. If you don’t have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/). -When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right. In the end, your screen should look like this: +When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right. ### Step 2. Installing CMake @@ -17,31 +17,29 @@ Download and install the latest version of CMake 3.9. Download the file named w ### Step 3. Installing Qt -Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, deselect components so that your window looks like this: +Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.9.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)". Note: Installing the Sources is optional but recommended if you have room for them (~2GB). -Keep the default components checked when going through the installer. - ### Step 4. Setting Qt Environment Variable Go to `Control Panel > System > Advanced System Settings > Environment Variables > New...` (or search “Environment Variables” in Start Search). * Set "Variable name": `QT_CMAKE_PREFIX_PATH` -* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake` +* Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake` ### Step 5. Installing OpenSSL -Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe). +Download and install the Win64 OpenSSL v1.0.2 Installer[https://slproweb.com/products/Win32OpenSSL.html]. ### Step 6. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: -```` +``` cd "%HIFI_DIR%" mkdir build cd build -cmake .. -G "Visual Studio 12 Win64" -```` +cmake .. -G "Visual Studio 15 Win64" +``` Where `%HIFI_DIR%` is the directory for the highfidelity repository. @@ -87,4 +85,4 @@ If not, add the directory where nmake is located to the PATH environment variabl #### Qt is throwing an error -Make sure you have the correct version (5.6.2) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly. +Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly. From 560cccb0df94d61987395bf178cb7f69070b6366 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Wed, 26 Jul 2017 17:01:45 -0700 Subject: [PATCH 178/280] Remove nmake troubleshooting step --- BUILD_WIN.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index e9a4a5fd3b..3e93656d45 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -75,14 +75,6 @@ For any problems after Step #6, first try this: Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. -#### nmake cannot be found - -Make sure nmake.exe is located at the following path: - - C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin - -If not, add the directory where nmake is located to the PATH environment variable. - #### Qt is throwing an error Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly. From dd76c30de5b703a5250fddd4978e869b517922ce Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Thu, 27 Jul 2017 17:31:30 +0700 Subject: [PATCH 179/280] Make Xylophone mallets equipable, make the mallets provide haptic feedback, fix a closing bracket bug stopping the xylophone from working and clean up code according to coding standards --- .../marketplace/xylophone/pUtils.js | 34 ----- .../marketplace/xylophone/xylophoneKey.js | 48 +++++-- .../marketplace/xylophone/xylophoneRezzer.js | 117 +++++++++++------- 3 files changed, 111 insertions(+), 88 deletions(-) delete mode 100644 unpublishedScripts/marketplace/xylophone/pUtils.js diff --git a/unpublishedScripts/marketplace/xylophone/pUtils.js b/unpublishedScripts/marketplace/xylophone/pUtils.js deleted file mode 100644 index 2cafbc1f50..0000000000 --- a/unpublishedScripts/marketplace/xylophone/pUtils.js +++ /dev/null @@ -1,34 +0,0 @@ -// -// pUtils.js -// -// Created by Patrick Gosch on 03/28/2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -getEntityTextures = function(id) { - var results = null; - var properties = Entities.getEntityProperties(id, "textures"); - if (properties.textures) { - try { - results = JSON.parse(properties.textures); - } catch (err) { - logDebug(err); - logDebug(properties.textures); - } - } - return results ? results : {}; -}; - -setEntityTextures = function(id, textureList) { - var json = JSON.stringify(textureList); - Entities.editEntity(id, {textures: json}); -}; - -editEntityTextures = function(id, textureName, textureURL) { - var textureList = getEntityTextures(id); - textureList[textureName] = textureURL; - setEntityTextures(id, textureList); -}; diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index afba7e8075..38b8552b6e 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -11,8 +11,9 @@ (function() { Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; - var TEXGRAY = Script.resolvePath("xylotex_bar_gray.png"); - var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); + var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); + var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); + var IS_DEBUG = false; var _this; function XylophoneKey() { @@ -22,7 +23,7 @@ XylophoneKey.prototype = { sound: null, isWaiting: false, - homePos: null, + homePosition: null, injector: null, preload: function(entityID) { @@ -45,20 +46,51 @@ hit: function() { if (!_this.isWaiting) { _this.isWaiting = true; - _this.homePos = Entities.getEntityProperties(_this.entityID, ["position"]).position; - _this.injector = Audio.playSound(_this.sound, {position: _this.homePos, volume: 1}); - editEntityTextures(_this.entityID, "file5", TEXGRAY); + _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; + _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; + var HAPTIC_DURATION = 20; + var HAPTIC_HAND = 2; // Both hands + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); _this.timeout(); } }, timeout: function() { Script.setTimeout(function() { - editEntityTextures(_this.entityID, "file5", TEXBLACK); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_BLACK); _this.isWaiting = false; }, TIMEOUT); + }, + + getEntityTextures: function(id) { + var results = null; + var properties = Entities.getEntityProperties(id, "textures"); + if (properties.textures) { + try { + results = JSON.parse(properties.textures); + } catch (err) { + if (IS_DEBUG) { + print(err); + print(properties.textures); + } + } + } + return results ? results : {}; + }, + + setEntityTextures: function(id, textureList) { + var json = JSON.stringify(textureList); + Entities.editEntity(id, {textures: json}); + }, + + editEntityTextures: function(id, textureName, textureURL) { + var textureList = _this.getEntityTextures(id); + textureList[textureName] = textureURL; + _this.setEntityTextures(id, textureList); + } }; return new XylophoneKey(); - }); diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index 6416f81037..e99341bb19 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -8,65 +8,69 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var soundFiles = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; -var keyModelURL = Script.resolvePath("xyloKey_2_a_e.fbx"); -var keyScriptURL = Script.resolvePath("xylophoneKey.js"); -var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); -var malletModelURL = Script.resolvePath("Mallet3-2pc.fbx"); -var malletModelColliderURL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; +var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); +var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); +var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); +var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var FORWARD = { x: 0, y: 0, z: -1 }; var center = MyAvatar.position; -var fwd = {x:0, y:0, z:-1}; -var xyloFramePos = Vec3.sum(center, Vec3.multiply(fwd, 0.8)); -var xyloFrameID = Entities.addEntity( { +var XYLOPHONE_FORWARD_OFFSET = 0.8; +var xylophoneFramePosition = Vec3.sum(center, Vec3.multiply(FORWARD, XYLOPHONE_FORWARD_OFFSET)); +var xylophoneFrameID = Entities.addEntity({ name: "Xylophone", type: "Model", modelURL: Script.resolvePath("xylophoneFrameWithWave.fbx"), - position: xyloFramePos, - rotation: Quat.fromVec3Radians({x:0, y:Math.PI, z:0}), + position: xylophoneFramePosition, + rotation: Quat.fromVec3Radians({ x: 0, y: Math.PI, z: 0 }), shapeType: "static-mesh" }); -center.y += (0.45); // key Y offset from frame -var keyPos, keyRot, ud, td, keyID; -for (var i = 1; i <= soundFiles.length; i++) { +var KEY_Y_OFFSET = 0.45; +center.y += KEY_Y_OFFSET; +var keyPosition, keyRotation, userData, textureData, keyID; +var ROTATION_START = 0.9; +var ROTATION_DELTA = 0.2; +for (var i = 1; i <= SOUND_FILES.length; i++) { + + keyRotation = Quat.fromVec3Radians({ x: 0, y: ROTATION_START - (i*ROTATION_DELTA), z: 0 }); + keyPosition = Vec3.sum(center, Vec3.multiplyQbyV(keyRotation, FORWARD)); - keyRot = Quat.fromVec3Radians({x:0, y:(0.9 - (i*0.2)), z:0}); - keyPos = Vec3.sum(center, Vec3.multiplyQbyV(keyRot, fwd)); - - ud = { - soundFile: soundFiles[i-1] + userData = { + soundFile: SOUND_FILES[i-1] }; - td = { + textureData = { "file4": Script.resolvePath("xylotex_bar" + i + ".png"), - "file5": TEXBLACK + "file5": TEXTURE_BLACK }; - keyID = Entities.addEntity( { + keyID = Entities.addEntity({ name: ("XyloKey" + i), type: "Model", - modelURL: keyModelURL, - position: keyPos, - rotation: keyRot, + modelURL: KEY_MODEL_URL, + position: keyPosition, + rotation: keyRotation, shapeType: "static-mesh", - script: keyScriptURL, - textures: JSON.stringify(td), - userData: JSON.stringify(ud), - parentID: xyloFrameID - } ); + script: KEY_SCRIPT_URL, + textures: JSON.stringify(textureData), + userData: JSON.stringify(userData), + parentID: xylophoneFrameID + }); } // if rezzed on/above something, wait until after model has loaded so you can read its dimensions then move object on to that surface. -var pickRay = {origin: center, direction: {x:0, y:-1, z:0}}; +var pickRay = {origin: center, direction: {x: 0, y: -1, z: 0}}; var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects && (intersection.distance < 10)) { var surfaceY = intersection.intersection.y; Script.setTimeout( function() { // should add loop to check for fbx loaded instead of delay - var xyloDimensions = Entities.getEntityProperties(xyloFrameID, ["dimensions"]).dimensions; - xyloFramePos.y = surfaceY + (xyloDimensions.y/2); - Entities.editEntity(xyloFrameID, {position: xyloFramePos}); + var xylophoneDimensions = Entities.getEntityProperties(xylophoneFrameID, ["dimensions"]).dimensions; + xylophoneFramePosition.y = surfaceY + (xylophoneDimensions.y/2); + Entities.editEntity(xylophoneFrameID, {position: xylophoneFramePosition}); rezMallets(); }, 2000); } else { @@ -75,28 +79,49 @@ if (intersection.intersects && (intersection.distance < 10)) { } function rezMallets() { - var malletProps = { + var malletProperties = { name: "Xylophone Mallet", type: "Model", - modelURL: malletModelURL, - compoundShapeURL: malletModelColliderURL, - collidesWith: "static,dynamic,kinematic,", + modelURL: MALLET_MODEL_URL, + compoundShapeURL: MALLET_MODEL_COLLIDER_URL, + collidesWith: "static,dynamic,kinematic", collisionMask: 7, collisionsWillMove: 1, dynamic: 1, damping: 1, angularDamping: 1, shapeType: "compound", - userData: "{\"grabbableKey\":{\"grabbable\":true}}", - dimensions: {"x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911} // not being set from fbx for some reason. + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + LeftHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ], + RightHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ] + } + } + }), + dimensions: { "x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911 } // not being set from fbx for some reason. }; - malletProps.position = Vec3.sum(xyloFramePos, {x: 0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI - 0.1, z:0}); - Entities.addEntity(malletProps); + var LEFT_MALLET_POSITION = { x: 0.1, y: 0.55, z: 0 }; + var LEFT_MALLET_ROTATION = { x: 0, y: Math.PI - 0.1, z: 0 }; + var RIGHT_MALLET_POSITION = { x: -0.1, y: 0.55, z: 0 }; + var RIGHT_MALLET_ROTATION = { x: 0, y: Math.PI + 0.1, z: 0 }; - malletProps.position = Vec3.sum(xyloFramePos, {x: -0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI + 0.1, z:0}); - Entities.addEntity(malletProps); + malletProperties.position = Vec3.sum(xylophoneFramePosition, LEFT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(LEFT_MALLET_ROTATION); + Entities.addEntity(malletProperties); + + malletProperties.position = Vec3.sum(xylophoneFramePosition, RIGHT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(RIGHT_MALLET_ROTATION); + Entities.addEntity(malletProperties); Script.stop(); } \ No newline at end of file From 8dc97142827ec1e9fb1f72af5a3711239cbfa58b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 09:08:34 -0700 Subject: [PATCH 180/280] Pass armCircumference and shoulderWidth to qml. --- .../resources/qml/hifi/tablet/OpenVrConfiguration.qml | 3 +++ plugins/openvr/src/ViveControllerManager.cpp | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 36424e22b4..e3aff97557 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -878,6 +878,9 @@ Rectangle { var viveController = settings["handController"]; var desktopMode = settings["desktopMode"]; + armCircumference.value = settings.armCircumference; + shoulderWidth.value = settings.shoulderWidth; + if (HmdHead) { headBox.checked = true; headPuckBox.checked = false; diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 61a0b33255..5aaf92ed0b 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -375,6 +375,7 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input } static const float CM_TO_M = 0.01f; +static const float M_TO_CM = 100.0f; void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJsonObject configurationSettings) { Locker locker(_lock); @@ -411,6 +412,10 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; } + } else if (iter.key() == "armCircumference") { + _armCircumference = (float)iter.value().toDouble() * CM_TO_M; + } else if (iter.key() == "shoulderWidth") { + _shoulderWidth = (float)iter.value().toDouble() * CM_TO_M; } iter++; } @@ -429,6 +434,8 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() { configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD); configurationSettings["handController"] = (_handConfig == HandConfig::HandController); configurationSettings["puckCount"] = (int)_validTrackedObjects.size(); + configurationSettings["armCircumference"] = (double)_armCircumference * M_TO_CM; + configurationSettings["shoulderWidth"] = (double)_shoulderWidth * M_TO_CM; return configurationSettings; } From 8f5c41af2dac88a6e9f3ddb6b07225c0807e47d0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 09:44:37 -0700 Subject: [PATCH 181/280] changed _controllerPoseMap from unordered_map to map --- interface/src/avatar/MyAvatar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5983c6eb0e..298452cad8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -767,7 +767,7 @@ private: glm::vec3 _hoverReferenceCameraFacing { 0.0f, 0.0f, -1.0f }; // hmd sensor space // all poses are in sensor-frame - std::unordered_map _controllerPoseMap; + std::map _controllerPoseMap; mutable std::mutex _controllerPoseMapMutex; bool _hmdLeanRecenterEnabled = true; From 778c8bf9a749e76fa479c7d45f4f9aa657fb4264 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 10:08:35 -0700 Subject: [PATCH 182/280] clang build error fix --- interface/src/avatar/MySkeletonModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 4a86e0ce0a..6e29af0d33 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -82,9 +82,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { }; for (auto pair : primaryControllers) { - auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); - if (pose.isValid()) { - AnimPose pose(pose.getRotation(), pose.getTranslation()); + auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (controllerPose.isValid()) { + AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; params.primaryControllerActiveFlags[pair.second] = true; } else { From bcb4cc32d26718ee9c622202cc7339252dc44e87 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 10:26:10 -0700 Subject: [PATCH 183/280] another clang error fix --- interface/src/avatar/MySkeletonModel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6e29af0d33..6d468c3f30 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -113,9 +113,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { }; for (auto pair : secondaryControllers) { - auto pose = myAvatar->getControllerPoseInAvatarFrame(pair.first); - if (pose.isValid()) { - AnimPose pose(pose.getRotation(), pose.getTranslation()); + auto controllerPose = myAvatar->getControllerPoseInAvatarFrame(pair.first); + if (controllerPose.isValid()) { + AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; params.secondaryControllerActiveFlags[pair.second] = true; } else { From f7add1f6c6cd6a443577cf8e7fc93464376a48b1 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 13:17:42 -0700 Subject: [PATCH 184/280] Updating with the latest dependency info --- BUILD.md | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/BUILD.md b/BUILD.md index c45b7cb636..4627cbf25f 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,28 +1,27 @@ ### Dependencies -* [cmake](https://cmake.org/download/) ~> 3.3.2 -* [Qt](https://www.qt.io/download-open-source) ~> 5.6.2 -* [OpenSSL](https://www.openssl.org/community/binaries.html) - * IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities. -* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) +- [cmake](https://cmake.org/download/): 3.9 +- [Qt](https://www.qt.io/download-open-source): 5.9.1 +- [OpenSSL](https://www.openssl.org/): Use the latest available version of OpenSSL to avoid security vulnerabilities. +- [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) -#### CMake External Project Dependencies +### CMake External Project Dependencies -* [boostconfig](https://github.com/boostorg/config) ~> 1.58 -* [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.83 -* [GLEW](http://glew.sourceforge.net/) -* [glm](https://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4 -* [gverb](https://github.com/highfidelity/gverb) -* [Oculus SDK](https://developer.oculus.com/downloads/) ~> 0.6 (Win32) / 0.5 (Mac / Linux) -* [oglplus](http://oglplus.org/) ~> 0.63 -* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only) -* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1 -* [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/) ~> 0.7.1 -* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3 -* [soxr](https://sourceforge.net/p/soxr/wiki/Home/) ~> 0.1.1 -* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 -* [Sixense](http://sixense.com/) ~> 071615 -* [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only) +These dependencies need not be installed manually. They are automatically downloaded on the platforms where they are required. +- [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 +- [GLEW](http://glew.sourceforge.net/): 1.13 +- [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8 +- [gverb](https://github.com/highfidelity/gverb) +- [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac) +- [OpenVR](https://github.com/ValveSoftware/openvr): 1.0.6 (Win32 only) +- [Polyvox](http://www.volumesoffun.com/): 0.2.1 +- [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 +- [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 +- [soxr](https://sourceforge.net/p/soxr/wiki/Home/): 0.1.1 +- [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3 +- [Sixense](http://sixense.com/): 071615 +- [zlib](http://www.zlib.net/): 1.28 (Win32 only) +- nVidia Texture Tools: 2.1 The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. From 46809112645ae20928990824e66066d611811d0d Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 27 Jul 2017 14:47:30 -0700 Subject: [PATCH 185/280] Enabled multiple selection and multiple deletion in asset browser --- interface/resources/qml/AssetServer.qml | 55 ++++++++++++++----- .../qml/hifi/dialogs/TabletAssetServer.qml | 36 ++++++++++-- 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index eb47afc951..cdac0d22e8 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -28,6 +28,7 @@ ScrollingWindow { minSize: Qt.vector2d(200, 300) property int colorScheme: hifi.colorSchemes.dark + property int selectionMode: SelectionMode.ExtendedSelection HifiConstants { id: hifi } @@ -35,7 +36,8 @@ ScrollingWindow { property var assetProxyModel: Assets.proxyModel; property var assetMappingsModel: Assets.mappingModel; property var currentDirectory; - + property var selectedItems: treeView.selection.selectedIndexes.length; + Settings { category: "Overlay.AssetServer" property alias x: root.x @@ -48,7 +50,7 @@ ScrollingWindow { assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); reload(); } - + function doDeleteFile(path) { console.log("Deleting " + path); @@ -118,11 +120,23 @@ ScrollingWindow { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; + + if (selectedItems > 1) { + return false; + } return supportedExtensions.reduce(function(total, current) { return total | new RegExp(current).test(path); }, false); } + + function canRename() { + if (treeView.selection.hasSelection && selectedItems == 1) { + return true; + } else { + return false; + } + } function clear() { Assets.mappingModel.clear(); @@ -289,24 +303,38 @@ ScrollingWindow { }); } function deleteFile(index) { + var path=[]; + if (!index) { - index = treeView.selection.currentIndex; + for (var i = 0; i < selectedItems; i++) { + treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100); + index = treeView.selection.currentIndex; + path[i] = assetProxyModel.data(index, 0x100); + } } - var path = assetProxyModel.data(index, 0x100); + if (!path) { return; } - + + var modalMessage = ""; + var items = selectedItems.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; - + + if (selectedItems > 1) { + modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; + } else { + modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"; + } + var object = desktop.messageBox({ icon: hifi.icons.question, buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, defaultButton: OriginalDialogs.StandardButton.Yes, title: "Delete", - text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?" - }); + text: modalMessage + }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { doDeleteFile(path); @@ -445,20 +473,20 @@ ScrollingWindow { color: hifi.buttons.black colorScheme: root.colorScheme width: 120 - + enabled: canAddToWorld(assetProxyModel.data(treeView.selection.currentIndex, 0x100)) - + onClicked: root.addToWorld() } - + HifiControls.Button { text: "Rename" color: hifi.buttons.black colorScheme: root.colorScheme width: 80 - + onClicked: root.renameFile() - enabled: treeView.selection.hasSelection + enabled: canRename() } HifiControls.Button { @@ -514,6 +542,7 @@ ScrollingWindow { treeModel: assetProxyModel canEdit: true colorScheme: root.colorScheme + selectionMode: SelectionMode.ExtendedSelection modifyEl: renameEl diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 0256805422..9c4c104e08 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -37,6 +37,7 @@ Rectangle { property var assetProxyModel: Assets.proxyModel; property var assetMappingsModel: Assets.mappingModel; property var currentDirectory; + property var selectedItems: treeView.selection.selectedIndexes.length; Settings { category: "Overlay.AssetServer" @@ -119,11 +120,23 @@ Rectangle { function canAddToWorld(path) { var supportedExtensions = [/\.fbx\b/i, /\.obj\b/i]; + + if (selectedItems > 1) { + return false; + } return supportedExtensions.reduce(function(total, current) { return total | new RegExp(current).test(path); }, false); } + + function canRename() { + if (treeView.selection.hasSelection && selectedItems == 1) { + return true; + } else { + return false; + } + } function clear() { Assets.mappingModel.clear(); @@ -290,23 +303,37 @@ Rectangle { }); } function deleteFile(index) { + var path=[]; + if (!index) { - index = treeView.selection.currentIndex; + for (var i = 0; i < selectedItems; i++) { + treeView.selection.setCurrentIndex(treeView.selection.selectedIndexes[i], 0x100); + index = treeView.selection.currentIndex; + path[i] = assetProxyModel.data(index, 0x100); + } } - var path = assetProxyModel.data(index, 0x100); + if (!path) { return; } + var modalMessage = ""; + var items = selectedItems.toString(); var isFolder = assetProxyModel.data(treeView.selection.currentIndex, 0x101); var typeString = isFolder ? 'folder' : 'file'; + + if (selectedItems > 1) { + modalMessage = "You are about to delete " + items + " items \nDo you want to continue?"; + } else { + modalMessage = "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?"; + } var object = tabletRoot.messageBox({ icon: hifi.icons.question, buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, defaultButton: OriginalDialogs.StandardButton.Yes, title: "Delete", - text: "You are about to delete the following " + typeString + ":\n" + path + "\nDo you want to continue?" + text: modalMessage }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { @@ -459,7 +486,7 @@ Rectangle { width: 80 onClicked: root.renameFile() - enabled: treeView.selection.hasSelection + enabled: canRename() } HifiControls.Button { @@ -515,6 +542,7 @@ Rectangle { treeModel: assetProxyModel canEdit: true colorScheme: root.colorScheme + selectionMode: SelectionMode.ExtendedSelection modifyEl: renameEl From 07fc9a6dc322f56237394707c9770615a497debe Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 27 Jul 2017 14:53:49 -0700 Subject: [PATCH 186/280] Indentation fixes --- interface/resources/qml/AssetServer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index cdac0d22e8..cc94679558 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -334,7 +334,7 @@ ScrollingWindow { defaultButton: OriginalDialogs.StandardButton.Yes, title: "Delete", text: modalMessage - }); + }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { doDeleteFile(path); From a6a4bd59230db5f5dd4c796a2271167c5112c6d2 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 27 Jul 2017 15:55:30 -0700 Subject: [PATCH 187/280] Update AssetServer.qml --- interface/resources/qml/AssetServer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index cc94679558..279cb7b056 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -303,7 +303,7 @@ ScrollingWindow { }); } function deleteFile(index) { - var path=[]; + var path = []; if (!index) { for (var i = 0; i < selectedItems; i++) { From 1beec26003f47bdcedc1c0d2172b7ffe228d9ad4 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 27 Jul 2017 15:55:55 -0700 Subject: [PATCH 188/280] Update TabletAssetServer.qml --- interface/resources/qml/hifi/dialogs/TabletAssetServer.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 9c4c104e08..9aee579102 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -303,7 +303,7 @@ Rectangle { }); } function deleteFile(index) { - var path=[]; + var path = []; if (!index) { for (var i = 0; i < selectedItems; i++) { From e557f759b592468320db14231953c88c47baf5d7 Mon Sep 17 00:00:00 2001 From: burtsloane Date: Thu, 27 Jul 2017 16:17:23 -0700 Subject: [PATCH 189/280] Update Midi.cpp --- libraries/midi/src/Midi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 7716602144..ad650cf067 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -20,9 +20,11 @@ #endif +#if defined Q_OS_WIN32 const int MIDI_BYTE_MASK = 0x0FF; const int MIDI_SHIFT_NOTE = 8; const int MIDI_SHIFT_VELOCITY = 16; +#endif const int MIDI_STATUS_MASK = 0x0F0; const int MIDI_NOTE_OFF = 0x080; const int MIDI_NOTE_ON = 0x090; From 8b7a29c3f3250ebfd777bf93f8d0153981feb3b2 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Fri, 28 Jul 2017 06:22:29 +0700 Subject: [PATCH 190/280] remove putils reference --- unpublishedScripts/marketplace/xylophone/xylophoneKey.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 38b8552b6e..5cff66e557 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,7 +9,6 @@ // (function() { - Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); From 55e23a31069087ca6d4db8445c0fc72cff3bc050 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 16:57:08 -0700 Subject: [PATCH 191/280] Update BUILD.md --- BUILD.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 4627cbf25f..30302d611b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -11,13 +11,11 @@ These dependencies need not be installed manually. They are automatically downlo - [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases): 2.83 - [GLEW](http://glew.sourceforge.net/): 1.13 - [glm](https://glm.g-truc.net/0.9.8/index.html): 0.9.8 -- [gverb](https://github.com/highfidelity/gverb) - [Oculus SDK](https://developer.oculus.com/downloads/): 1.11 (Win32) / 0.5 (Mac) - [OpenVR](https://github.com/ValveSoftware/openvr): 1.0.6 (Win32 only) - [Polyvox](http://www.volumesoffun.com/): 0.2.1 - [QuaZip](https://sourceforge.net/projects/quazip/files/quazip/): 0.7.3 - [SDL2](https://www.libsdl.org/download-2.0.php): 2.0.3 -- [soxr](https://sourceforge.net/p/soxr/wiki/Home/): 0.1.1 - [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/): 4.3 - [Sixense](http://sixense.com/): 071615 - [zlib](http://www.zlib.net/): 1.28 (Win32 only) From 6b386000dbb6d33d4d99b2e277181b100a1118f2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 27 Jul 2017 17:11:13 -0700 Subject: [PATCH 192/280] initialize 'success' and use for return value --- libraries/shared/src/SpatiallyNestable.cpp | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 0a480d2d71..fc189c5e3e 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -949,34 +949,34 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { const float PARENTED_EXPANSION_FACTOR = 3.0f; bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { - bool success; + bool success = false; AACube maxAACube = getMaximumAACube(success); - if (success && (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube))) { - if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - } else { - _queryAACube = maxAACube; - } - - getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { - bool success; - AACube descendantAACube = descendant->getQueryAACube(success); - if (success) { - if (_queryAACube.contains(descendantAACube)) { - return; - } - _queryAACube += descendantAACube.getMinimumPoint(); - _queryAACube += descendantAACube.getMaximumPoint(); + if (success) { + // maybe update _queryAACube + if (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube)) { + if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { + // make an expanded AACube centered on the object + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + } else { + _queryAACube = maxAACube; } - }); - _queryAACubeSet = true; - return true; - } else { - return false; + getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { + bool childSuccess; + AACube descendantAACube = descendant->getQueryAACube(childSuccess); + if (childSuccess) { + if (_queryAACube.contains(descendantAACube)) { + return; + } + _queryAACube += descendantAACube.getMinimumPoint(); + _queryAACube += descendantAACube.getMaximumPoint(); + } + }); + _queryAACubeSet = true; + } } + return success; } void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) { From 6c2b6674bb22336a83d95b8830f51248b82b004b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 27 Jul 2017 17:33:54 -0700 Subject: [PATCH 193/280] load and save shoulderConfig to settings. --- .../qml/hifi/tablet/OpenVrConfiguration.qml | 9 +--- plugins/openvr/src/ViveControllerManager.cpp | 41 +++++++++++++++---- plugins/openvr/src/ViveControllerManager.h | 3 ++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index e3aff97557..78c10e2ffa 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -1052,17 +1052,12 @@ Rectangle { "Z": handZOffset.value } - var shoulderObject = { - "override": shouldersChecked, - "armCircumference": armCircumference.value, - "shoulderWidth": shoulderWidth.value - } - var settingsObject = { "bodyConfiguration": trackerConfiguration, "headConfiguration": headObject, "handConfiguration": handObject, - "shoulderConfiguration": shoulderObject, + "armCircumference": armCircumference.value, + "shoulderWidth": shoulderWidth.value, "desktopMode": viveInDesktop.checked } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 5aaf92ed0b..5a1c23839e 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -167,6 +167,7 @@ void ViveControllerManager::setConfigurationSettings(const QJsonObject configura } } _inputDevice->configureCalibrationSettings(configurationSettings); + saveSettings(); } } @@ -187,6 +188,8 @@ QString ViveControllerManager::configurationLayout() { bool ViveControllerManager::activate() { InputPlugin::activate(); + loadSettings(); + if (!_system) { _system = acquireOpenVrSystem(); } @@ -229,6 +232,8 @@ void ViveControllerManager::deactivate() { auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; + + saveSettings(); } bool ViveControllerManager::isHeadControllerMounted() const { @@ -281,7 +286,34 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu } } -static const size_t CONTROLLER_HISTORY_SIZE = 90 * 3; +void ViveControllerManager::loadSettings() { + Settings settings; + QString nameString = getName(); + settings.beginGroup(nameString); + { + if (_inputDevice) { + const double DEFAULT_ARM_CIRCUMFERENCE = 0.33; + const double DEFAULT_SHOULDER_WIDTH = 0.48; + _inputDevice->_armCircumference = settings.value("armCircumference", QVariant(DEFAULT_ARM_CIRCUMFERENCE)).toDouble(); + _inputDevice->_shoulderWidth = settings.value("shoulderWidth", QVariant(DEFAULT_SHOULDER_WIDTH)).toDouble(); + } + } + settings.endGroup(); +} + +void ViveControllerManager::saveSettings() const { + Settings settings; + QString nameString = getName(); + settings.beginGroup(nameString); + { + if (_inputDevice) { + settings.setValue(QString("armCircumference"), _inputDevice->_armCircumference); + settings.setValue(QString("shoulderWidth"), _inputDevice->_shoulderWidth); + } + } + settings.endGroup(); +} + ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), @@ -405,13 +437,6 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso } else { _handConfig = HandConfig::HandController; } - } else if (iter.key() == "shoulderConfiguration") { - QJsonObject shoulderObj = iter.value().toObject(); - bool shouldersChecked = shoulderObj["override"].toBool(); - if (shouldersChecked) { - _armCircumference = shoulderObj["armCircumference"].toDouble() * CM_TO_M; - _shoulderWidth = shoulderObj["shoulderWidth"].toDouble() * CM_TO_M; - } } else if (iter.key() == "armCircumference") { _armCircumference = (float)iter.value().toDouble() * CM_TO_M; } else if (iter.key() == "shoulderWidth") { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 1b466d7329..9a7b2cbc93 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -57,6 +57,9 @@ public: void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } + virtual void saveSettings() const override; + virtual void loadSettings() override; + private: class InputDevice : public controller::InputDevice { public: From c1d179eba102819c43fb4146ef9c0836bfec0551 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Jul 2017 14:20:49 +1200 Subject: [PATCH 194/280] Add "dominant hand changed" signal --- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 07843b8c33..490f22ed8b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -258,6 +258,7 @@ MyAvatar::~MyAvatar() { void MyAvatar::setDominantHand(const QString& hand) { if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) { _dominantHand = hand; + emit dominantHandChanged(_dominantHand); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1657f78b26..9ba6cb0353 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -615,6 +615,7 @@ signals: void wentAway(); void wentActive(); void skeletonChanged(); + void dominantHandChanged(const QString& hand); private: From bb5750449828d617bdaa53f2914a4c18cf3ecd03 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Jul 2017 19:39:22 -0700 Subject: [PATCH 195/280] Prevent deadlocks from Overlays update logic recursing into other Overlays calls --- interface/src/ui/overlays/Overlays.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 100f853a96..d21bc974a8 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -323,7 +323,7 @@ signals: private: void cleanupOverlaysToDelete(); - mutable QMutex _mutex; + mutable QMutex _mutex { QMutex::Recursive }; QMap _overlaysHUD; QMap _overlaysWorld; #if OVERLAY_PANELS From 4684b388c79a2a36d0a2556817fa71f9df25483a Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 27 Jul 2017 11:31:22 -0700 Subject: [PATCH 196/280] Merge pull request #10987 from SamGondelman/hmd59Crash2 Fix crash when putting on HMD for Qt 5.9 --- plugins/oculus/src/OculusBaseDisplayPlugin.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 93f4787f0f..0df504dfa2 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -126,15 +126,17 @@ void OculusBaseDisplayPlugin::internalDeactivate() { } bool OculusBaseDisplayPlugin::activateStandBySession() { - _session = acquireOculusSession(); if (!_session) { - return false; + _session = acquireOculusSession(); } - return true; + return _session; } void OculusBaseDisplayPlugin::deactivateSession() { - releaseOculusSession(); - _session = nullptr; + // FIXME + // Switching to Qt 5.9 exposed a race condition or similar issue that caused a crash when putting on an Rift + // while already in VR mode. Commenting these out is a workaround. + //releaseOculusSession(); + //_session = nullptr; } void OculusBaseDisplayPlugin::updatePresentPose() { //mat4 sensorResetMat; From 45934cb53e2b1622ef3a0366f6703b68f563ca33 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 28 Jul 2017 18:04:29 +0200 Subject: [PATCH 197/280] Fix Landscape mode, when menu pushed in Create mode. Fix pages gets white in Create mode --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 ++ libraries/ui/src/ui/TabletScriptingInterface.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..080c72f3bb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -205,6 +205,8 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); + _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); // mark the TabletProxy object as cpp ownership. diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6ff5e46cea..b74d9fcb5c 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -483,6 +483,11 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { return result; } + //set landscape off when pushing menu items while in Create mode + if (_landscape) { + setLandscape(false); + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; From 3ec9640ea1e8bdf201dd56e8043432bf9b4262e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Jul 2017 09:22:47 -0700 Subject: [PATCH 198/280] fix calculation of walk motor for HMD --- interface/src/avatar/MyAvatar.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b644defde2..83d170300b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1586,9 +1586,14 @@ void MyAvatar::updateMotors() { motorRotation = getMyHead()->getHeadOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift - // so we decompose camera's rotation and store the twist part in motorRotation + // we decompose camera's rotation and store the twist part in motorRotation + // however, we need to perform the decomposition in the avatar-frame + // using the local UP axis and then transform back into world-frame + glm::quat orientation = getOrientation(); + glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame glm::quat liftRotation; - motorRotation = getOrientation(); + swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation); + motorRotation = orientation * motorRotation; } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; From 3d0c13915a247040acbf631b9e91c68e9ba4152b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 09:33:02 -0700 Subject: [PATCH 199/280] Fix tablet button crash --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6ff5e46cea..37a2bae0bf 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -825,7 +825,13 @@ TabletButtonProxy::~TabletButtonProxy() { void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { Q_ASSERT(QThread::currentThread() == qApp->thread()); + if (_qmlButton) { + QObject::disconnect(_qmlButton, &QQuickItem::destroyed, this, nullptr); + } _qmlButton = qmlButton; + if (_qmlButton) { + QObject::connect(_qmlButton, &QQuickItem::destroyed, this, [this] { _qmlButton = nullptr; }); + } } void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { From cc4fbc97cdab663701315f823e928e9fd2429646 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Fri, 28 Jul 2017 15:59:17 -0400 Subject: [PATCH 200/280] [WL21389] PR1 Update based on code review discussion & feedback (details below). * Removed some left overs from prior approach. * Moved _collisionShapeType & getShapeType override from ShapeEntityItem to RenderableShapeEntityItem (see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r130154903) * Switched _collisionShapeType default from SHAPE_TYPE_NONE to SHAPE_TYPE_ELLIPSOID ** see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r129982909 Note(s): * Retested and the cylinder behaves as expected along with the Box & Sphere shapes save from the previously mentioned caveats in the PR notes (https://github.com/highfidelity/hifi/pull/11048) * Confirmed that currently unsupported shapes (hedrons, polygons, & cone) fallback to ellipsoid behavior given default change. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 112 +++++++++--------- .../src/RenderableShapeEntityItem.h | 7 ++ libraries/entities/src/ShapeEntityItem.cpp | 9 +- libraries/entities/src/ShapeEntityItem.h | 2 - libraries/shared/src/ShapeInfo.cpp | 2 +- 5 files changed, 67 insertions(+), 65 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5fa2354bd5..c853335915 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -91,67 +90,72 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { const glm::vec3 entityDimensions = getDimensions(); switch (_shape){ - case entity::Shape::Quad: - case entity::Shape::Cube: { - _collisionShapeType = SHAPE_TYPE_BOX; - } - break; - case entity::Shape::Sphere: { - - float diameter = entityDimensions.x; - const float MIN_DIAMETER = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (diameter > MIN_DIAMETER - && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { - - _collisionShapeType = SHAPE_TYPE_SPHERE; + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; } - else { + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; + } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO WL21389: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); + + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) + } + break; + default:{ _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - } - break; - case entity::Shape::Cylinder: { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO WL21389: determine if rotation is axis-aligned - //const Transform::Quat & rot = _transform.getRotation(); - - // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and - // hull ( or dimensions, need circular cross section) - // Should allow for minor variance along axes? - - } - break; - case entity::Shape::Triangle: - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: - case entity::Shape::Cone: { - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - } - break; - case entity::Shape::Torus: - { - // Not in GeometryCache::buildShapes, unsupported. - _collisionShapeType = SHAPE_TYPE_NONE; - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) - } - break; - default:{ - //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. - } - break; + break; } EntityItem::computeShapeInfo(info); } +// This value specifes how the shape should be treated by physics calculations. +ShapeType RenderableShapeEntityItem::getShapeType() const { + return _collisionShapeType; +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index f93e4b991e..d603cdedef 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -29,10 +29,17 @@ public: bool isTransparent() override; virtual void computeShapeInfo(ShapeInfo& info) override; + ShapeType getShapeType() const override; + private: std::unique_ptr _procedural { nullptr }; + //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain + //! prior functionality where new or unsupported shapes are treated as + //! ellipsoids. + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; + SIMPLE_RENDERABLE(); }; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 157e3afab3..eb1bed503c 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,12 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO WL21389: fill out with other shapes? + switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; - _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,11 +160,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -ShapeType ShapeEntityItem::getShapeType() const { - return _collisionShapeType; -} - void ShapeEntityItem::setColor(const rgbColor& value) { memcpy(_color, value, sizeof(rgbColor)); } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 42a92f7bd7..60fcfd628a 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } bool supportsDetailedRayIntersection() const override; @@ -100,7 +99,6 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5930880859..a556548b25 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -124,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO WL21389: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc). const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { From 17dd028ec7ffc761920dbcf4226bf691ef66acba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Jul 2017 14:23:18 -0700 Subject: [PATCH 201/280] make Paths available to desktop and tablet QML context --- interface/src/Application.cpp | 1 - libraries/ui/src/ui/OffscreenQmlSurface.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b7b674288..94d9d6e885 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2122,7 +2122,6 @@ void Application::initializeUi() { surfaceContext->setContextProperty("AvatarManager", DependencyManager::get().data()); surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface); surfaceContext->setContextProperty("LODManager", DependencyManager::get().data()); - surfaceContext->setContextProperty("Paths", DependencyManager::get().data()); surfaceContext->setContextProperty("HMD", DependencyManager::get().data()); surfaceContext->setContextProperty("Scene", DependencyManager::get().data()); surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 34865ea058..2012ebbe30 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -329,6 +329,7 @@ void initializeQmlEngine(QQmlEngine* engine, QQuickWindow* window) { rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext)); rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext)); rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext)); + rootContext->setContextProperty("Paths", DependencyManager::get().data()); } QQmlEngine* acquireEngine(QQuickWindow* window) { From d42052a299e7746ee7a230cca2e39a4e772f0a7c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 14:56:21 -0700 Subject: [PATCH 202/280] Fix overlay event handling thread issues --- interface/src/ui/overlays/Overlays.cpp | 12 ++-- interface/src/ui/overlays/Web3DOverlay.cpp | 83 +++++++++------------- interface/src/ui/overlays/Web3DOverlay.h | 9 --- 3 files changed, 41 insertions(+), 63 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 72682fcb8c..7fb4722c7d 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -706,27 +706,27 @@ bool Overlays::isAddedOverlay(OverlayID id) { } void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) { - emit mousePressOnOverlay(overlayID, event); + QMetaObject::invokeMethod(this, "mousePressOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event)); } void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) { - emit mouseReleaseOnOverlay(overlayID, event); + QMetaObject::invokeMethod(this, "mouseReleaseOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event)); } void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) { - emit mouseMoveOnOverlay(overlayID, event); + QMetaObject::invokeMethod(this, "mouseMoveOnOverlay", Q_ARG(OverlayID, overlayID), Q_ARG(PointerEvent, event)); } void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) { - emit hoverEnterOverlay(id, event); + QMetaObject::invokeMethod(this, "hoverEnterOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event)); } void Overlays::sendHoverOverOverlay(OverlayID id, PointerEvent event) { - emit hoverOverOverlay(id, event); + QMetaObject::invokeMethod(this, "hoverOverOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event)); } void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) { - emit hoverLeaveOverlay(id, event); + QMetaObject::invokeMethod(this, "hoverLeaveOverlay", Q_ARG(OverlayID, id), Q_ARG(PointerEvent, event)); } OverlayID Overlays::getKeyboardFocusOverlay() { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..a069b67d2b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -100,30 +100,14 @@ Web3DOverlay::~Web3DOverlay() { } _webSurface->pause(); - _webSurface->disconnect(_connection); - - QObject::disconnect(_mousePressConnection); - _mousePressConnection = QMetaObject::Connection(); - QObject::disconnect(_mouseReleaseConnection); - _mouseReleaseConnection = QMetaObject::Connection(); - QObject::disconnect(_mouseMoveConnection); - _mouseMoveConnection = QMetaObject::Connection(); - QObject::disconnect(_hoverLeaveConnection); - _hoverLeaveConnection = QMetaObject::Connection(); - - QObject::disconnect(_emitScriptEventConnection); - _emitScriptEventConnection = QMetaObject::Connection(); - QObject::disconnect(_webEventReceivedConnection); - _webEventReceivedConnection = QMetaObject::Connection(); - - // The lifetime of the QML surface MUST be managed by the main thread - // Additionally, we MUST use local variables copied by value, rather than - // member variables, since they would implicitly refer to a this that - // is no longer valid - auto webSurface = _webSurface; - AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { - DependencyManager::get()->release(QML, webSurface); - }); + auto overlays = &(qApp->getOverlays()); + QObject::disconnect(overlays, &Overlays::mousePressOnOverlay, this, nullptr); + QObject::disconnect(overlays, &Overlays::mouseReleaseOnOverlay, this, nullptr); + QObject::disconnect(overlays, &Overlays::mouseMoveOnOverlay, this, nullptr); + QObject::disconnect(overlays, &Overlays::hoverLeaveOverlay, this, nullptr); + QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); + QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); + DependencyManager::get()->release(QML, _webSurface); _webSurface.reset(); } auto geometryCache = DependencyManager::get(); @@ -153,6 +137,9 @@ QString Web3DOverlay::pickURL() { void Web3DOverlay::loadSourceURL() { + if (!_webSurface) { + return; + } QUrl sourceUrl(_url); if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || @@ -252,10 +239,11 @@ void Web3DOverlay::render(RenderArgs* args) { } }; - _mousePressConnection = connect(&(qApp->getOverlays()), &Overlays::mousePressOnOverlay, this, forwardPointerEvent, Qt::DirectConnection); - _mouseReleaseConnection = connect(&(qApp->getOverlays()), &Overlays::mouseReleaseOnOverlay, this, forwardPointerEvent, Qt::DirectConnection); - _mouseMoveConnection = connect(&(qApp->getOverlays()), &Overlays::mouseMoveOnOverlay, this, forwardPointerEvent, Qt::DirectConnection); - _hoverLeaveConnection = connect(&(qApp->getOverlays()), &Overlays::hoverLeaveOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) { + auto overlays = &(qApp->getOverlays()); + QObject::connect(overlays, &Overlays::mousePressOnOverlay, this, forwardPointerEvent); + QObject::connect(overlays, &Overlays::mouseReleaseOnOverlay, this, forwardPointerEvent); + QObject::connect(overlays, &Overlays::mouseMoveOnOverlay, this, forwardPointerEvent); + QObject::connect(overlays, &Overlays::hoverLeaveOverlay, this, [=](OverlayID overlayID, const PointerEvent& event) { auto self = weakSelf.lock(); if (!self) { return; @@ -265,10 +253,10 @@ void Web3DOverlay::render(RenderArgs* args) { event.getButton(), event.getButtons(), event.getKeyboardModifiers()); forwardPointerEvent(overlayID, event); } - }, Qt::DirectConnection); + }); - _emitScriptEventConnection = connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); - _webEventReceivedConnection = connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); + QObject::connect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent); + QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); } else { if (_currentMaxFPS != _desiredMaxFPS) { setMaxFPS(_desiredMaxFPS); @@ -438,11 +426,11 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { QList touchPoints; touchPoints.push_back(point); - QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setTarget(_webSurface->getRootItem()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(touchPointState); + QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + touchEvent.setWindow(_webSurface->getWindow()); + touchEvent.setTarget(_webSurface->getRootItem()); + touchEvent.setTouchPoints(touchPoints); + touchEvent.setTouchPointStates(touchPointState); // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). @@ -452,16 +440,16 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { // receive mouse events #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) if (event.getType() == PointerEvent::Move) { - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + QMouseEvent mouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } #endif - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) if (event.getType() == PointerEvent::Move) { - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + QMouseEvent mouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } #endif } @@ -505,8 +493,8 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { return; } - QMouseEvent* mouseEvent = new QMouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } void Web3DOverlay::setProperties(const QVariantMap& properties) { @@ -608,6 +596,9 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { _scriptURL = scriptURL; if (_webSurface) { AbstractViewStateInterface::instance()->postLambdaEvent([this, scriptURL] { + if (!_webSurface) { + return; + } _webSurface->getRootItem()->setProperty("scriptURL", scriptURL); }); } @@ -631,9 +622,5 @@ Web3DOverlay* Web3DOverlay::createClone() const { } void Web3DOverlay::emitScriptEvent(const QVariant& message) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message)); - } else { - emit scriptEventReceived(message); - } + QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message)); } diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 1e3706ed25..aba2ee8555 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -72,7 +72,6 @@ signals: private: InputMode _inputMode { Touch }; QSharedPointer _webSurface; - QMetaObject::Connection _connection; gpu::TexturePointer _texture; QString _url; QString _scriptURL; @@ -88,14 +87,6 @@ private: uint8_t _currentMaxFPS { 0 }; bool _mayNeedResize { false }; - - QMetaObject::Connection _mousePressConnection; - QMetaObject::Connection _mouseReleaseConnection; - QMetaObject::Connection _mouseMoveConnection; - QMetaObject::Connection _hoverLeaveConnection; - - QMetaObject::Connection _emitScriptEventConnection; - QMetaObject::Connection _webEventReceivedConnection; }; #endif // hifi_Web3DOverlay_h From 5946a965da15e2b1b733da43b1e8bfbb1de79504 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 18:51:10 -0700 Subject: [PATCH 203/280] Disable touch event emulation for tablet until we fix crash --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a069b67d2b..32b16ff39b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -337,11 +337,19 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { + // FIXME touch event emulation is broken in some way. Do NOT enable this code + // unless you have done a debug build of the application and verified that + // you are not getting assertion errors on handling the touch events inside + // Qt. +#if 0 if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { handlePointerEventAsMouse(event); } +#else + handlePointerEventAsMouse(event); +#endif } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { From 3c9ab36416a187251a22947358a522dfdc4e6ace Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 29 Jul 2017 12:00:41 +0200 Subject: [PATCH 204/280] Remove already already implemented fix --- interface/src/ui/overlays/Web3DOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ffb4faf83e..2bcb247df8 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -192,7 +192,6 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); From 734330dc063311990e8308bed080582b8655fbc7 Mon Sep 17 00:00:00 2001 From: Menithal Date: Sat, 29 Jul 2017 15:48:31 +0300 Subject: [PATCH 205/280] Fixed Particle Editor Update event Particle Editor was agressively refreshing when ever a particle was be ing up dated Added a flag check to make sure the particle was always using the latest version --- scripts/system/edit.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..02cc35e645 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -14,7 +14,7 @@ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE - + "use strict"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function () { +selectionManager.addEventListener(function (test) { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); @@ -275,7 +275,8 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); } entityID = Entities.addEntity(properties); - if (properties.type == "ParticleEffect") { + + if (properties.type === "ParticleEffect") { selectParticleEntity(entityID); } @@ -2229,10 +2230,9 @@ var particleExplorerTool = new ParticleExplorerTool(); var selectedParticleEntity = 0; var selectedParticleEntityID = null; - function selectParticleEntity(entityID) { var properties = Entities.getEntityProperties(entityID); - + selectedParticleEntityID = entityID; if (properties.emitOrientation) { properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); } @@ -2274,7 +2274,6 @@ entityListTool.webView.webEventReceived.connect(function (data) { return; } // Destroy the old particles web view first - selectParticleEntity(ids[0]); } else { selectedParticleEntity = 0; particleExplorerTool.destroyWebView(); From 7d18d2e01788e8c7d07cd204edad031f37493c55 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 22:14:18 -0700 Subject: [PATCH 206/280] Restore corrected touch event simulation --- interface/src/ui/overlays/Web3DOverlay.cpp | 162 ++++++++++----------- interface/src/ui/overlays/Web3DOverlay.h | 2 + 2 files changed, 75 insertions(+), 89 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 32b16ff39b..ffdafa3217 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -248,7 +248,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (!self) { return; } - if (self->_pressed && overlayID == selfOverlayID) { + if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) { PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), event.getButton(), event.getButtons(), event.getKeyboardModifiers()); forwardPointerEvent(overlayID, event); @@ -337,19 +337,11 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { - // FIXME touch event emulation is broken in some way. Do NOT enable this code - // unless you have done a debug build of the application and verified that - // you are not getting assertion errors on handling the touch events inside - // Qt. -#if 0 if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { handlePointerEventAsMouse(event); } -#else - handlePointerEventAsMouse(event); -#endif } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { @@ -357,19 +349,68 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { return; } - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); - QPointF windowPoint(windowPos.x, windowPos.y); - - if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { - this->_pressed = true; - } else if (event.getType() == PointerEvent::Release && event.getButton() == PointerEvent::PrimaryButton) { - this->_pressed = false; + //do not send secondary button events to tablet + if (event.getButton() == PointerEvent::SecondaryButton || + //do not block composed events + event.getButtons() == PointerEvent::SecondaryButton) { + return; } - QEvent::Type touchType; - Qt::TouchPointState touchPointState; - QEvent::Type mouseType; + QPointF windowPoint; + { + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); + windowPoint = QPointF(windowPos.x, windowPos.y); + } + + Qt::TouchPointState state = Qt::TouchPointStationary; + if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { + state = Qt::TouchPointPressed; + } else if (event.getType() == PointerEvent::Release) { + state = Qt::TouchPointReleased; + } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) { + state = Qt::TouchPointMoved; + } + + QEvent::Type touchType = QEvent::TouchUpdate; + if (_activeTouchPoints.empty()) { + // If the first active touch point is being created, send a begin + touchType = QEvent::TouchBegin; + } if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) { + // If the last active touch point is being released, send an end + touchType = QEvent::TouchEnd; + } + + { + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(state); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + _activeTouchPoints[event.getID()] = point; + } + + QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + { + QList touchPoints; + Qt::TouchPointStates touchPointStates; + for (const auto& entry : _activeTouchPoints) { + touchPointStates |= entry.second.state(); + touchPoints.push_back(entry.second); + } + + touchEvent.setWindow(_webSurface->getWindow()); + touchEvent.setTarget(_webSurface->getRootItem()); + touchEvent.setTouchPoints(touchPoints); + touchEvent.setTouchPointStates(touchPointStates); + } + + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + // + // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will + // receive mouse events Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons = Qt::NoButton; if (event.getButton() == PointerEvent::PrimaryButton) { @@ -379,84 +420,27 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { buttons |= Qt::LeftButton; } - switch (event.getType()) { - case PointerEvent::Press: - touchType = QEvent::TouchBegin; - touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; - break; - case PointerEvent::Release: - touchType = QEvent::TouchEnd; - touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; - break; - case PointerEvent::Move: - touchType = QEvent::TouchUpdate; - touchPointState = Qt::TouchPointMoved; - mouseType = QEvent::MouseMove; - - if (((event.getButtons() & PointerEvent::PrimaryButton) > 0) != this->_pressed) { - // Mouse was pressed/released while off the overlay; convert touch and mouse events to press/release to reflect - // current mouse/touch status. - this->_pressed = !this->_pressed; - if (this->_pressed) { - touchType = QEvent::TouchBegin; - touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; - - } else { - touchType = QEvent::TouchEnd; - touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; - - } - button = Qt::LeftButton; - buttons |= Qt::LeftButton; - } - - break; - default: - return; - } - - //do not send secondary button events to tablet - if (event.getButton() == PointerEvent::SecondaryButton || - //do not block composed events - event.getButtons() == PointerEvent::SecondaryButton) { - return; - } - - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(touchPointState); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); - - QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - touchEvent.setWindow(_webSurface->getWindow()); - touchEvent.setTarget(_webSurface->getRootItem()); - touchEvent.setTouchPoints(touchPoints); - touchEvent.setTouchPointStates(touchPointState); - - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - // - // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will - // receive mouse events #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } #endif - QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + + if (touchType == QEvent::TouchBegin) { + _touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + } else if (_touchBeginAccepted) { + QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + } + + // If this was a release event, remove the point from the active touch points + if (state == Qt::TouchPointReleased) { + _activeTouchPoints.erase(event.getID()); + } #if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } #endif diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index aba2ee8555..61dc7a1749 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -81,6 +81,8 @@ private: bool _showKeyboardFocusHighlight{ true }; bool _pressed{ false }; + bool _touchBeginAccepted { false }; + std::map _activeTouchPoints; QTouchDevice _touchDevice; uint8_t _desiredMaxFPS { 10 }; From 17b06e8e12e504fb167ee762fca56de5d6da7419 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 11:54:39 -0700 Subject: [PATCH 207/280] Fix log warnings from non-notifiable properties --- libraries/shared/src/PathUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 577c215c9e..3cb3cd3b63 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -25,8 +25,8 @@ class PathUtils : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - Q_PROPERTY(QString resources READ resourcesPath) - Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation) + Q_PROPERTY(QString resources READ resourcesPath CONSTANT) + Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation CONSTANT) public: static const QString& resourcesPath(); From af2452eb644a28f897a0d6a643095c881b778b68 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 29 Jul 2017 13:06:07 -0700 Subject: [PATCH 208/280] Revert "Disable touch event emulation for tablet until we fix crash" --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 32b16ff39b..a069b67d2b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -337,19 +337,11 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { - // FIXME touch event emulation is broken in some way. Do NOT enable this code - // unless you have done a debug build of the application and verified that - // you are not getting assertion errors on handling the touch events inside - // Qt. -#if 0 if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { handlePointerEventAsMouse(event); } -#else - handlePointerEventAsMouse(event); -#endif } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { From 861b33a84563185027a559473909b36b613a470f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 17:22:25 -0700 Subject: [PATCH 209/280] Fix potential nullptr access in EntityTreeElement --- libraries/entities/src/EntityTreeElement.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 108cb39222..487bf60f61 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -1001,7 +1001,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int if (!bestFitBefore && bestFitAfter) { // This is the case where the entity existed, and is in some element in our tree... if (currentContainingElement.get() != this) { - currentContainingElement->removeEntityItem(entityItem); + // if the currentContainingElement is non-null, remove the entity from it + if (currentContainingElement) { + currentContainingElement->removeEntityItem(entityItem); + } addEntityItem(entityItem); } } From bb7110d81b47957a6757b801ccf3af39c6f2fc2a Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 30 Jul 2017 19:22:54 +0200 Subject: [PATCH 210/280] Desktop Running scripts window now closed and as soon as Tablet opened, it will show Running Script. TODO: make Tablet opens automatically --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++++++++++++- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..9ed87ea337 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -208,12 +208,13 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _toolbarMode = toolbarMode; + auto offscreenUi = DependencyManager::get(); + if (toolbarMode) { removeButtonsFromHomeScreen(); addButtonsToToolbar(); // create new desktop window - auto offscreenUi = DependencyManager::get(); auto tabletRootWindow = new TabletRootWindow(); tabletRootWindow->initQml(QVariantMap()); auto quickItem = tabletRootWindow->asQuickItem(); @@ -230,16 +231,37 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { removeButtonsFromToolbar(); addButtonsToHomeScreen(); + //check if running scripts opened and save it for reopen in Tablet + if (offscreenUi->isVisible("RunningScripts")) { + offscreenUi->hide("RunningScripts"); + _showRunningScripts = true; + } // destroy desktop window if (_desktopWindow) { _desktopWindow->deleteLater(); _desktopWindow = nullptr; } } + loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); + + //connect to Tablet shown signal to open running scripts + if (_showRunningScripts) { + QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); + //qApp->toggleTabletUI(true); + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + } } + static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (buttonProxy == NULL){ diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index e61398585e..82b7ad52b6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -252,6 +252,7 @@ protected: enum class State { Uninitialized, Home, Web, Menu, QML }; State _state { State::Uninitialized }; bool _landscape { false }; + bool _showRunningScripts { false }; }; Q_DECLARE_METATYPE(TabletProxy*); From b5f5a037f007e91b22d27cb39507e347637e7bed Mon Sep 17 00:00:00 2001 From: humbletim Date: Sun, 30 Jul 2017 16:25:56 -0400 Subject: [PATCH 211/280] remove "grabbable: true" property from attachment Overlays --- .../dist/app-doppleganger-marketplace.js | 3 +-- .../doppleganger-attachments/doppleganger-attachments.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js index 6abd3a0fc5..c2cf2a2353 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -1075,7 +1075,7 @@ dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = __webpack_require__(1), @@ -1196,7 +1196,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index 5dee264fae..7526f56511 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -6,7 +6,7 @@ // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -127,7 +127,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, From 5fab6c37a3daec1beb0302af43fff7ce2f46f05a Mon Sep 17 00:00:00 2001 From: Matti Lahtinen Date: Mon, 31 Jul 2017 08:56:04 +0300 Subject: [PATCH 212/280] Removed stray variable test --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 02cc35e645..300db5d50a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function (test) { +selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); From 1f6b92a5190966886712a5356ecef593215eb75b Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Mon, 31 Jul 2017 17:27:49 +0700 Subject: [PATCH 213/280] Vibrate only the controller that hits the key and decrease timeout so that the xylophone can be played faster --- .../marketplace/xylophone/xylophoneKey.js | 22 +++++++++++-------- .../marketplace/xylophone/xylophoneRezzer.js | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 5cff66e557..3d131252c8 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,12 +9,12 @@ // (function() { - var TIMEOUT = 150; + var TIMEOUT = 50; // at 30 ms, the key's color sometimes fails to switch when hit var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var IS_DEBUG = false; var _this; - + function XylophoneKey() { _this = this; } @@ -34,24 +34,28 @@ collisionWithEntity: function(thisEntity, otherEntity, collision) { if (collision.type === 0) { - _this.hit(); + _this.hit(otherEntity); } }, - clickDownOnEntity: function() { - _this.hit(); + clickDownOnEntity: function(otherEntity) { + _this.hit(otherEntity); }, - hit: function() { + hit: function(otherEntity) { if (!_this.isWaiting) { _this.isWaiting = true; _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; - var HAPTIC_DURATION = 20; - var HAPTIC_HAND = 2; // Both hands - Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); + var HAPTIC_DURATION = 20; + var userData = JSON.parse(Entities.getEntityProperties(otherEntity, 'userData').userData); + if (userData.hasOwnProperty('hand')){ + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, userData.hand); + } + _this.timeout(); } }, diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index e99341bb19..6adf8710a7 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -11,6 +11,7 @@ var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var MALLET_SCRIPT_URL = Script.resolvePath("xylophoneMallet.js"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); @@ -91,6 +92,7 @@ function rezMallets() { damping: 1, angularDamping: 1, shapeType: "compound", + script: MALLET_SCRIPT_URL, userData: JSON.stringify({ grabbableKey: { invertSolidWhileHeld: true From e30e0b1a5f43f92c6ef0cc7e6117da209a6f3812 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 31 Jul 2017 16:45:21 +0200 Subject: [PATCH 214/280] Make sure Tablet will be shown with opened running scripts dialog. Fixed potential stack corruption --- .../resources/qml/hifi/tablet/TabletRoot.qml | 8 ++++- .../ui/src/ui/TabletScriptingInterface.cpp | 35 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index faa4013bce..c7df6ac64f 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -202,5 +202,11 @@ Item { width: 480 height: 706 - function setShown(value) {} + function setShown(value) { + if (value === true) { + HMD.openTablet() + } else { + HMD.closeTablet() + } + } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 9ed87ea337..74150a5a4b 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -245,20 +245,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); - - //connect to Tablet shown signal to open running scripts - if (_showRunningScripts) { - QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); - //qApp->toggleTabletUI(true); - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); - if (_tabletShown && _showRunningScripts) { - _showRunningScripts = false; - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); - } } @@ -384,9 +370,26 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { }); if (_initialScreen) { - pushOntoStack(_initialPath); + if (!_showRunningScripts) { + pushOntoStack(_initialPath); + } _initialScreen = false; } + + if (_showRunningScripts) { + _showRunningScripts = false; + //connect to Tablet shown signal to open running scripts + //we cant just call pushOnStack here since RunningScripts draws incorrectly + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); //not needed anymore, disconnect + if (_tabletShown) { + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + //show Tablet. Make sure, setShown implemented in TabletRoot.qml + QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); + } } else { removeButtonsFromHomeScreen(); _state = State::Uninitialized; @@ -523,7 +526,7 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } - return root; + return (root != nullptr); } void TabletProxy::popFromStack() { From ed9ea17917d2f2d49630d8a8329dab97ccbe0730 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 31 Jul 2017 08:02:18 -0700 Subject: [PATCH 215/280] remove semi-colon --- scripts/system/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 85de9fd0ac..fa997e20cc 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -654,7 +654,7 @@ var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; - }; + } dimensions = { x: width, From 60201548670ca5549691dbab2091b93e14e4042d Mon Sep 17 00:00:00 2001 From: Menithal Date: Mon, 31 Jul 2017 22:49:11 +0300 Subject: [PATCH 216/280] Removed some whitespaces to trigger new build --- scripts/system/edit.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 300db5d50a..b5e54e7a57 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -26,11 +26,8 @@ Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", "libraries/progressDialog.js", - "libraries/entitySelectionTool.js", - "libraries/ToolTip.js", - "libraries/entityCameraTool.js", "libraries/gridTool.js", "libraries/entityList.js", From 43c8079f0383c8f92ca44d1d21773d87a6c14221 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 13:55:43 -0700 Subject: [PATCH 217/280] temporary inspectionMode setting to turn ContextOverlays completely off/on --- .../src/ui/overlays/ContextOverlayInterface.cpp | 15 +++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..0235a8d4f4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -34,6 +34,11 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; + // initially, set _enabled to match the switch. Later we enable/disable via the getter/setters + // if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all + // the time after getting edge highlighting, etc... + _enabled = _settingSwitch.get(); + auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { @@ -66,6 +71,16 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; +void ContextOverlayInterface::setEnabled(bool enabled) { + // only enable/disable if the setting in 'on'. If it is 'off', + // make sure _enabled is always false. + if (_settingSwitch.get()) { + _enabled = enabled; + } else { + _enabled = false; + } +} + bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..1ec3d3aefd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } - void setEnabled(bool enabled) { _enabled = enabled; } + void setEnabled(bool enabled); bool getEnabled() { return _enabled; } public slots: @@ -73,6 +73,8 @@ private: bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); + + Setting::Handle _settingSwitch { "inspectionMode", false }; }; #endif // hifi_ContextOverlayInterface_h From 0b79809f547c9ec90c3b611a02972feabdaa3584 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Mon, 31 Jul 2017 17:08:30 -0400 Subject: [PATCH 218/280] [WL21389] PR1 Feedback: Small change missed last commit (details below). Until they're properly implemented, default the hedrons and polygons to SHAPE_TYPE_ELLIPSOID within RenderableShapeEntity::computeShapeInfo. Changes committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp --- libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c853335915..3f9497741f 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -132,7 +132,7 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Icosahedron: case entity::Shape::Cone: { //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } break; case entity::Shape::Torus: From b371c875c47bf53a24857064c2dc5c3907fdbbc6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 14:46:26 -0700 Subject: [PATCH 219/280] undo tab mapping to bring up tablet, for now --- interface/resources/controllers/keyboardMouse.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index e3d2132e01..f377e02e5f 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -15,7 +15,7 @@ { "comment" : "Mouse turn need to be small continuous increments", "from": { "makeAxis" : [ [ "Keyboard.MouseMoveLeft" ], - [ "Keyboard.MouseMoveRight" ] + [ "Keyboard.MouseMoveRight" ] ] }, "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], @@ -31,8 +31,8 @@ { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", "from": { "makeAxis" : [ [ "Keyboard.TouchpadLeft" ], - [ "Keyboard.TouchpadRight" ] - ] + [ "Keyboard.TouchpadRight" ] + ] }, "when": [ "Application.InHMD", "Application.SnapTurn" ], "to": "Actions.StepYaw", @@ -131,6 +131,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } + { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } ] } From de97dfc9d74309f1ea34c5f7eb0e1e721dbcd26c Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 15:25:02 -0700 Subject: [PATCH 220/280] install the bundled vcredist_x64 instead of relying on DLLs being setup by cmake directly in the install folder --- cmake/templates/NSIS.template.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 5417220ef1..de63e164e6 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,9 +853,11 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" + ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - Delete "$INSTDIR\vcredist_x64.exe" + ;Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From 46966b11321254fd96e5b14d0a052f85b68e0857 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:19:34 -0700 Subject: [PATCH 221/280] switching from LZMA (default) to BZIP2 for NSIS compression --- cmake/macros/GenerateInstallers.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..0296ad9901 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,6 +22,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From ae31df426c32094f780abecbed1c09ec70e9f648 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:52:38 -0700 Subject: [PATCH 222/280] remove vcredist_x64 after installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index de63e164e6..2445933b52 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -857,7 +857,7 @@ Section "-Core installation" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - ;Delete "$INSTDIR\vcredist_x64.exe" + Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From 6855dcbacf253d630e15c1d82340434da1a4c9e0 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 17:17:11 -0700 Subject: [PATCH 223/280] use bzip2 compression only for PR builds (so that we internally benefit from faster builds) --- cmake/macros/GenerateInstallers.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0296ad9901..aa681e27b4 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,7 +22,9 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) - set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + if (PR_BUILD) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + endif () set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From bea91be521c8668333cf3778f04599c0c55af18a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 12:27:12 -0700 Subject: [PATCH 224/280] Fix for potential crash in JS console --- interface/src/ui/JSConsole.cpp | 69 ++++++++++++++++-------------- interface/src/ui/JSConsole.h | 10 ++--- interface/src/ui/TestingDialog.cpp | 8 ++-- interface/src/ui/TestingDialog.h | 2 +- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index a62fb2270b..8c1ff87d13 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "JSConsole.h" + #include #include #include @@ -20,7 +22,6 @@ #include #include "Application.h" -#include "JSConsole.h" #include "ScriptHighlighting.h" const int NO_CURRENT_HISTORY_COMMAND = -1; @@ -60,14 +61,12 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } -JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : +JSConsole::JSConsole(QWidget* parent, const QSharedPointer& scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), - _commandHistory(_readLines(_savedHistoryFilename)), - _ownScriptEngine(scriptEngine == NULL), - _scriptEngine(NULL) { + _commandHistory(_readLines(_savedHistoryFilename)) { _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -90,36 +89,37 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : } JSConsole::~JSConsole() { - disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); + if (_scriptEngine) { + disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + _scriptEngine.reset(); } delete _ui; } -void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { +void JSConsole::setScriptEngine(const QSharedPointer& scriptEngine) { if (_scriptEngine == scriptEngine && scriptEngine != NULL) { return; } if (_scriptEngine != NULL) { - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); - } + disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + _scriptEngine.reset(); } // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine - _ownScriptEngine = (scriptEngine == NULL); - _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; + if (scriptEngine.isNull()) { + _scriptEngine = QSharedPointer(DependencyManager::get()->loadScript(_consoleFileName, false), &QObject::deleteLater); + } else { + _scriptEngine = scriptEngine; + } - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); + connect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + connect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); } void JSConsole::executeCommand(const QString& command) { @@ -135,19 +135,22 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QFuture future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command); + QWeakPointer weakScriptEngine = _scriptEngine; + auto consoleFileName = _consoleFileName; + QFuture future = QtConcurrent::run([weakScriptEngine, consoleFileName, command]()->QScriptValue{ + QScriptValue result; + auto scriptEngine = weakScriptEngine.lock(); + if (scriptEngine) { + BLOCKING_INVOKE_METHOD(scriptEngine.data(), "evaluate", + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, command), + Q_ARG(const QString&, consoleFileName)); + } + return result; + }); _executeWatcher.setFuture(future); } -QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { - QScriptValue result; - BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate", - Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, command), - Q_ARG(const QString&, _consoleFileName)); - return result; -} - void JSConsole::commandFinished() { QScriptValue result = _executeWatcher.result(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 59280f65aa..5010b5b9ca 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "ui_console.h" #include "ScriptEngine.h" @@ -29,10 +30,10 @@ const int CONSOLE_HEIGHT = 200; class JSConsole : public QWidget { Q_OBJECT public: - JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL); + JSConsole(QWidget* parent, const QSharedPointer& scriptEngine = QSharedPointer()); ~JSConsole(); - void setScriptEngine(ScriptEngine* scriptEngine = NULL); + void setScriptEngine(const QSharedPointer& scriptEngine = QSharedPointer()); void clear(); public slots: @@ -58,17 +59,14 @@ private: void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); - QScriptValue executeCommandInWatcher(const QString& command); QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QString _savedHistoryFilename; QList _commandHistory; - // Keeps track if the script engine is created inside the JSConsole - bool _ownScriptEngine; QString _rootCommand; - ScriptEngine* _scriptEngine; + QSharedPointer _scriptEngine; static const QString _consoleFileName; }; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index f55eb63a5b..bba14cd5d7 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -24,12 +24,12 @@ TestingDialog::TestingDialog(QWidget* parent) : _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); auto _engines = DependencyManager::get(); - _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _engine.reset(_engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath)); _console->setScriptEngine(_engine); - connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); + connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } void TestingDialog::onTestingFinished(const QString& scriptPath) { - _engine = nullptr; - _console->setScriptEngine(nullptr); + _engine.reset(); + _console->setScriptEngine(); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index b90b8f2e99..055e43eaf7 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -29,7 +29,7 @@ public: private: std::unique_ptr _console; - ScriptEngine* _engine; + QSharedPointer _engine; }; #endif From 9b28d546e037f5dc1a9b1a4cd729ea26f4deb73d Mon Sep 17 00:00:00 2001 From: "Scromie@turnmeupgames.com" Date: Tue, 1 Aug 2017 12:32:27 +0300 Subject: [PATCH 225/280] Test plan --- Test Plan 2.docx | Bin 0 -> 7305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Test Plan 2.docx diff --git a/Test Plan 2.docx b/Test Plan 2.docx new file mode 100644 index 0000000000000000000000000000000000000000..da60821b53e86ea9383e166da7f1d63dba588740 GIT binary patch literal 7305 zcmaKRWmp_ruhxxxyQt50eftRLb;_hGa=-*>-7q`i6obI~u09 zS^X?VV`Ygmjg1v#rM3{s0L((Ne{TIbVH89$@6 zt%i_E@gJCFYZ+(==A^6h_ℜHr^16?r6-Yt^lIBVxkg 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 I;wQ-e1EA-uc>n+a literal 0 HcmV?d00001 From f1d7ef76e7f28cce1bee01f47af6229903327bd9 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Tue, 1 Aug 2017 23:41:16 +0700 Subject: [PATCH 226/280] Add the mallet js file --- .../marketplace/xylophone/xylophoneMallet.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 unpublishedScripts/marketplace/xylophone/xylophoneMallet.js diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js new file mode 100644 index 0000000000..061d1303bb --- /dev/null +++ b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js @@ -0,0 +1,25 @@ +// +// xylophoneMallet.js +// +// Created by Johnathan Franck on 07/30/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +(function() { + function XylophoneMallet() { + } + + XylophoneMallet.prototype = { + startEquip: function(entityID, args) { + var LEFT_HAND = 0; + var RIGHT_HAND = 1; + var userData = JSON.parse(Entities.getEntityProperties(entityID, 'userData').userData); + userData.hand = args[0] === "left" ? LEFT_HAND : RIGHT_HAND; + Entities.editEntity(entityID, {userData: JSON.stringify(userData)}); + } + }; + + return new XylophoneMallet(); +}); From 3a9b8c02a6532f4c414897bd8d28ee5d271db482 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 1 Aug 2017 13:41:28 -0400 Subject: [PATCH 227/280] * disable Avatar "follow" / recentering behavior when using HMD + "View > Mirror" * restore access to "View > Mirror" from HMD mode --- interface/src/Application.cpp | 6 ++---- interface/src/avatar/MyAvatar.cpp | 3 ++- scripts/system/hmd.js | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 94d9d6e885..69359660af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4483,11 +4483,9 @@ void Application::cameraModeChanged() { void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { - if (isHMDMode()) { - menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - } else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { + if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); + getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers } } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 490f22ed8b..160235d77e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2849,7 +2849,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - if (myAvatar.getHMDLeanRecenterEnabled()) { + if (myAvatar.getHMDLeanRecenterEnabled() && + qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); } diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c545e6bcee..3598b461a4 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -40,9 +40,8 @@ function updateControllerDisplay() { var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. -// Disable them in hmd. -var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; +// Independent and Entity mode make people sick; disable them in hmd. +var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { HMD.closeTablet(); From c22e08f3e8ac3e4a550f0d7f7e4316bd090109dc Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 19:58:23 +0100 Subject: [PATCH 228/280] loacked script engine in EntityEditPacketSender --- libraries/entities/src/EntityEditPacketSender.cpp | 1 + libraries/entities/src/EntityEditPacketSender.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 7845b0d5e3..ee0fcf8218 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -54,6 +54,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, EntityItemProperties entityProperties = entity->getProperties(); entityProperties.merge(properties); + std::lock_guard lock(_mutex); QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); QVariant variantProperties = scriptProperties.toVariant(); QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 9190a8296a..81efaa865c 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -14,6 +14,8 @@ #include +#include + #include "EntityItem.h" #include "AvatarData.h" @@ -49,6 +51,7 @@ private: EntityItemID entityItemID, const EntityItemProperties& properties); private: + std::mutex _mutex; AvatarData* _myAvatar { nullptr }; QScriptEngine _scriptEngine; }; From 25b5cb4762a0969609bfcc9ce474edabaf4f1d02 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Aug 2017 13:17:33 -0700 Subject: [PATCH 229/280] Switch order of precondition and setSecondaryTargets --- libraries/animation/src/AnimInverseKinematics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 74fbf00067..d17fbebf3a 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -1056,8 +1056,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars { PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0); - preconditionRelativePosesToAvoidLimbLock(context, targets); setSecondaryTargets(context); + preconditionRelativePosesToAvoidLimbLock(context, targets); + solve(context, targets, dt, jointChainInfoVec); } @@ -1610,7 +1611,7 @@ void AnimInverseKinematics::debugDrawRelativePoses(const AnimContext& context) c const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f); const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f); const vec4 GRAY(0.2f, 0.2f, 0.2f, 1.0f); - const float AXIS_LENGTH = 2.0f; // cm + const float AXIS_LENGTH = 10.0f; // cm // draw each pose for (int i = 0; i < (int)poses.size(); i++) { From cca29e849b50a8b982144439b3ca46d2cb2f73d9 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 23:18:42 +0100 Subject: [PATCH 230/280] fix issue of tablet begin blank when switching between HMD and Desktop mode --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 43c460fd23..e7b736d232 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -236,7 +236,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _desktopWindow = nullptr; } } - loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } From 762b4098143b4fa68cf10e16e232ca5229fe6a7f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:20:33 -0700 Subject: [PATCH 231/280] unused variable --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cbe184f31e..b0fc826f0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -142,7 +142,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } From 588afaaa85861ea2f0ca70446cbc0d510d70c6a6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:59:42 -0700 Subject: [PATCH 232/280] unused variable... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index b0fc826f0a..e406d139d0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -57,7 +57,6 @@ ContextOverlayInterface::ContextOverlayInterface() { }); } -static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters From 1b6feb63c8d901d484dfac389fbf7bb4d1ff46b5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Aug 2017 18:40:43 -0700 Subject: [PATCH 233/280] Fix for crash when attempting to teleport while avatar was loading The fix had two parts. * Make Avatar::getAbsoluteDefaultJointXXXInObjectFrame thread safe * Make teleport.js handle a zero foot offset more gracefully, to prevent the avatar from teleporting into the floor. --- .../src/avatars-renderer/Avatar.cpp | 33 ++++++++++++++----- scripts/system/controllers/teleport.js | 8 ++++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 06814c2707..d775fe05f0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -899,17 +899,34 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { } glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const { - glm::quat rotation; - glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot(); - return Quaternions::Y_180 * rot; + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + auto skeleton = model->getRig().getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // The rotation part of the geometry-to-rig transform is always identity so we can skip it. + // Y_180 is to convert from rig-frame into avatar-frame + return Quaternions::Y_180 * skeleton->getAbsoluteDefaultPose(index).rot(); + } + } + return Quaternions::Y_180; } glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const { - glm::vec3 translation; - const Rig& rig = _skeletonModel->getRig(); - glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); - glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans); + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + const Rig& rig = model->getRig(); + auto skeleton = rig.getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // trans is in geometry frame. + glm::vec3 trans = skeleton->getAbsoluteDefaultPose(index).trans(); + // Y_180 is to convert from rig-frame into avatar-frame + glm::mat4 geomToAvatarMat = Matrices::Y_180 * rig.getGeometryToRigTransform(); + return transformPoint(geomToAvatarMat, trans); + } + } + return Vectors::ZERO; } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index b058ec670f..15ba314a1d 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -381,7 +381,13 @@ function getAvatarFootOffset() { } if (footJointIndex != -1) { // default vertical offset from foot to avatar root. - return -MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex).y; + var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); + if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { + // if footPos is exactly zero, it's probably wrong because avatar is currently loading, fall back to default. + return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; + } else { + return -footPos.y; + } } else { return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; } From 4330e312122448354b9f892ec5e306812d54fbf4 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 16:32:09 +0200 Subject: [PATCH 234/280] Toolbar mode: 1. Fixed switching between different apps. 2. Fixed switching from Create --- interface/resources/qml/hifi/audio/Audio.qml | 2 +- libraries/ui/src/ui/TabletScriptingInterface.cpp | 13 ++++++------- scripts/system/edit.js | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 03d27e3831..93742e39a5 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -33,7 +33,7 @@ Rectangle { // only show the title if loaded through a "loader" function showTitle() { - return root.parent.objectName == "loader"; + return (root.parent !== null) && root.parent.objectName == "loader"; } Column { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..2bdad096f4 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -237,7 +237,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } } loadHomeScreen(true); - emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { @@ -463,14 +462,14 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } if (root) { - if (_state != State::QML) { - removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); - _state = State::QML; + removeButtonsFromHomeScreen(); //works only in Tablet + QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); + _state = State::QML; + if (path != _currentPathLoaded) { emit screenChanged(QVariant("QML"), path); - _currentPathLoaded = path; - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } + _currentPathLoaded = path; + QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } else { qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..046cd2e66a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -419,7 +419,7 @@ var toolBar = (function () { createButton = activeButton; tablet.screenChanged.connect(function (type, url) { if (isActive && (type !== "QML" || url !== "Edit.qml")) { - that.toggle(); + that.setActive(false) } }); tablet.fromQml.connect(fromQml); From 05f1caab01b8fa1a0e872c92898559a81c0148b4 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Aug 2017 16:35:58 +0100 Subject: [PATCH 235/280] better solution to help and blank tablet issues --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index e7b736d232..603b85c160 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -228,7 +228,12 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); } else { removeButtonsFromToolbar(); - addButtonsToHomeScreen(); + + if (_currentPathLoaded == TABLET_SOURCE_URL) { + addButtonsToHomeScreen(); + } else { + loadHomeScreen(true); + } // destroy desktop window if (_desktopWindow) { From 7d361c69c2493e6198c19a5fe750be87f60d3e4d Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 19:52:47 +0200 Subject: [PATCH 236/280] Audio mic bar show only rectangle on mouse over --- interface/resources/qml/hifi/audio/MicBar.qml | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 10e12551b7..e757d7cadf 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -27,7 +27,7 @@ Rectangle { color: "#00000000"; border { - width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0; + width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0; color: colors.border; } @@ -61,7 +61,7 @@ Rectangle { drag.target: dragTarget; } - Item { + QtObject { id: colors; readonly property string unmuted: "#FFF"; @@ -72,7 +72,7 @@ Rectangle { readonly property string red: colors.muted; readonly property string fill: "#55000000"; readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; - readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted; + readonly property string icon: Audio.muted ? muted : unmuted; } Item { @@ -92,10 +92,8 @@ Rectangle { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; - function exclusiveOr(a, b) { return (a || b) && !(a && b); } - id: image; - source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon; + source: Audio.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -118,9 +116,9 @@ Rectangle { Item { id: status; - readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted; + readonly property string color: Audio.muted ? colors.muted : colors.unmuted; - visible: Audio.muted || mouseArea.containsMouse; + visible: Audio.muted; anchors { left: parent.left; @@ -133,14 +131,14 @@ Rectangle { Text { anchors { - horizontalCenter: parent.horizontalCenter; + horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter; } color: parent.color; - text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE"; - font.pointSize: 12; + text: Audio.muted ? "MUTED" : "MUTE"; + font.pointSize: 12; } Rectangle { @@ -150,7 +148,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } @@ -161,7 +159,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } } From 106b0ad8bb7e1118d6abd2f0f7aa747588fedc3e Mon Sep 17 00:00:00 2001 From: seefo Date: Mon, 31 Jul 2017 14:37:40 -0700 Subject: [PATCH 237/280] Added Vive pucks as an input channel --- interface/resources/controllers/standard.json | 19 ++++++++++++++++++- interface/resources/controllers/vive.json | 19 ++++++++++++++++++- .../controllers/src/controllers/Actions.cpp | 17 +++++++++++++++++ .../controllers/src/controllers/Actions.h | 17 +++++++++++++++++ .../src/controllers/StandardController.cpp | 17 +++++++++++++++++ scripts/developer/tests/puck-attach.js | 6 +++--- scripts/developer/tests/viveTrackedObjects.js | 2 +- 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 166f1a6869..0d5c095585 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -109,6 +109,23 @@ { "from": "Standard.Head", "to": "Actions.Head" }, { "from": "Standard.LeftArm", "to": "Actions.LeftArm" }, - { "from": "Standard.RightArm", "to": "Actions.RightArm" } + { "from": "Standard.RightArm", "to": "Actions.RightArm" }, + + { "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" }, + { "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" }, + { "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" }, + { "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" }, + { "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" }, + { "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" }, + { "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" }, + { "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" }, + { "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" }, + { "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" }, + { "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" }, + { "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" }, + { "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" }, + { "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" }, + { "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" }, + { "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" } ] } diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 73ab5cb2ae..02fc09c815 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -77,6 +77,23 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, + + { "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" }, + { "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" }, + { "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" }, + { "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" }, + { "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" }, + { "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" }, + { "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" }, + { "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" }, + { "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" }, + { "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" }, + { "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" }, + { "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" }, + { "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" }, + { "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" }, + { "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" }, + { "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" } ] } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index b3c0ed3f05..d8dd7f5e35 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -101,6 +101,23 @@ namespace controller { makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"), makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"), + makePosePair(Action::TRACKED_OBJECT_00, "TrackedObject00"), + makePosePair(Action::TRACKED_OBJECT_01, "TrackedObject01"), + makePosePair(Action::TRACKED_OBJECT_02, "TrackedObject02"), + makePosePair(Action::TRACKED_OBJECT_03, "TrackedObject03"), + makePosePair(Action::TRACKED_OBJECT_04, "TrackedObject04"), + makePosePair(Action::TRACKED_OBJECT_05, "TrackedObject05"), + makePosePair(Action::TRACKED_OBJECT_06, "TrackedObject06"), + makePosePair(Action::TRACKED_OBJECT_07, "TrackedObject07"), + makePosePair(Action::TRACKED_OBJECT_08, "TrackedObject08"), + makePosePair(Action::TRACKED_OBJECT_09, "TrackedObject09"), + makePosePair(Action::TRACKED_OBJECT_10, "TrackedObject10"), + makePosePair(Action::TRACKED_OBJECT_11, "TrackedObject11"), + makePosePair(Action::TRACKED_OBJECT_12, "TrackedObject12"), + makePosePair(Action::TRACKED_OBJECT_13, "TrackedObject13"), + makePosePair(Action::TRACKED_OBJECT_14, "TrackedObject14"), + makePosePair(Action::TRACKED_OBJECT_15, "TrackedObject15"), + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index ec4800c9aa..6319b5746e 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -146,6 +146,23 @@ enum class Action { RIGHT_HAND_PINKY3, RIGHT_HAND_PINKY4, + TRACKED_OBJECT_00, + TRACKED_OBJECT_01, + TRACKED_OBJECT_02, + TRACKED_OBJECT_03, + TRACKED_OBJECT_04, + TRACKED_OBJECT_05, + TRACKED_OBJECT_06, + TRACKED_OBJECT_07, + TRACKED_OBJECT_08, + TRACKED_OBJECT_09, + TRACKED_OBJECT_10, + TRACKED_OBJECT_11, + TRACKED_OBJECT_12, + TRACKED_OBJECT_13, + TRACKED_OBJECT_14, + TRACKED_OBJECT_15, + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 40b87bc6b2..ed729867df 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -166,6 +166,23 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(DD, "Down"), makePair(DL, "Left"), makePair(DR, "Right"), + + makePair(TRACKED_OBJECT_00, "TrackedObject00"), + makePair(TRACKED_OBJECT_01, "TrackedObject01"), + makePair(TRACKED_OBJECT_02, "TrackedObject02"), + makePair(TRACKED_OBJECT_03, "TrackedObject03"), + makePair(TRACKED_OBJECT_04, "TrackedObject04"), + makePair(TRACKED_OBJECT_05, "TrackedObject05"), + makePair(TRACKED_OBJECT_06, "TrackedObject06"), + makePair(TRACKED_OBJECT_07, "TrackedObject07"), + makePair(TRACKED_OBJECT_08, "TrackedObject08"), + makePair(TRACKED_OBJECT_09, "TrackedObject09"), + makePair(TRACKED_OBJECT_10, "TrackedObject10"), + makePair(TRACKED_OBJECT_11, "TrackedObject11"), + makePair(TRACKED_OBJECT_12, "TrackedObject12"), + makePair(TRACKED_OBJECT_13, "TrackedObject13"), + makePair(TRACKED_OBJECT_14, "TrackedObject14"), + makePair(TRACKED_OBJECT_15, "TrackedObject15") }; return availableInputs; } diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 2d0a2e6d02..dd94f81a99 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -84,7 +84,7 @@ function getAvailableTrackedObjects() { var i; for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { var key = indexToTrackedObjectName(i); - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose && pose.valid) { available.push(i); } @@ -126,8 +126,8 @@ function pad(num, size) { } function update() { - if (attachedEntity && attachedObj && Controller.Hardware.Vive) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[attachedObj.key]); + if (attachedEntity && attachedObj && Controller.Standard) { + var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); diff --git a/scripts/developer/tests/viveTrackedObjects.js b/scripts/developer/tests/viveTrackedObjects.js index 4155afb82b..1d60f658d9 100644 --- a/scripts/developer/tests/viveTrackedObjects.js +++ b/scripts/developer/tests/viveTrackedObjects.js @@ -23,7 +23,7 @@ var BLUE = {x: 0, y: 0, z: 1, w: 1}; function update(dt) { if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose.valid) { DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); } else { From 620f1a65c0f4cfe1519e008749a9a18c4c6d32c0 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 12:29:30 -0700 Subject: [PATCH 238/280] New puck-attach script --- scripts/developer/tests/puck-attach.js | 205 +++++++++++++++---------- 1 file changed, 121 insertions(+), 84 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index dd94f81a99..25aeee54eb 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -1,21 +1,13 @@ // // Created by Anthony J. Thibault on 2017/06/20 +// Modified by Robbie Uvanni to support multiple pucks and easier placement of pucks on entities, on 2017/08/01 // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // When this script is running, a new app button, named "PUCKTACH", will be added to the toolbar/tablet. -// Click this app to bring up the puck attachment panel. This panel contains the following fields. -// -// * Tracked Object - A drop down list of all the available pucks found. If no pucks are found this list will only have a single NONE entry. -// Closing and re-opening the app will refresh this list. -// * Model URL - A model url of the model you wish to be attached to the specified puck. -// * Position X, Y, Z - used to apply an offset between the puck and the attached model. -// * Rot X, Y, Z - used to apply euler angle offsets, in degrees, between the puck and the attached model. -// * Create Attachment - when this button is pressed a new Entity will be created at the specified puck's location. -// If a puck atttachment already exists, it will be destroyed before the new entity is created. -// * Destroy Attachmetn - destroies the entity attached to the puck. +// Click this app to bring up the puck attachment panel. // /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -25,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var HTML_URL = "https://s3.amazonaws.com/hifi-public/tony/html/puck-attach.html"; +var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -34,20 +26,9 @@ var tabletButton = tablet.addButton({ activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/puck-a.svg" }); -tabletButton.clicked.connect(function () { - if (shown) { - tablet.gotoHomeScreen(); - } else { - tablet.gotoWebScreen(HTML_URL); - } -}); - var shown = false; -var attachedEntity; -var attachedObj; - function onScreenChanged(type, url) { - if (type === "Web" && url === HTML_URL) { + if (type === "Web" && url === TABLET_APP_URL) { tabletButton.editProperties({isActive: true}); if (!shown) { // hook up to event bridge @@ -57,9 +38,7 @@ function onScreenChanged(type, url) { // send available tracked objects to the html running in the tablet. var availableTrackedObjects = getAvailableTrackedObjects(); tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - - // print("PUCK-ATTACH: availableTrackedObjects = " + JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. + }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -71,13 +50,15 @@ function onScreenChanged(type, url) { shown = false; } } - tablet.screenChanged.connect(onScreenChanged); +function pad(num, size) { + var tempString = "000000000" + num; + return tempString.substr(tempString.length - size); +} function indexToTrackedObjectName(index) { return "TrackedObject" + pad(index, 2); } - function getAvailableTrackedObjects() { var available = []; var NUM_TRACKED_OBJECTS = 16; @@ -92,83 +73,139 @@ function getAvailableTrackedObjects() { return available; } -function attach(obj) { - attachedEntity = Entities.addEntity({ - type: "Model", - name: "puck-attach-entity", - modelURL: obj.modelurl - }); - attachedObj = obj; - var localPos = {x: Number(obj.posx), y: Number(obj.posy), z: Number(obj.posz)}; - var localRot = Quat.fromVec3Degrees({x: Number(obj.rotx), y: Number(obj.roty), z: Number(obj.rotz)}); - attachedObj.localXform = new Xform(localRot, localPos); - var key = indexToTrackedObjectName(Number(attachedObj.puckno)); - attachedObj.key = key; - - // print("PUCK-ATTACH: attachedObj = " + JSON.stringify(attachedObj)); - - Script.update.connect(update); - update(); +function getRelativePosition(origin, rotation, offset) { + var relativeOffset = Vec3.multiplyQbyV(rotation, offset); + var worldPosition = Vec3.sum(origin, relativeOffset); + return worldPosition; +} +function getPropertyForEntity(entityID, propertyName) { + return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } -function remove() { - if (attachedEntity) { - Script.update.disconnect(update); - Entities.deleteEntity(attachedEntity); - attachedEntity = undefined; - } - attachedObj = undefined; -} +var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_NAME = "Tracked Puck"; -function pad(num, size) { - var tempString = "000000000" + num; - return tempString.substr(tempString.length - size); -} +var trackedPucks = { }; +var lastPuck = { }; -function update() { - if (attachedEntity && attachedObj && Controller.Standard) { - var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); - if (pose && pose.valid) { - Entities.editEntity(attachedEntity, { - position: finalXform.pos, - rotation: finalXform.rot - }); - } else { - if (pose) { - print("PUCK-ATTACH: WARNING: invalid pose for " + attachedObj.key); - } else { - print("PUCK-ATTACH: WARNING: could not find key " + attachedObj.key); +function createPuck(puck) { + // create a puck entity and add it to our list of pucks + var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + "name": "Tracked Puck", + "type": "Model", + "modelURL": VIVE_PUCK_MODEL, + "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, + "position": spawnPosition, + "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + trackedPucks[puck.puckno] = { + "puckEntityID": puckEntityID, + "trackedEntityID": "" + }; + lastPuck = trackedPucks[puck.puckno]; +} +function finalizePuck() { + // find nearest entity and change its parent to the puck + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); + var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); + + var foundEntity; + var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + + for (var i = 0; i < foundEntities.length; i++) { + var entity = foundEntities[i]; + + if (getPropertyForEntity(entity, "name") !== VIVE_PUCK_NAME) { + var entityPosition = getPropertyForEntity(entity, "position"); + var d = Vec3.distance(entityPosition, puckPosition); + + if (d < leastDistance) { + leastDistance = d; + foundEntity = entity; } } } + + if (foundEntity) { + lastPuck.trackedEntityID = foundEntity; + Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + } +} +function updatePucks() { + // for each puck, update its position and orientation + for (var puck in trackedPucks) { + var action = indexToTrackedObjectName(puck); + var pose = Controller.getPoseValue(Controller.Standard[action]); + if (pose && pose.valid) { + if (trackedPucks[puck].trackedEntityID) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + + Entities.editEntity(trackedPucks[puck].puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + } + } + } +} +function destroyPuck(puckName) { + // unparent entity and delete its parent + var puckEntityID = trackedPucks[puckName].puckEntityID; + var trackedEntityID = trackedPucks[puckName].trackedEntityID; + + Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); + Entities.deleteEntity(puckEntityID); +} +function destroyPucks() { + // remove all pucks and unparent entities + for (var puck in trackedPucks) { + destroyPuck(puck); + } } function onWebEventReceived(msg) { var obj = {}; - try { + + try { obj = JSON.parse(msg); - } catch (err) { - return; + } catch (err) { + return; } - if (obj.cmd === "attach") { - remove(); - attach(obj); - } else if (obj.cmd === "detach") { - remove(); + + switch (obj.cmd) { + case "create": + createPuck(obj); + break; + case "finalize": + finalizePuck(); + break; } } +Script.update.connect(updatePucks); Script.scriptEnding.connect(function () { - remove(); tablet.removeButton(tabletButton); + destroyPucks(); if (shown) { tablet.webEventReceived.disconnect(onWebEventReceived); tablet.gotoHomeScreen(); } tablet.screenChanged.disconnect(onScreenChanged); }); - -}()); // END LOCAL_SCOPE +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(TABLET_APP_URL); + } +}); +}()); // END LOCAL_SCOPE \ No newline at end of file From 961f748d8761332ba2939937b70cd6cac91d500b Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 13:08:24 -0700 Subject: [PATCH 239/280] Fixed a potential bug with puck-attach parenting --- scripts/developer/tests/puck-attach.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 25aeee54eb..3a5d8736f8 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -140,18 +140,25 @@ function finalizePuck() { } function updatePucks() { // for each puck, update its position and orientation - for (var puck in trackedPucks) { - var action = indexToTrackedObjectName(puck); + for (var puckName in trackedPucks) { + var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { - if (trackedPucks[puck].trackedEntityID) { + var puck = trackedPucks[puckName]; + if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); - Entities.editEntity(trackedPucks[puck].puckEntityID, { + Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, - rotation: finalXform.rot + rotation: finalXform.rot, + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID }); } } From 5a7a6bab7049b899b9b011fbb1bfda7f0a23639a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Aug 2017 13:57:27 -0700 Subject: [PATCH 240/280] Remove PDB files, eliminate sixense --- cmake/macros/GenerateInstallers.cmake | 18 +++----- cmake/macros/InstallBesideConsole.cmake | 5 ++- cmake/templates/FixupBundlePostBuild.cmake.in | 43 ++++++++----------- interface/CMakeLists.txt | 5 ++- plugins/hifiSixense/CMakeLists.txt | 14 +++--- 5 files changed, 40 insertions(+), 45 deletions(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..75190d044a 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -24,21 +24,12 @@ macro(GENERATE_INSTALLERS) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) + if (WIN32) - # include CMake module that will install compiler system libraries - # so that we have msvcr120 and msvcp120 installed with targets - set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) - - # as long as we're including sixense plugin with installer - # we need re-distributables for VS 2011 as well - # this should be removed if/when sixense support is pulled - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll" - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll" - ) - + # Do not install the Visual Studio C runtime libraries. The installer will do this automatically + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include(InstallRequiredSystemLibraries) - set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") # install and reference the Add/Remove icon @@ -90,3 +81,4 @@ macro(GENERATE_INSTALLERS) include(CPack) endmacro() + diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index d5777fff12..3c991acf86 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -22,9 +22,12 @@ macro(install_beside_console) else () # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${COMPONENT_INSTALL_DIR} COMPONENT ${SERVER_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) # on windows for PR and production builds, sign the executable diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 57d1fd787f..d4726884c2 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -11,34 +11,28 @@ include(BundleUtilities) -# replace copy_resolved_item_into_bundle -# -# The official version of copy_resolved_item_into_bundle will print out a "warning:" when -# the resolved item matches the resolved embedded item. This not not really an issue that -# should rise to the level of a "warning" so we replace this message with a "status:" -# -function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) - if (WIN32) - # ignore case on Windows - string(TOLOWER "${resolved_item}" resolved_item_compare) - string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare) - else() - set(resolved_item_compare "${resolved_item}") - set(resolved_embedded_item_compare "${resolved_embedded_item}") +function(gp_resolved_file_type_override resolved_file type_var) + if( file MATCHES ".*VCRUNTIME140.*" ) + set(type "system" PARENT_SCOPE) endif() - - if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") - # this is our only change from the original version - message(STATUS "status: resolved_item == resolved_embedded_item - not copying...") - else() - #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") - if(UNIX AND NOT APPLE) - file(RPATH_REMOVE FILE "${resolved_embedded_item}") - endif() + if( file MATCHES ".*concrt140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*msvcp140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*vcruntime140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-crt-conio.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-core-winrt.*" ) + set(type "system" PARENT_SCOPE) endif() endfunction() + message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}") @@ -52,3 +46,4 @@ endif() file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c97975e50a..81c8a44baf 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -309,9 +309,12 @@ else (APPLE) # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${INTERFACE_INSTALL_DIR} COMPONENT ${CLIENT_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}") diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt index 14676217db..58aeb6c88f 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -6,9 +6,11 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (NOT ANDROID) - set(TARGET_NAME hifiSixense) - setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) - target_sixense() -endif () +# FIXME if we want to re-enable this, we need to fix the mechanism for installing the +# dependency dlls `msvcr100` and `msvcp100` WITHOUT using CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS +#if (NOT ANDROID) +# set(TARGET_NAME hifiSixense) +# setup_hifi_plugin(Script Qml Widgets) +# link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) +# target_sixense() +#endif () From 82654c7cda887b19bc673ecb51f9c07bcb8652e1 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 15:28:26 -0700 Subject: [PATCH 241/280] Made requested changed to puck-attach.js --- scripts/developer/tests/puck-attach.js | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 3a5d8736f8..65adf35d00 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -83,7 +83,9 @@ function getPropertyForEntity(entityID, propertyName) { } var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; @@ -91,23 +93,28 @@ var lastPuck = { }; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); // should be an overlay var puckEntityProperties = { - "name": "Tracked Puck", - "type": "Model", - "modelURL": VIVE_PUCK_MODEL, - "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, - "position": spawnPosition, - "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' }; var puckEntityID = Entities.addEntity(puckEntityProperties); + + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + trackedPucks[puck.puckno] = { - "puckEntityID": puckEntityID, - "trackedEntityID": "" + puckEntityID: puckEntityID, + trackedEntityID: "" }; lastPuck = trackedPucks[puck.puckno]; } @@ -117,7 +124,7 @@ function finalizePuck() { var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); var foundEntity; - var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + var leastDistance = Number.MAX_VALUE; for (var i = 0; i < foundEntities.length; i++) { var entity = foundEntities[i]; @@ -141,6 +148,9 @@ function finalizePuck() { function updatePucks() { // for each puck, update its position and orientation for (var puckName in trackedPucks) { + if (!trackedPucks.hasOwnProperty(puckName)) { + continue; + } var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { @@ -148,7 +158,7 @@ function updatePucks() { if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + var finalXform = Xform.mul(avatarXform, puckXform); Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, @@ -169,13 +179,16 @@ function destroyPuck(puckName) { var puckEntityID = trackedPucks[puckName].puckEntityID; var trackedEntityID = trackedPucks[puckName].trackedEntityID; + // remove the puck as a parent entity and then delete the puck Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); Entities.deleteEntity(puckEntityID); } function destroyPucks() { // remove all pucks and unparent entities - for (var puck in trackedPucks) { - destroyPuck(puck); + for (var puckName in trackedPucks) { + if (trackedPucks.hasOwnProperty(puckName)) { + destroyPuck(puckName); + } } } From 3c1607715ecaec1952d9aadfdfcf7d562b79f5f2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 2 Aug 2017 17:42:56 -0700 Subject: [PATCH 242/280] Updated WASAPI plugin --- 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 1bf195fc84..4437024962 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_wasapi8.zip - URL_MD5 b01510437ea15527156bc25cdf733bd9 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip + URL_MD5 94f4765bdbcd53cd099f349ae031e769 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 24718e242405f422e2fcfc1a64a55be1725611c6 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 09:03:23 -0400 Subject: [PATCH 243/280] WL21423 Camera mode on startup is determined by user settings and HMD status --- interface/src/Application.cpp | 44 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ceded99f40..8debcbd538 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4146,6 +4146,7 @@ void Application::loadSettings() { //DependencyManager::get()->setAutomaticLODAdjust(false); Menu::getInstance()->loadSettings(); + // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. auto pluginManager = PluginManager::getInstance(); auto plugins = pluginManager->getPreferredDisplayPlugins(); @@ -4159,24 +4160,44 @@ void Application::loadSettings() { break; } } - } else { + } + + Setting::Handle firstRun { Settings::firstRun, true }; + bool isFirstPerson = false; + if (firstRun.get()) { // If this is our first run, and no preferred devices were set, default to // an HMD device if available. - Setting::Handle firstRun { Settings::firstRun, true }; - if (firstRun.get()) { - auto displayPlugins = pluginManager->getDisplayPlugins(); - for (auto& plugin : displayPlugins) { - if (plugin->isHmd()) { - if (auto action = menu->getActionForOption(plugin->getName())) { - action->setChecked(true); - action->trigger(); - break; - } + auto displayPlugins = pluginManager->getDisplayPlugins(); + for (auto& plugin : displayPlugins) { + if (plugin->isHmd()) { + if (auto action = menu->getActionForOption(plugin->getName())) { + action->setChecked(true); + action->trigger(); + break; } } } + + isFirstPerson = (qApp->isHMDMode()); + } else { + // if this is not the first run, the camera will be initialized differently depending on user settings + + if (qApp->isHMDMode()) { + // if the HMD is active, use first-person camera, unless the appropriate setting is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); + } else { + // if HMD is not active, only use first person if the menu option is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + } } + // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings + // dictated that we should be in first person + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + cameraMenuChanged(); + auto inputs = pluginManager->getInputPlugins(); for (auto plugin : inputs) { if (!plugin->isActive()) { @@ -4225,7 +4246,6 @@ void Application::init() { DependencyManager::get()->init(); DependencyManager::get()->init(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _timerStart.start(); _lastTimeUpdated.start(); From 4b7779f0f55381a1ea10f1b41eb6688ec7ecab69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 3 Aug 2017 07:57:21 -0700 Subject: [PATCH 244/280] add flag to outline render flags, fix pal.js/edit.js interaction --- interface/src/Application.cpp | 11 ++++++++--- .../src/RenderableModelEntityItem.cpp | 5 ++++- libraries/render/src/render/Args.h | 3 ++- scripts/system/pal.js | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 571f206cb5..d50348d9b7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1489,7 +1489,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["atp_mapping_requests"] = atpMappingRequests; properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; - + QJsonObject bytesDownloaded; bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt(); bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt(); @@ -5420,8 +5420,13 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; - if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + auto contextOverlayInterface = DependencyManager::get(); + if (contextOverlayInterface->getEnabled()) { + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE; + } else { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + } } renderArgs->_outlineFlags = renderOutlineFlags; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a03360a0e5..911fdf4184 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,7 +372,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + // this simple logic should say we set showingEntityHighlight to true whenever we are in marketplace mode and we have a marketplace id, or + // whenever we are not set to none and shouldHighlight is true. + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE) && getMarketplaceID().length() != 0) || + (args->_outlineFlags != RenderArgs::RENDER_OUTLINE_NONE && getShouldHighlight()); if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index f53454cf75..d5b5440c32 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -66,7 +66,8 @@ namespace render { enum OutlineFlags { RENDER_OUTLINE_NONE = 0, RENDER_OUTLINE_WIREFRAMES = 1, - RENDER_OUTLINE_SHADER = 2 + RENDER_OUTLINE_MARKETPLACE_MODE = 2, + RENDER_OUTLINE_SHADER = 4 }; enum DebugFlags { RENDER_DEBUG_NONE = 0, diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 58a54ef859..2a8a89ae7d 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -710,6 +710,7 @@ function off() { Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); tablet.tabletShownChanged.disconnect(tabletVisibilityChanged); isWired = false; + ContextOverlay.enabled = true } if (audioTimer) { Script.clearInterval(audioTimer); @@ -718,7 +719,6 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; - ContextOverlay.enabled = true; } function tabletVisibilityChanged() { From 6ea4ed80cd7a5ff0bd49aacd8ddb0790aed97e02 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 3 Aug 2017 09:19:08 -0700 Subject: [PATCH 245/280] Suppress initial PacketType::SilentAudioFrame on audio gate startup --- assignment-client/src/Agent.h | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index a549df5fb3..da60a07367 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -109,7 +109,7 @@ private: QHash _outgoingScriptAudioSequenceNumbers; AudioGate _audioGate; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; bool _isNoiseGateEnabled { false }; CodecPluginPointer _codec; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 31e36671c7..1c26b1bb08 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -364,7 +364,7 @@ private: AudioIOStats _stats; AudioGate* _audioGate { nullptr }; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; From 2279e174e6dd9b16564e7035a85c900576a9d6bf Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Thu, 3 Aug 2017 09:41:41 -0700 Subject: [PATCH 246/280] Switching to completely silent VC++ redistributable installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 2445933b52..5af51ff8c9 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,7 +853,7 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" - ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" From a439cc18372164e81290faeb325061ba7ab69afd Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 3 Aug 2017 20:58:44 +0200 Subject: [PATCH 247/280] Make connection to tablet shown signal more consistent, eliminates missed signals --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++----------- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 007754ed78..0fd32b42e6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -187,6 +187,7 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent) if (QThread::currentThread() != qApp->thread()) { qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name; } + connect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } TabletProxy::~TabletProxy() { @@ -194,6 +195,7 @@ TabletProxy::~TabletProxy() { if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; } + disconnect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } void TabletProxy::setToolbarMode(bool toolbarMode) { @@ -235,8 +237,7 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } else { loadHomeScreen(true); } - //check if running scripts opened and save it for reopen in Tablet - qDebug() << __FUNCTION__ << offscreenUi->isVisible("RunningScripts"); + //check if running scripts window opened and save it for reopen in Tablet if (offscreenUi->isVisible("RunningScripts")) { offscreenUi->hide("RunningScripts"); _showRunningScripts = true; @@ -322,6 +323,13 @@ void TabletProxy::emitWebEvent(const QVariant& msg) { emit webEventReceived(msg); } +void TabletProxy::onTabletShown() { + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } +} + bool TabletProxy::isPathLoaded(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; @@ -377,18 +385,7 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { _initialScreen = false; } - qDebug() << __FUNCTION__ << _showRunningScripts; if (_showRunningScripts) { - _showRunningScripts = false; - //connect to Tablet shown signal to open running scripts - //we cant just call pushOnStack here since RunningScripts draws incorrectly - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); //not needed anymore, disconnect - if (_tabletShown) { - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); //show Tablet. Make sure, setShown implemented in TabletRoot.qml QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); } @@ -491,7 +488,6 @@ void TabletProxy::loadQMLSource(const QVariant& path) { if (root) { removeButtonsFromHomeScreen(); //works only in Tablet - qDebug() << __FUNCTION__ << path << _currentPathLoaded << (int)_state; QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; if (path != _currentPathLoaded) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 82b7ad52b6..af38cb9351 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ protected slots: void addButtonsToHomeScreen(); void desktopWindowClosed(); void emitWebEvent(const QVariant& msg); + void onTabletShown(); protected: void removeButtonsFromHomeScreen(); void loadHomeScreen(bool forceOntoHomeScreen); From dff4e6e32c2d6af0834139cc26150ffb715240c8 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 15:40:38 -0400 Subject: [PATCH 248/280] WL21423 Changing order of camera setup steps --- 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 8debcbd538..7728015d70 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4193,9 +4193,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); From 498ac5d0ab65209aa40230435c10572ecde04f9d Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 15:08:06 -0700 Subject: [PATCH 249/280] Updated version of puck-attach.js --- scripts/developer/tests/puck-attach.js | 173 +++++++++++++++++-------- 1 file changed, 121 insertions(+), 52 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 65adf35d00..823308e3d5 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -17,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; +var TABLET_APP_URL = "https://hifi-content.s3.amazonaws.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -33,12 +33,6 @@ function onScreenChanged(type, url) { if (!shown) { // hook up to event bridge tablet.webEventReceived.connect(onWebEventReceived); - - Script.setTimeout(function () { - // send available tracked objects to the html running in the tablet. - var availableTrackedObjects = getAvailableTrackedObjects(); - tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -72,6 +66,12 @@ function getAvailableTrackedObjects() { } return available; } +function sendAvailableTrackedObjects() { + tablet.emitScriptEvent(JSON.stringify({ + pucks: getAvailableTrackedObjects(), + selectedPuck: ((lastPuck === undefined) ? -1 : lastPuck.name) + })); +} function getRelativePosition(origin, rotation, offset) { var relativeOffset = Vec3.multiplyQbyV(rotation, offset); @@ -81,45 +81,74 @@ function getRelativePosition(origin, rotation, offset) { function getPropertyForEntity(entityID, propertyName) { return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } +function entityExists(entityID) { + return Object.keys(Entities.getEntityProperties(entityID)).length > 0; +} var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres -var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 0.5; // metres +var VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE = 10.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; -var lastPuck = { }; +var lastPuck; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); - var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); - - // should be an overlay - var puckEntityProperties = { - name: "Tracked Puck", - type: "Model", - modelURL: VIVE_PUCK_MODEL, - dimensions: VIVE_PUCK_DIMENSIONS, - position: spawnPosition, - userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' - }; - - var puckEntityID = Entities.addEntity(puckEntityProperties); + var action = indexToTrackedObjectName(puck.puckno); + var pose = Controller.getPoseValue(Controller.Standard[action]); - if (trackedPucks.hasOwnProperty(puck.puckno)) { - destroyPuck(puck.puckno); + if (pose && pose.valid) { + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + + // if we've already created this puck, destroy it + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + // if we had an unfinalized puck, destroy it + if (lastPuck !== undefined) { + destroyPuck(lastPuck.name); + } + // create our new unfinalized puck + trackedPucks[puck.puckno] = { + puckEntityID: puckEntityID, + trackedEntityID: "" + }; + lastPuck = trackedPucks[puck.puckno]; + lastPuck.name = Number(puck.puckno); } - - trackedPucks[puck.puckno] = { - puckEntityID: puckEntityID, - trackedEntityID: "" - }; - lastPuck = trackedPucks[puck.puckno]; } -function finalizePuck() { +function finalizePuck(puckName) { // find nearest entity and change its parent to the puck + + if (!trackedPucks.hasOwnProperty(puckName)) { + print('2'); + return; + } + if (lastPuck === undefined) { + print('3'); + return; + } + if (lastPuck.name !== Number(puckName)) { + print('1'); + return; + } + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); @@ -142,7 +171,16 @@ function finalizePuck() { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + // remember the userdata for the tracked entity since we're about to + // remove it and make it ungrabbable + lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + // update properties of the tracked entity + Entities.editEntity(lastPuck.trackedEntityID, { + "parentID": lastPuck.puckEntityID, + "userData": '{ "grabbableKey": { "grabbable": false } }' + }); + // remove reference to puck since it is now calibrated and finalized + lastPuck = undefined; } } function updatePucks() { @@ -156,32 +194,57 @@ function updatePucks() { if (pose && pose.valid) { var puck = trackedPucks[puckName]; if (puck.trackedEntityID) { - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, puckXform); + if (entityExists(puck.trackedEntityID)) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, puckXform); + + var d = Vec3.distance(MyAvatar.position, finalXform.pos); + if (d > VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE) { + print('tried to move tracked object too far away: ' + d); + return; + } - Entities.editEntity(puck.puckEntityID, { - position: finalXform.pos, - rotation: finalXform.rot, - }); - - // in case someone grabbed both entities and destroyed the - // child/parent relationship - Entities.editEntity(puck.trackedEntityID, { - parentID: puck.puckEntityID - }); + Entities.editEntity(puck.puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID + }); + } else { + destroyPuck(puckName); + } } } } } function destroyPuck(puckName) { // unparent entity and delete its parent - var puckEntityID = trackedPucks[puckName].puckEntityID; - var trackedEntityID = trackedPucks[puckName].trackedEntityID; + if (!trackedPucks.hasOwnProperty(puckName)) { + return; + } + + var puck = trackedPucks[puckName]; + var puckEntityID = puck.puckEntityID; + var trackedEntityID = puck.trackedEntityID; - // remove the puck as a parent entity and then delete the puck - Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); - Entities.deleteEntity(puckEntityID); + // remove the puck as a parent entity and restore the tracked entities + // former userdata + Entities.editEntity(trackedEntityID, { + "parentID": "{00000000-0000-0000-0000-000000000000}", + "userData": puck.trackedEntityUserData + }); + + delete trackedPucks[puckName]; + + Script.setTimeout(function() { + // delete the puck + Entities.deleteEntity(puckEntityID); + }, 100); } function destroyPucks() { // remove all pucks and unparent entities @@ -202,11 +265,17 @@ function onWebEventReceived(msg) { } switch (obj.cmd) { + case "ready": + sendAvailableTrackedObjects(); + break; case "create": createPuck(obj); break; case "finalize": - finalizePuck(); + finalizePuck(obj.puckno); + break; + case "destroy": + destroyPuck(obj.puckno); break; } } From 67b59210796bb2e10b08b9904b4e42a95d1b006f Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 16:01:12 -0700 Subject: [PATCH 250/280] Changed puck-attach to make tracked entities collisionless --- scripts/developer/tests/puck-attach.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 823308e3d5..04d5db5710 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -171,17 +171,19 @@ function finalizePuck(puckName) { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - // remember the userdata for the tracked entity since we're about to - // remove it and make it ungrabbable + // remember the userdata and collisionless flag for the tracked entity since + // we're about to remove it and make it ungrabbable and collisionless lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + lastPuck.trackedEntityCollisionFlag = getPropertyForEntity(foundEntity, "collisionless"); // update properties of the tracked entity Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID, - "userData": '{ "grabbableKey": { "grabbable": false } }' + "userData": '{ "grabbableKey": { "grabbable": false } }', + "collisionless": 1 }); // remove reference to puck since it is now calibrated and finalized lastPuck = undefined; - } + } } function updatePucks() { // for each puck, update its position and orientation @@ -233,14 +235,19 @@ function destroyPuck(puckName) { var trackedEntityID = puck.trackedEntityID; // remove the puck as a parent entity and restore the tracked entities - // former userdata + // former userdata and collision flag Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}", - "userData": puck.trackedEntityUserData + "userData": puck.trackedEntityUserData, + "collisionless": puck.trackedEntityCollisionFlag }); delete trackedPucks[puckName]; + // in some cases, the entity deletion may occur before the parent change + // has been processed, resulting in both the puck and the tracked entity + // to be deleted so we wait 100ms before deleting the puck, assuming + // that the parent change has occured Script.setTimeout(function() { // delete the puck Entities.deleteEntity(puckEntityID); From 9058a8e2ca73481570ff0198ed90747d2cb82f85 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 4 Aug 2017 16:36:00 +0200 Subject: [PATCH 251/280] Cleanup top menu on menu reinstantiation --- interface/resources/qml/hifi/tablet/TabletMenuStack.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 2fd33e9cbc..9076cd6c48 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -114,6 +114,7 @@ Item { } function clearMenus() { + topMenu = null d.clear() } From 85500841fb5ba0b361411aae13713bb9d9e99ccf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 4 Aug 2017 11:19:09 -0700 Subject: [PATCH 252/280] Crash fix for avatars that are missing "Hips", "Head" and other required joints. --- interface/src/avatar/MyCharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 3d98a0e604..761e2e5bff 100755 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -36,7 +36,7 @@ MyCharacterController::~MyCharacterController() { void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { CharacterController::setDynamicsWorld(world); - if (world) { + if (world && _rigidBody) { initRayShotgun(world); } } From 9fdc044ae10d4ae34175169240a4c70bedc5a485 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 4 Aug 2017 11:19:59 -0700 Subject: [PATCH 253/280] Crash fix for web3d overlays that are not the tablet. --- interface/src/ui/overlays/Web3DOverlay.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d61370151c..e904ff4e4c 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -127,8 +127,9 @@ QString Web3DOverlay::pickURL() { QUrl sourceUrl(_url); if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" || _url.toLower().endsWith(".htm") || _url.toLower().endsWith(".html")) { - - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + if (_webSurface) { + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); + } return "Web3DOverlay.qml"; } else { return QUrl::fromLocalFile(PathUtils::resourcesPath()).toString() + "/" + _url; From d05e1677c3a58272b14c655bbd940a9d73200edd Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 4 Aug 2017 23:04:15 +0300 Subject: [PATCH 254/280] 6677: Change "Desktop" App to Say "Exit VR" --- scripts/system/hmd.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 3598b461a4..c95d85ebeb 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,17 +43,20 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; +var switchToVR = "Enter VR"; +var switchToDesktop = "Exit VR"; + function onHmdChanged(isHmd) { HMD.closeTablet(); if (isHmd) { button.editProperties({ icon: "icons/tablet-icons/switch-desk-i.svg", - text: "DESKTOP" + text: switchToDesktop }); } else { button.editProperties({ icon: "icons/tablet-icons/switch-vr-i.svg", - text: "VR" + text: switchToVR }); } desktopOnlyViews.forEach(function (view) { @@ -70,7 +73,7 @@ function onClicked() { if (headset) { button = tablet.addButton({ icon: HMD.active ? "icons/tablet-icons/switch-desk-i.svg" : "icons/tablet-icons/switch-vr-i.svg", - text: HMD.active ? "DESKTOP" : "VR", + text: HMD.active ? switchToDesktop : switchToVR, sortOrder: 2 }); onHmdChanged(HMD.active); From 86e348916724fc8dd290569acd9fd44ee0c37e02 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 09:42:42 -0700 Subject: [PATCH 255/280] Updates from Threaded Rendering project --- interface/src/Application.cpp | 49 +++++------- interface/src/Application.h | 9 ++- interface/src/scripting/AudioDevices.cpp | 1 + interface/src/ui/ResourceImageItem.cpp | 3 +- interface/src/ui/SnapshotAnimated.cpp | 3 +- .../src/RenderableWebEntityItem.cpp | 7 +- .../src/RenderableWebEntityItem.h | 2 +- .../plugins/src/plugins/DisplayPlugin.cpp | 17 +++- libraries/plugins/src/plugins/DisplayPlugin.h | 11 ++- libraries/render/src/render/Args.h | 3 - libraries/shared/src/ThreadHelpers.cpp | 77 ++++++++++++++----- libraries/shared/src/ThreadHelpers.h | 13 +++- libraries/shared/src/shared/QtHelpers.cpp | 29 ++++++- libraries/shared/src/shared/QtHelpers.h | 1 + tests/render-perf/src/main.cpp | 2 +- 15 files changed, 155 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d016c9d020..549e5338a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,7 +2256,7 @@ void Application::paintGL() { QMutexLocker viewLocker(&_viewMutex); _viewFrustum.calculate(); } - renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), + renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); { @@ -2772,7 +2772,12 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } -bool _renderRequested { false }; +void Application::onPresent(quint32 frameCount) { + if (shouldPaint()) { + postEvent(this, new QEvent(static_cast(Idle)), Qt::HighEventPriority); + postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); + } +} bool Application::event(QEvent* event) { if (!Menu::getInstance()) { @@ -2788,23 +2793,9 @@ bool Application::event(QEvent* event) { // Explicit idle keeps the idle running at a lower interval, but without any rendering // see (windowMinimizedChanged) case Event::Idle: - { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - _lastTimeUpdated.start(); - idle(nsecsElapsed); - } - return true; - - case Event::Present: - if (!_renderRequested) { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - if (shouldPaint(nsecsElapsed)) { - _renderRequested = true; - _lastTimeUpdated.start(); - idle(nsecsElapsed); - postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); - } - } + idle(); + // Clear the event queue of pending idle calls + removePostedEvents(this, Idle); return true; case Event::Paint: @@ -2812,9 +2803,8 @@ bool Application::event(QEvent* event) { // or AvatarInputs will mysteriously move to the bottom-right AvatarInputs::getInstance()->update(); paintGL(); - // wait for the next present event before starting idle / paint again - removePostedEvents(this, Present); - _renderRequested = false; + // Clear the event queue of pending paint calls + removePostedEvents(this, Paint); return true; default: @@ -3633,7 +3623,7 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; -bool Application::shouldPaint(float nsecsElapsed) { +bool Application::shouldPaint() { if (_aboutToQuit) { return false; } @@ -3653,11 +3643,9 @@ bool Application::shouldPaint(float nsecsElapsed) { (float)paintDelaySamples / paintDelayUsecs << "us"; } #endif - - float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC; - + // Throttle if requested - if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { + if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { return false; } @@ -3874,7 +3862,7 @@ void setupCpuMonitorThread() { #endif -void Application::idle(float nsecsElapsed) { +void Application::idle() { PerformanceTimer perfTimer("idle"); // Update the deadlock watchdog @@ -3931,7 +3919,8 @@ void Application::idle(float nsecsElapsed) { steamClient->runCallbacks(); } - float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; + float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND; + _lastTimeUpdated.start(); // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { @@ -7106,6 +7095,7 @@ void Application::updateDisplayMode() { auto oldDisplayPlugin = _displayPlugin; if (_displayPlugin) { + disconnect(_displayPluginPresentConnection); _displayPlugin->deactivate(); } @@ -7146,6 +7136,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; + _displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 123aa85e2e..300bd4ac02 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -129,8 +129,7 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; enum Event { - Present = DisplayPlugin::Present, - Paint, + Paint = QEvent::User + 1, Idle, Lambda }; @@ -409,6 +408,7 @@ private slots: void clearDomainOctreeDetails(); void clearDomainAvatars(); void onAboutToQuit(); + void onPresent(quint32 frameCount); void resettingDomain(); @@ -455,8 +455,8 @@ private: void cleanupBeforeQuit(); - bool shouldPaint(float nsecsElapsed); - void idle(float nsecsElapsed); + bool shouldPaint(); + void idle(); void update(float deltaTime); // Various helper functions called during update() @@ -518,6 +518,7 @@ private: OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; + QMetaObject::Connection _displayPluginPresentConnection; mutable std::mutex _displayPluginLock; InputPluginList _activeInputPlugins; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index d02f4d8fcf..f2e6dbf4d7 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "AudioDevices.h" diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 7b9592fa4c..5b7c1896fe 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include "Application.h" #include "ResourceImageItem.h" #include @@ -16,6 +15,8 @@ #include #include +#include + ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() { auto textureCache = DependencyManager::get(); connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update())); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 70767b007d..3c00be8358 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -13,8 +13,9 @@ #include #include #include +#include -#include +#include #include "SnapshotAnimated.h" QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9a898b4071..ba8e0c18e7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -69,7 +69,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { } } -bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { +bool RenderableWebEntityItem::buildWebSurface() { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { qWarning() << "Too many concurrent web views to create new view"; return false; @@ -132,6 +132,8 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer handlePointerEvent(event); } }; + + auto renderer = DependencyManager::get(); _mousePressConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent); _mouseReleaseConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent); _mouseMoveConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent); @@ -185,8 +187,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - auto renderer = qSharedPointerCast(args->_renderData); - if (!buildWebSurface(renderer)) { + if (!buildWebSurface()) { return; } _fadeStartTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0f5d307766..a2318081b6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -57,7 +57,7 @@ public: virtual QObject* getRootItem() override; private: - bool buildWebSurface(QSharedPointer renderer); + bool buildWebSurface(); void destroyWebSurface(); glm::vec2 getWindowSize() const; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 747c72c08e..20c72159c4 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -18,6 +18,19 @@ void DisplayPlugin::incrementPresentCount() { ++_presentedFrameIndex; - // Alert the app that it needs to paint a new presentation frame - qApp->postEvent(qApp, new QEvent(static_cast(Present)), Qt::HighEventPriority); + { + QMutexLocker locker(&_presentMutex); + _presentCondition.wakeAll(); + } + + emit presented(_presentedFrameIndex); } + +void DisplayPlugin::waitForPresent() { + QMutexLocker locker(&_presentMutex); + while (isActive()) { + if (_presentCondition.wait(&_presentMutex, MSECS_PER_SECOND)) { + break; + } + } +} \ No newline at end of file diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 481a2609fc..9e18ee534d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -134,10 +136,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: - enum Event { - Present = QEvent::User + 1 - }; - virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -221,12 +219,15 @@ public: virtual void cycleDebugOutput() {} + void waitForPresent(); + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); + void presented(quint32 frame); protected: void incrementPresentCount(); @@ -234,6 +235,8 @@ protected: gpu::ContextPointer _gpuContext; private: + QMutex _presentMutex; + QWaitCondition _presentCondition; std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; QElapsedTimer _paintDelayTimer; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d5b5440c32..6a91081c95 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -77,7 +77,6 @@ namespace render { Args() {} Args(const gpu::ContextPointer& context, - QSharedPointer renderData = QSharedPointer(nullptr), float sizeScale = 1.0f, int boundaryLevelAdjust = 0, RenderMode renderMode = DEFAULT_RENDER_MODE, @@ -85,7 +84,6 @@ namespace render { DebugFlags debugFlags = RENDER_DEBUG_NONE, gpu::Batch* batch = nullptr) : _context(context), - _renderData(renderData), _sizeScale(sizeScale), _boundaryLevelAdjust(boundaryLevelAdjust), _renderMode(renderMode), @@ -110,7 +108,6 @@ namespace render { std::shared_ptr _context; std::shared_ptr _blitFramebuffer; std::shared_ptr _shapePipeline; - QSharedPointer _renderData; std::stack _viewFrustums; glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f }; glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f }; diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8f3d16a577..8cf8b85284 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -10,29 +10,66 @@ #include +// Support for viewing the thread name in the debugger. +// Note, Qt actually does this for you but only in debug builds +// Code from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx +// and matches logic in `qt_set_thread_name` in qthread_win.cpp +#ifdef Q_OS_WIN +#include +#pragma pack(push,8) +struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) +#endif + +void setThreadName(const std::string& name) { +#ifdef Q_OS_WIN + static const DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{ 0x1000, name.c_str(), (DWORD)-1, 0 }; + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { } +#endif +} + +void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { + Q_ASSERT(QThread::currentThread() == object->thread()); + // setup a thread for the NodeList and its PacketReceiver + QThread* thread = new QThread(); + thread->setObjectName(name); + + // Execute any additional work to do before the thread starts (like moving members to the target thread + preStartCallback(thread); + + // Link the in-thread initialization code + QObject::connect(thread, &QThread::started, [name, startCallback] { + if (!name.isEmpty()) { + // Make it easy to spot our thread processes inside the debugger + setThreadName("Hifi_" + name.toStdString()); + } + startCallback(); + }); + + // Make sure the thread will be destroyed and cleaned up + QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); + QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // put the object on the thread + object->moveToThread(thread); + thread->start(); + if (priority != QThread::InheritPriority) { + thread->setPriority(priority); + } +} void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority) { - Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver - QThread* thread = new QThread(); - thread->setObjectName(name); - - QString tempName = name; - QObject::connect(thread, &QThread::started, [startCallback] { - startCallback(); - }); - // Make sure the thread will be destroyed and cleaned up - QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); - QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); - - // put the object on the thread - object->moveToThread(thread); - thread->start(); - if (priority != QThread::InheritPriority) { - thread->setPriority(priority); - } + moveToNewNamedThread(object, name, [](QThread*){}, startCallback, priority); } void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { - moveToNewNamedThread(object, name, [] {}, priority); + moveToNewNamedThread(object, name, [](QThread*){}, []{}, priority); } diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index 6e024f787a..d236344dc5 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -32,8 +32,17 @@ void withLock(QMutex& lock, F function) { function(); } -void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority = QThread::InheritPriority); -void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority); +void moveToNewNamedThread(QObject* object, const QString& name, + std::function preStartCallback, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + QThread::Priority priority = QThread::InheritPriority); class ConditionalGuard { public: diff --git a/libraries/shared/src/shared/QtHelpers.cpp b/libraries/shared/src/shared/QtHelpers.cpp index 1ce1c3e07c..3e8c6d57ed 100644 --- a/libraries/shared/src/shared/QtHelpers.cpp +++ b/libraries/shared/src/shared/QtHelpers.cpp @@ -11,11 +11,24 @@ #include #include #include +#include +#include "../Profile.h" Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety") namespace hifi { namespace qt { +static QHash threadHash; +static QReadWriteLock threadHashLock; + +void addBlockingForbiddenThread(const QString& name, QThread* thread) { + if (!thread) { + thread = QThread::currentThread(); + } + QWriteLocker locker(&threadHashLock); + threadHash[thread] = name; +} + bool blockingInvokeMethod( const char* function, QObject *obj, const char *member, @@ -30,9 +43,23 @@ bool blockingInvokeMethod( QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { - if (QThread::currentThread() == qApp->thread()) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function; + return QMetaObject::invokeMethod(obj, member, + Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + + { + QReadLocker locker(&threadHashLock); + for (const auto& thread : threadHash.keys()) { + if (currentThread == thread) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << threadHash[thread]; + } + } } + + PROFILE_RANGE(app, function); return QMetaObject::invokeMethod(obj, member, Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 5da65a378f..2133119324 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -14,6 +14,7 @@ namespace hifi { namespace qt { +void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr); bool blockingInvokeMethod( const char* function, diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ce47a896aa..dbb315a9ae 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -681,7 +681,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); - RenderArgs renderArgs(_renderThread._gpuContext, _octree, DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); From c8ebc299f5aab37ba8db0965b21c12bb837d4a16 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 4 Aug 2017 23:25:26 +0100 Subject: [PATCH 256/280] Limited Max Length of the Chat Box to 256 - as this exceeds Message. max. --- scripts/system/html/ChatPage.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/ChatPage.html b/scripts/system/html/ChatPage.html index e1a3776dd5..9606eeab3e 100644 --- a/scripts/system/html/ChatPage.html +++ b/scripts/system/html/ChatPage.html @@ -195,7 +195,7 @@
- +
From 0e508432c4c745d08d890502d7279c062fc7b105 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 4 Aug 2017 15:27:33 -0700 Subject: [PATCH 257/280] PR comments --- libraries/shared/src/ThreadHelpers.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8cf8b85284..654b8e0252 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -38,11 +38,14 @@ void setThreadName(const std::string& name) { void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver + + // Create the target thread QThread* thread = new QThread(); thread->setObjectName(name); - // Execute any additional work to do before the thread starts (like moving members to the target thread + // Execute any additional work to do before the thread starts like moving members to the target thread. + // This is required as QObject::moveToThread isn't virutal, so we can't override it on objects that contain + // an OpenGLContext and ensure that the context moves to the target thread as well. preStartCallback(thread); // Link the in-thread initialization code @@ -54,8 +57,10 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function Date: Mon, 24 Jul 2017 17:10:45 -0700 Subject: [PATCH 258/280] Changed marketplaces message to be more discoverable --- scripts/system/html/js/marketplacesInject.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 3b3d4b4937..45b2e99018 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -57,7 +57,7 @@ $("body").append( '
' + (!isInitialHiFiPage ? '' : '') + - (isInitialHiFiPage ? '🛈 See also other marketplaces.' : '') + + (isInitialHiFiPage ? '🛈 Get items from Blocks and Clara.io!' : '') + (!isDirectoryPage ? '' : '') + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + '
' From 4570814145dc54c600eaf3b6bcc7b1209f091d81 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Tue, 25 Jul 2017 18:36:39 -0700 Subject: [PATCH 259/280] Added Blocks to marketplace window --- scripts/system/html/img/blocks-tile.png | Bin 0 -> 35130 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 scripts/system/html/img/blocks-tile.png diff --git a/scripts/system/html/img/blocks-tile.png b/scripts/system/html/img/blocks-tile.png new file mode 100644 index 0000000000000000000000000000000000000000..49de535c1c2e908165b7902e562daef419cbc4a2 GIT binary patch literal 35130 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 literal 0 HcmV?d00001 From ca6a323d541dc7311054090acf4df784ebbc05d3 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 15:21:39 -0700 Subject: [PATCH 260/280] Can drag blocks zip folder into hifi and upload the .obj --- interface/src/Application.cpp | 19 ++++++- interface/src/Application.h | 3 ++ .../src/FileScriptingInterface.cpp | 50 +++++++++++-------- .../src/FileScriptingInterface.h | 2 +- scripts/system/html/js/marketplacesInject.js | 24 +++++++-- scripts/system/html/marketplaces.html | 36 ++++++++----- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 549e5338a0..4fa973f68c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -230,6 +230,9 @@ static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; +// temporary zip handling for Emily +static const QString ZIP_EXTENSION = ".zip"; + static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; @@ -260,7 +263,10 @@ const QHash Application::_acceptedExtensi { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }, { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl } + { FST_EXTENSION, &Application::askToSetAvatarUrl }, + + // temporary zip handling for Emily + { ZIP_EXTENSION, &Application::importFromZIP } }; class DeadlockWatchdogThread : public QThread { @@ -2779,6 +2785,15 @@ void Application::onPresent(quint32 frameCount) { } } +bool Application::importFromZIP(const QString& filePath) { + qDebug() << "A zip file has been dropped in: " << filePath; + QUrl empty = ""; + qApp->getFileDownloadInterface()->runUnzip(filePath, empty, false, true); + return true; +} + +bool _renderRequested { false }; + bool Application::event(QEvent* event) { if (!Menu::getInstance()) { return false; @@ -6218,7 +6233,7 @@ void Application::addAssetToWorldFromURLRequestFinished() { if (tempFile.open(QIODevice::WriteOnly)) { tempFile.write(request->getData()); addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key. - qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true); + qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false); } else { QString errorInfo = "Couldn't open temporary file for download"; qWarning(interfaceapp) << errorInfo; diff --git a/interface/src/Application.h b/interface/src/Application.h index 300bd4ac02..dcd1a9a532 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -482,6 +482,9 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); + // temporary zip handling for Emily + bool importFromZIP(const QString& filePath); + bool nearbyEntitiesAreReadyForPhysics(); int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode); void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 30d0a3a201..7d8458598d 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -32,12 +32,19 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { +void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks) { qCDebug(scriptengine) << "Url that was downloaded: " + url.toString(); qCDebug(scriptengine) << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); QString tempDir = path; - tempDir.remove(fileName); + if (!isBlocks) { + tempDir.remove(fileName); + } else { + QTemporaryDir blocks; + tempDir = blocks.path(); + path.remove("file:///"); + } + qCDebug(scriptengine) << "Temporary directory at: " + tempDir; if (!isTempDir(tempDir)) { qCDebug(scriptengine) << "Temporary directory mismatch; risk of losing files"; @@ -45,6 +52,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { } QString file = unzipFile(path, tempDir); + qCDebug(scriptengine) << "Unzipped file: " << file; QString filename = QUrl::fromLocalFile(file).toString(); if (file != "") { qCDebug(scriptengine) << "File to upload: " + filename; @@ -54,6 +62,26 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd) { emit unzipResult(path, filename, autoAdd); } +QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { + + QDir dir(path); + QString dirName = dir.path(); + qCDebug(scriptengine) << "Directory to unzip: " << dirName; + QString target = tempDir + "/model_repo"; + QStringList list = JlCompress::extractDir(dirName, target); + + qCDebug(scriptengine) << list; + + if (!list.isEmpty()) { + return list.front(); + } + else { + qCDebug(scriptengine) << "Extraction failed"; + return ""; + } + +} + // fix to check that we are only referring to a temporary directory bool FileScriptingInterface::isTempDir(QString tempDir) { QString folderName = "/" + tempDir.section("/", -1); @@ -92,24 +120,6 @@ void FileScriptingInterface::downloadZip(QString path, const QString link) { request->send(); } -QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { - - QDir dir(path); - QString dirName = dir.path(); - QString target = tempDir + "/model_repo"; - QStringList list = JlCompress::extractDir(dirName, target); - - qCDebug(scriptengine) << list; - - if (!list.isEmpty()) { - return list.front(); - } else { - qCDebug(scriptengine) << "Extraction failed"; - return ""; - } - -} - // this function is not in use void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) { /*if (!file.isDir()) { diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 5e9a6029e8..dc5ffdace8 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -24,7 +24,7 @@ public: public slots: QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url, bool autoAdd); + void runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks); QString getTempDir(); signals: diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 45b2e99018..0ee337a2ef 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -33,8 +33,8 @@ $("head").append( '' + ); + } + function updateClaraCode() { // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. @@ -322,10 +335,12 @@ var DIRECTORY = 0; var HIFI = 1; - var CLARA = 2; + var BLOCKS = 2; + var CLARA = 3; var pageType = DIRECTORY; if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } + if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } injectCommonCode(pageType === DIRECTORY); @@ -336,6 +351,9 @@ case HIFI: injectHiFiCode(); break; + case BLOCKS: + injectBlocksCode(); + break; case CLARA: injectClaraCode(); break; diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 6051a9df96..8c5fe15429 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -30,19 +30,31 @@
-

This is the default High Fidelity marketplace. Viewing and downloading content from here is fully supported in Interface.

-
- -
-
+
+ +
+
+
@@ -53,9 +55,9 @@
  • Create an account here or log in as an existing user.
  • Choose a model from the list and click “Download to High Fidelity”.
  • -
    - -
    +
    +
    +
    From ebc28a655797da21af274f033ed43da7f1fd5158 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 2 Aug 2017 12:24:14 -0700 Subject: [PATCH 267/280] Renamed isBlocks to isZip, added debug print for non-entity files --- interface/src/Application.cpp | 9 +++++---- interface/src/Application.h | 4 ++-- libraries/script-engine/src/FileScriptingInterface.cpp | 6 +++--- libraries/script-engine/src/FileScriptingInterface.h | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 90f4198383..ff4eb87dce 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6259,12 +6259,12 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { addAssetToWorldError(filename, "Couldn't unzip file " + filename + "."); } -void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { +void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString path = filePath; QString filename = filenameFromPath(path); - if (isBlocks) { + if (isZip) { QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); mapping = "/" + assetFolder + "/" + filename; @@ -6355,6 +6355,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { addAssetToWorldAddEntity(filePath, mapping); } else { + qCDebug(interfaceapp) << "Zipped contents are not valid entity files"; addAssetToWorldInfoDone(filenameFromPath(filePath)); } } @@ -6646,12 +6647,12 @@ void Application::onAssetToWorldMessageBoxClosed() { } -void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { +void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip) { if (autoAdd) { if (!unzipFile.isEmpty()) { for (int i = 0; i < unzipFile.length(); i++) { qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); - addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); + addAssetToWorld(unzipFile.at(i), zipFile, isZip); } } else { addAssetToWorldUnzipFailure(zipFile); diff --git a/interface/src/Application.h b/interface/src/Application.h index 6731cd8611..f8eb393f9e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -331,14 +331,14 @@ public slots: // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); void addAssetToWorldFromURLRequestFinished(); - void addAssetToWorld(QString filePath, QString zipFile, bool isBlocks); + void addAssetToWorld(QString filePath, QString zipFile, bool isZip); void addAssetToWorldUnzipFailure(QString filePath); void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy); void addAssetToWorldUpload(QString filePath, QString mapping); void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash); void addAssetToWorldAddEntity(QString filePath, QString mapping); - void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isBlocks); + void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip); FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index af1cf7994a..0baf3034e8 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -32,12 +32,12 @@ FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent // nothing for now } -void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks) { +void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool isZip) { qCDebug(scriptengine) << "Url that was downloaded: " + url.toString(); qCDebug(scriptengine) << "Path where download is saved: " + path; QString fileName = "/" + path.section("/", -1); QString tempDir = path; - if (!isBlocks) { + if (!isZip) { tempDir.remove(fileName); } else { QTemporaryDir blocks; @@ -60,7 +60,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool else { qCDebug(scriptengine) << "Unzip failed"; } - emit unzipResult(path, fileList, autoAdd, isBlocks); + emit unzipResult(path, fileList, autoAdd, isZip); } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index 806fa7fd9b..4069e7cc78 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -24,11 +24,11 @@ public: public slots: QString convertUrlToPath(QUrl url); - void runUnzip(QString path, QUrl url, bool autoAdd, bool isBlocks); + void runUnzip(QString path, QUrl url, bool autoAdd, bool isZip); QString getTempDir(); signals: - void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks); + void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip); private: bool isTempDir(QString tempDir); From c2cb7573c5678e3c7d21100a922c7515a6183bbc Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 09:42:39 -0700 Subject: [PATCH 268/280] Removed commented out blocks code --- scripts/system/html/js/marketplacesInject.js | 34 ++------------------ scripts/system/html/marketplaces.html | 14 -------- 2 files changed, 3 insertions(+), 45 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 92fa869242..8a8cf62008 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -57,7 +57,7 @@ $("body").append( '
    ' + (!isInitialHiFiPage ? '' : '') + - (isInitialHiFiPage ? '🛈 Get items from Blocks and Clara.io!' : '') + + (isInitialHiFiPage ? '🛈 Get items from Clara.io!' : '') + (!isDirectoryPage ? '' : '') + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + '
    ' @@ -65,10 +65,7 @@ // Footer actions. $("#back-button").on("click", function () { - if (window.history.state != null) window.history.back(); - // to fix back button issue when in directory - //else window.location = "http://www.highfidelity.com/marketplace"; - else window.location = "https://metaverse.highfidelity.com/marketplace?"; + (window.history.state != null) ? window.history.back() : window.location = "https://metaverse.highfidelity.com/marketplace?"; }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(GOTO_DIRECTORY); @@ -82,11 +79,6 @@ letUsKnow.replaceWith(letUsKnow.html()); // Add button links. - - /* Blocks not yet implemented - $('#exploreBlocksMarketplace').on('click', function () { - window.location = "https://vr.google.com/objects"; - });*/ $('#exploreClaraMarketplace').on('click', function () { window.location = "https://clara.io/library?gameCheck=true&public=true"; }); @@ -99,18 +91,6 @@ // Nothing to do. } - function injectBlocksCode() { - // Make space for marketplaces footer in Blocks pages. - /*$("body").append( - '
    ' + - '' + - '
    ' - );*/ - $("body").append( - '

    hello

    ' - ); - } - function updateClaraCode() { // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. @@ -342,15 +322,12 @@ var DIRECTORY = 0; var HIFI = 1; - var BLOCKS = 2; - var CLARA = 3; + var CLARA = 2; var pageType = DIRECTORY; if (location.href.indexOf("highfidelity.com/") !== -1) { pageType = HIFI; } - if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } - //if (pageType != BLOCKS) injectCommonCode(pageType === DIRECTORY); switch (pageType) { case DIRECTORY: @@ -359,11 +336,6 @@ case HIFI: injectHiFiCode(); break; - case BLOCKS: - console.log("in Blocks"); - //injectBlocksCode(); - console.log("blocks injection"); - break; case CLARA: injectClaraCode(); break; diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 11c9e208ca..9e001c064f 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -31,20 +31,6 @@
    -
    From 8c9ce829867d8e520d829e6fbbee61a9720ad45a Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 10:37:48 -0700 Subject: [PATCH 269/280] Fixed Mac and Ubuntu platform build issue (hopefully) --- 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 ff4eb87dce..18c9dabfb0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2783,7 +2783,7 @@ void Application::onPresent(quint32 frameCount) { bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; - QUrl empty = ""; + QUrl empty; qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true); return true; } From af432087e337a987390501d735b5310231738316 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 3 Aug 2017 11:49:02 -0700 Subject: [PATCH 270/280] small revisions to bracketing/variable name --- libraries/script-engine/src/FileScriptingInterface.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 0baf3034e8..5f2460be78 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -40,8 +40,8 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool if (!isZip) { tempDir.remove(fileName); } else { - QTemporaryDir blocks; - tempDir = blocks.path(); + QTemporaryDir zipTemp; + tempDir = zipTemp.path(); path.remove("file:///"); } @@ -56,8 +56,7 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool if (filename != "") { qCDebug(scriptengine) << "File to upload: " + filename; - } - else { + } else { qCDebug(scriptengine) << "Unzip failed"; } emit unzipResult(path, fileList, autoAdd, isZip); @@ -76,8 +75,7 @@ QStringList FileScriptingInterface::unzipFile(QString path, QString tempDir) { if (!list.isEmpty()) { return list; - } - else { + } else { qCDebug(scriptengine) << "Extraction failed"; return list; } From a246c592775f16f554c0b7cfb048f1260349d20a Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 4 Aug 2017 16:59:21 -0700 Subject: [PATCH 271/280] changed debug print for unsupported entity files --- 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 18c9dabfb0..2bc1d1e20e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6355,7 +6355,7 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { addAssetToWorldAddEntity(filePath, mapping); } else { - qCDebug(interfaceapp) << "Zipped contents are not valid entity files"; + qCDebug(interfaceapp) << "Zipped contents are not supported entity files"; addAssetToWorldInfoDone(filenameFromPath(filePath)); } } From 22afb25d44a425d4780de32805f0057f4a0e5920 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Fri, 4 Aug 2017 17:19:26 -0700 Subject: [PATCH 272/280] Capitalize "ENTER VR" --- scripts/system/hmd.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c95d85ebeb..c9c3dbe493 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,8 +43,8 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; -var switchToVR = "Enter VR"; -var switchToDesktop = "Exit VR"; +var switchToVR = "ENTER VR"; +var switchToDesktop = "EXIT VR"; function onHmdChanged(isHmd) { HMD.closeTablet(); From 06c9378b0345092805fa2f90065974c953207298 Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 5 Aug 2017 04:07:04 +0300 Subject: [PATCH 273/280] fix for 6760: Create Mode turns to Portrait when Reopening tablet --- scripts/system/libraries/WebTablet.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 53f88ea62d..c3d55d5875 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -42,7 +42,7 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home- // returns object with two fields: // * position - position in front of the user // * rotation - rotation of entity so it faces the user. -function calcSpawnInfo(hand, tabletHeight) { +function calcSpawnInfo(hand, tabletHeight, landscape) { var finalPosition; var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; @@ -81,7 +81,7 @@ function calcSpawnInfo(hand, tabletHeight) { return { position: position, - rotation: rotation + rotation: landscape ? Quat.multiply(rotation, { x: 0.0, y: 0.0, z: 0.707, w: 0.707 }) : rotation }; } else { var forward = Quat.getForward(headRot); @@ -89,7 +89,7 @@ function calcSpawnInfo(hand, tabletHeight) { var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); return { position: finalPosition, - rotation: Quat.multiply(orientation, {x: 0, y: 1, z: 0, w: 0}) + rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180) }; } } @@ -424,7 +424,7 @@ WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMou tabletProperties.parentJointIndex = SENSOR_TO_ROOM_MATRIX; // compute the appropriate position of the tablet, near the hand controller that was used to spawn it. - var spawnInfo = calcSpawnInfo(hand, this.height); + var spawnInfo = calcSpawnInfo(hand, this.height, this.landscape); tabletProperties.position = spawnInfo.position; tabletProperties.rotation = spawnInfo.rotation; } else { From 3a013bf86e6071ddaa1a70d428fc084fe5f54781 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 5 Aug 2017 13:28:08 +0200 Subject: [PATCH 274/280] Remove snap to item. Add caching for scrolling speedup --- .../resources/qml/hifi/dialogs/content/AttachmentsContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml index 4adb485c2b..62cd581e3b 100644 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml @@ -50,7 +50,7 @@ Item { margins: 4 } clip: true - snapMode: ListView.SnapToItem + cacheBuffer: 4000 model: ListModel {} delegate: Item { From 134bad95341af905dd01423b1eec359b12d8a1ba Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 6 Aug 2017 19:09:40 +0200 Subject: [PATCH 275/280] fixed typo leads to lost touch or mouse release events --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d61370151c..8dc5ae2ca2 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -252,7 +252,7 @@ void Web3DOverlay::render(RenderArgs* args) { if (overlayID == selfOverlayID && (self->_pressed || (!self->_activeTouchPoints.empty() && self->_touchBeginAccepted))) { PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - forwardPointerEvent(overlayID, event); + forwardPointerEvent(overlayID, endEvent); } }); From 186035dc5d1b1a5f9a36f0d18cb7662e73469df3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 11:57:55 -0700 Subject: [PATCH 276/280] adjust various things that cause a crash-on-exit on Linux --- assignment-client/src/audio/AudioMixer.cpp | 2 +- .../src/audio/AudioMixerClientData.cpp | 4 +- assignment-client/src/avatars/AvatarMixer.cpp | 4 +- libraries/networking/src/LimitedNodeList.cpp | 14 +++--- libraries/networking/src/NLPacket.cpp | 11 ++-- libraries/networking/src/Node.cpp | 8 +-- libraries/networking/src/Node.h | 5 ++ libraries/networking/src/PacketReceiver.cpp | 4 +- .../networking/src/udt/PacketHeaders.cpp | 32 ------------ libraries/networking/src/udt/PacketHeaders.h | 50 +++++++++++++++++-- .../ui/src/ui/TabletScriptingInterface.cpp | 4 -- tests/render-texture-load/src/GLIHelpers.h | 1 + 12 files changed, 75 insertions(+), 64 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 93b9b10eb7..9ed6c7fdbc 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -127,7 +127,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer mess // construct a "fake" audio received message from the byte array and packet list information auto audioData = message->getMessage().mid(NUM_BYTES_RFC4122_UUID); - PacketType rewrittenType = REPLICATED_PACKET_MAPPING.key(message->getType()); + PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType()); if (rewrittenType == PacketType::Unknown) { qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING"; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 408ddf038c..9bba9c7f30 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -125,11 +125,11 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c // now make sure it's a packet type that we want to replicate // first check if it is an original type that we should replicate - PacketType mirroredType = REPLICATED_PACKET_MAPPING.value(message.getType()); + PacketType mirroredType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType()); if (mirroredType == PacketType::Unknown) { // if it wasn't check if it is a replicated type that we should re-replicate - if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) { mirroredType = message.getType(); } else { qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning"; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index eea44f031e..c67e998dd4 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -144,10 +144,10 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node // check if this is a packet type we replicate // which means it must be a packet type present in REPLICATED_PACKET_MAPPING or must be the // replicated version of one of those packet types - PacketType replicatedType = REPLICATED_PACKET_MAPPING.value(message.getType()); + PacketType replicatedType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType()); if (replicatedType == PacketType::Unknown) { - if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) { replicatedType = message.getType(); } else { qDebug() << __FUNCTION__ << "called without replicatable packet type - returning"; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index eab0e5e41f..e03ec5e771 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -220,7 +220,7 @@ bool LimitedNodeList::packetVersionMatch(const udt::Packet& packet) { const HifiSockAddr& senderSockAddr = packet.getSenderSockAddr(); QUuid sourceID; - if (NON_SOURCED_PACKETS.contains(headerType)) { + if (PacketTypeEnum::getNonSourcedPackets().contains(headerType)) { hasBeenOutput = versionDebugSuppressMap.contains(senderSockAddr, headerType); if (!hasBeenOutput) { @@ -256,8 +256,8 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe PacketType headerType = NLPacket::typeInHeader(packet); - if (NON_SOURCED_PACKETS.contains(headerType)) { - if (REPLICATED_PACKET_MAPPING.key(headerType) != PacketType::Unknown) { + if (PacketTypeEnum::getNonSourcedPackets().contains(headerType)) { + if (PacketTypeEnum::getReplicatedPacketMapping().key(headerType) != PacketType::Unknown) { // this is a replicated packet type - make sure the socket that sent it to us matches // one from one of our current upstream nodes @@ -298,7 +298,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe SharedNodePointer matchingNode = nodeWithUUID(sourceID); if (matchingNode) { - if (!NON_VERIFIED_PACKETS.contains(headerType)) { + if (!PacketTypeEnum::getNonVerifiedPackets().contains(headerType)) { QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet); QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, matchingNode->getConnectionSecret()); @@ -345,13 +345,13 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) { } void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) { - if (!NON_SOURCED_PACKETS.contains(packet.getType())) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) { packet.writeSourceID(getSessionUUID()); } if (!connectionSecret.isNull() - && !NON_SOURCED_PACKETS.contains(packet.getType()) - && !NON_VERIFIED_PACKETS.contains(packet.getType())) { + && !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType()) + && !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) { packet.writeVerificationHashGivenSecret(connectionSecret); } } diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp index a11dd69753..5c5077691b 100644 --- a/libraries/networking/src/NLPacket.cpp +++ b/libraries/networking/src/NLPacket.cpp @@ -12,8 +12,8 @@ #include "NLPacket.h" int NLPacket::localHeaderSize(PacketType type) { - bool nonSourced = NON_SOURCED_PACKETS.contains(type); - bool nonVerified = NON_VERIFIED_PACKETS.contains(type); + bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type); + bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type); qint64 optionalSize = (nonSourced ? 0 : NUM_BYTES_RFC4122_UUID) + ((nonSourced || nonVerified) ? 0 : NUM_BYTES_MD5_HASH); return sizeof(PacketType) + sizeof(PacketVersion) + optionalSize; } @@ -198,13 +198,13 @@ void NLPacket::readVersion() { } void NLPacket::readSourceID() { - if (!NON_SOURCED_PACKETS.contains(_type)) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(_type)) { _sourceID = sourceIDInHeader(*this); } } void NLPacket::writeSourceID(const QUuid& sourceID) const { - Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type)); + Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion); memcpy(_packet.get() + offset, sourceID.toRfc4122().constData(), NUM_BYTES_RFC4122_UUID); @@ -213,7 +213,8 @@ void NLPacket::writeSourceID(const QUuid& sourceID) const { } void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const { - Q_ASSERT(!NON_SOURCED_PACKETS.contains(_type) && !NON_VERIFIED_PACKETS.contains(_type)); + Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) && + !PacketTypeEnum::getNonVerifiedPackets().contains(_type)); auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index d7f8f404e6..25eef38dbd 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -29,11 +29,9 @@ int NodePtrMetaTypeId = qRegisterMetaType("Node*"); int sharedPtrNodeMetaTypeId = qRegisterMetaType>("QSharedPointer"); int sharedNodePtrMetaTypeId = qRegisterMetaType("SharedNodePointer"); -namespace NodeType { - QHash TypeNameHash; -} - void NodeType::init() { + QHash& TypeNameHash = Node::getTypeNameHash(); + TypeNameHash.insert(NodeType::DomainServer, "Domain Server"); TypeNameHash.insert(NodeType::EntityServer, "Entity Server"); TypeNameHash.insert(NodeType::Agent, "Agent"); @@ -50,6 +48,7 @@ void NodeType::init() { } const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { + QHash& TypeNameHash = Node::getTypeNameHash(); QHash::iterator matchedTypeName = TypeNameHash.find(nodeType); return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; } @@ -85,6 +84,7 @@ NodeType_t NodeType::downstreamType(NodeType_t primaryType) { } NodeType_t NodeType::fromString(QString type) { + QHash& TypeNameHash = Node::getTypeNameHash(); return TypeNameHash.key(type, NodeType::Unassigned); } diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index c20ff5a395..4d09f077bd 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -86,6 +86,11 @@ public: bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; } + static QHash& getTypeNameHash() { + static QHash TypeNameHash; + return TypeNameHash; + } + private: // privatize copy and assignment operator to disallow Node copying Node(const Node &otherNode); diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 21db207375..556e55beb2 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -32,7 +32,7 @@ bool PacketReceiver::registerListenerForTypes(PacketTypeList types, QObject* lis // Partition types based on whether they are sourced or not (non sourced in front) auto middle = std::partition(std::begin(types), std::end(types), [](PacketType type) { - return NON_SOURCED_PACKETS.contains(type); + return PacketTypeEnum::getNonSourcedPackets().contains(type); }); QMetaMethod nonSourcedMethod, sourcedMethod; @@ -123,7 +123,7 @@ QMetaMethod PacketReceiver::matchingMethodForListener(PacketType type, QObject* SIGNATURE_TEMPLATE.arg(slot, NON_SOURCED_MESSAGE_LISTENER_PARAMETERS) }; - if (!NON_SOURCED_PACKETS.contains(type)) { + if (!PacketTypeEnum::getNonSourcedPackets().contains(type)) { static const QString SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer,QSharedPointer"; static const QString TYPEDEF_SOURCED_MESSAGE_LISTENER_PARAMETERS = "QSharedPointer,SharedNodePointer"; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index d2500196d9..241ccaf5d6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -22,38 +22,6 @@ Q_DECLARE_METATYPE(PacketType); int packetTypeMetaTypeId = qRegisterMetaType(); -const QSet NON_VERIFIED_PACKETS = QSet() - << PacketType::NodeJsonStats << PacketType::EntityQuery - << PacketType::OctreeDataNack << PacketType::EntityEditNack - << PacketType::DomainListRequest << PacketType::StopNode - << PacketType::DomainDisconnectRequest << PacketType::UsernameFromIDRequest - << PacketType::NodeKickRequest << PacketType::NodeMuteRequest; - -const QSet NON_SOURCED_PACKETS = QSet() - << PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment - << PacketType::DomainServerRequireDTLS << PacketType::DomainConnectRequest - << PacketType::DomainList << PacketType::DomainConnectionDenied - << PacketType::DomainServerPathQuery << PacketType::DomainServerPathResponse - << PacketType::DomainServerAddedNode << PacketType::DomainServerConnectionToken - << PacketType::DomainSettingsRequest << PacketType::DomainSettings - << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat - << PacketType::ICEServerHeartbeatACK << PacketType::ICEPing << PacketType::ICEPingReply - << PacketType::ICEServerHeartbeatDenied << PacketType::AssignmentClientStatus << PacketType::StopNode - << PacketType::DomainServerRemovedNode << PacketType::UsernameFromIDReply << PacketType::OctreeFileReplacement - << PacketType::ReplicatedMicrophoneAudioNoEcho << PacketType::ReplicatedMicrophoneAudioWithEcho - << PacketType::ReplicatedInjectAudio << PacketType::ReplicatedSilentAudioFrame - << PacketType::ReplicatedAvatarIdentity << PacketType::ReplicatedKillAvatar << PacketType::ReplicatedBulkAvatarData; - -const QHash REPLICATED_PACKET_MAPPING { - { PacketType::MicrophoneAudioNoEcho, PacketType::ReplicatedMicrophoneAudioNoEcho }, - { PacketType::MicrophoneAudioWithEcho, PacketType::ReplicatedMicrophoneAudioWithEcho }, - { PacketType::InjectAudio, PacketType::ReplicatedInjectAudio }, - { PacketType::SilentAudioFrame, PacketType::ReplicatedSilentAudioFrame }, - { PacketType::AvatarIdentity, PacketType::ReplicatedAvatarIdentity }, - { PacketType::KillAvatar, PacketType::ReplicatedKillAvatar }, - { PacketType::BulkAvatarData, PacketType::ReplicatedBulkAvatarData } -}; - PacketVersion versionForPacketType(PacketType packetType) { switch (packetType) { case PacketType::DomainList: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index cb3db791b4..8ed0966291 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -123,19 +123,59 @@ public: ReplicatedBulkAvatarData, NUM_PACKET_TYPE }; + + const static QHash getReplicatedPacketMapping() { + const QHash REPLICATED_PACKET_MAPPING { + { PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho }, + { PacketTypeEnum::Value::MicrophoneAudioWithEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho }, + { PacketTypeEnum::Value::InjectAudio, PacketTypeEnum::Value::ReplicatedInjectAudio }, + { PacketTypeEnum::Value::SilentAudioFrame, PacketTypeEnum::Value::ReplicatedSilentAudioFrame }, + { PacketTypeEnum::Value::AvatarIdentity, PacketTypeEnum::Value::ReplicatedAvatarIdentity }, + { PacketTypeEnum::Value::KillAvatar, PacketTypeEnum::Value::ReplicatedKillAvatar }, + { PacketTypeEnum::Value::BulkAvatarData, PacketTypeEnum::Value::ReplicatedBulkAvatarData } + }; + + return REPLICATED_PACKET_MAPPING; + } + + const static QSet getNonVerifiedPackets() { + const QSet NON_VERIFIED_PACKETS = QSet() + << PacketTypeEnum::Value::NodeJsonStats << PacketTypeEnum::Value::EntityQuery + << PacketTypeEnum::Value::OctreeDataNack << PacketTypeEnum::Value::EntityEditNack + << PacketTypeEnum::Value::DomainListRequest << PacketTypeEnum::Value::StopNode + << PacketTypeEnum::Value::DomainDisconnectRequest << PacketTypeEnum::Value::UsernameFromIDRequest + << PacketTypeEnum::Value::NodeKickRequest << PacketTypeEnum::Value::NodeMuteRequest; + return NON_VERIFIED_PACKETS; + } + + const static QSet getNonSourcedPackets() { + const QSet NON_SOURCED_PACKETS = QSet() + << PacketTypeEnum::Value::StunResponse << PacketTypeEnum::Value::CreateAssignment + << PacketTypeEnum::Value::RequestAssignment << PacketTypeEnum::Value::DomainServerRequireDTLS + << PacketTypeEnum::Value::DomainConnectRequest << PacketTypeEnum::Value::DomainList + << PacketTypeEnum::Value::DomainConnectionDenied << PacketTypeEnum::Value::DomainServerPathQuery + << PacketTypeEnum::Value::DomainServerPathResponse << PacketTypeEnum::Value::DomainServerAddedNode + << PacketTypeEnum::Value::DomainServerConnectionToken << PacketTypeEnum::Value::DomainSettingsRequest + << PacketTypeEnum::Value::DomainSettings << PacketTypeEnum::Value::ICEServerPeerInformation + << PacketTypeEnum::Value::ICEServerQuery << PacketTypeEnum::Value::ICEServerHeartbeat + << PacketTypeEnum::Value::ICEServerHeartbeatACK << PacketTypeEnum::Value::ICEPing + << PacketTypeEnum::Value::ICEPingReply << PacketTypeEnum::Value::ICEServerHeartbeatDenied + << PacketTypeEnum::Value::AssignmentClientStatus << PacketTypeEnum::Value::StopNode + << PacketTypeEnum::Value::DomainServerRemovedNode << PacketTypeEnum::Value::UsernameFromIDReply + << PacketTypeEnum::Value::OctreeFileReplacement << PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho + << PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio + << PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity + << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData; + return NON_SOURCED_PACKETS; + } }; using PacketType = PacketTypeEnum::Value; -extern const QHash REPLICATED_PACKET_MAPPING; - const int NUM_BYTES_MD5_HASH = 16; typedef char PacketVersion; -extern const QSet NON_VERIFIED_PACKETS; -extern const QSet NON_SOURCED_PACKETS; - PacketVersion versionForPacketType(PacketType packetType); QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols QString protocolVersionsSignatureBase64(); diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 0fd32b42e6..984d743ebf 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -28,11 +28,9 @@ const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system"; const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; TabletScriptingInterface::TabletScriptingInterface() { - qCDebug(uiLogging) << "Building tablet scripting interface"; } TabletScriptingInterface::~TabletScriptingInterface() { - qCDebug(uiLogging) << "Destroying tablet scripting interface"; } ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() { @@ -191,7 +189,6 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent) } TabletProxy::~TabletProxy() { - qCDebug(uiLogging) << "Destroying tablet proxy " << _name; if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; } @@ -846,7 +843,6 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) : } TabletButtonProxy::~TabletButtonProxy() { - qCDebug(uiLogging) << "Destroying tablet button proxy " ; if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet button proxy on wrong thread"; } diff --git a/tests/render-texture-load/src/GLIHelpers.h b/tests/render-texture-load/src/GLIHelpers.h index c2841311a9..886176e874 100644 --- a/tests/render-texture-load/src/GLIHelpers.h +++ b/tests/render-texture-load/src/GLIHelpers.h @@ -28,6 +28,7 @@ #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-result" #pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wtype-limits" #endif #include From 6f9460162d3d93ee5bfde7137b4a1aacf3558dd7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 12:47:45 -0700 Subject: [PATCH 277/280] avoid calling a pure virtual function from GLBackend dtor --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 2 -- libraries/gpu-gl/src/gpu/gl/GLBackend.h | 2 +- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 5 +++++ libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 5 +++++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 11e67811b6..eac74fbdf9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -196,8 +196,6 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() { - resetStages(); - killInput(); killTransform(); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 88aecda617..1908db614d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -66,7 +66,7 @@ protected: public: static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); - ~GLBackend(); + virtual ~GLBackend(); void setCameraCorrection(const Mat4& correction); void render(const Batch& batch) final override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 2c64b9d23d..42926fdb1c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -43,6 +43,11 @@ public: explicit GL41Backend(bool syncCache) : Parent(syncCache) {} GL41Backend() : Parent() {} + virtual ~GL41Backend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } static const std::string GL41_VERSION; const std::string& getVersion() const override { return GL41_VERSION; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index d5ff1a3485..1a4b63d35f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -39,6 +39,11 @@ public: explicit GL45Backend(bool syncCache) : Parent(syncCache) {} GL45Backend() : Parent() {} + virtual ~GL45Backend() { + // call resetStages here rather than in ~GLBackend dtor because it will call releaseResourceBuffer + // which is pure virtual from GLBackend's dtor. + resetStages(); + } static const std::string GL45_VERSION; const std::string& getVersion() const override { return GL45_VERSION; } From 83a30e2f350ed9d9811c21bcee381c1730fbf280 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 6 Aug 2017 13:12:27 -0700 Subject: [PATCH 278/280] missed some statics --- libraries/networking/src/udt/PacketHeaders.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 8ed0966291..e2304e62f7 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -125,7 +125,7 @@ public: }; const static QHash getReplicatedPacketMapping() { - const QHash REPLICATED_PACKET_MAPPING { + const static QHash REPLICATED_PACKET_MAPPING { { PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho }, { PacketTypeEnum::Value::MicrophoneAudioWithEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho }, { PacketTypeEnum::Value::InjectAudio, PacketTypeEnum::Value::ReplicatedInjectAudio }, @@ -134,12 +134,11 @@ public: { PacketTypeEnum::Value::KillAvatar, PacketTypeEnum::Value::ReplicatedKillAvatar }, { PacketTypeEnum::Value::BulkAvatarData, PacketTypeEnum::Value::ReplicatedBulkAvatarData } }; - return REPLICATED_PACKET_MAPPING; } const static QSet getNonVerifiedPackets() { - const QSet NON_VERIFIED_PACKETS = QSet() + const static QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeEnum::Value::NodeJsonStats << PacketTypeEnum::Value::EntityQuery << PacketTypeEnum::Value::OctreeDataNack << PacketTypeEnum::Value::EntityEditNack << PacketTypeEnum::Value::DomainListRequest << PacketTypeEnum::Value::StopNode @@ -149,7 +148,7 @@ public: } const static QSet getNonSourcedPackets() { - const QSet NON_SOURCED_PACKETS = QSet() + const static QSet NON_SOURCED_PACKETS = QSet() << PacketTypeEnum::Value::StunResponse << PacketTypeEnum::Value::CreateAssignment << PacketTypeEnum::Value::RequestAssignment << PacketTypeEnum::Value::DomainServerRequireDTLS << PacketTypeEnum::Value::DomainConnectRequest << PacketTypeEnum::Value::DomainList From 07a4beac73ed8c789148816a52ed7d5ee00a5d02 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 7 Aug 2017 10:28:36 -0700 Subject: [PATCH 279/280] Fix the crash --- .../ui/overlays/ContextOverlayInterface.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e406d139d0..77b9424d2f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -257,15 +257,23 @@ void ContextOverlayInterface::openMarketplace() { } void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); - } + auto entityTree = qApp->getEntities()->getTree(); + entityTree->withReadLock([&] { + auto entityItem = entityTree->findEntityByEntityItemID(entityItemID); + if ((entityItem != NULL) && !entityItem->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + entityItem->setShouldHighlight(true); + } + }); } void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); - } + auto entityTree = qApp->getEntities()->getTree(); + entityTree->withReadLock([&] { + auto entityItem = entityTree->findEntityByEntityItemID(entityItemID); + if ((entityItem != NULL) && entityItem->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + entityItem->setShouldHighlight(false); + } + }); } From 38db4a6f472c82335fc90c300158b9cdfa109c60 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 7 Aug 2017 20:03:55 +0100 Subject: [PATCH 280/280] fixed create spinner not appearing --- scripts/system/html/css/edit-style.css | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 40f3d682cd..5ba5568b59 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -267,18 +267,17 @@ input[type=number] { padding-right: 3px; } input[type=number]::-webkit-inner-spin-button { - -webkit-appearance: none; opacity: 1.0; display: block; position: relative; width: 10px; - height: 100%; + height: 90%; overflow: hidden; font-family: hifi-glyphs; - font-size: 46px; + font-size: 32px; color: #afafaf; cursor: pointer; - /*background-color: #000000;*/ + background-color: #000000; } input[type=number]::-webkit-inner-spin-button:before, input[type=number]::-webkit-inner-spin-button:after {
    -
    - +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +

    Clara.io has thousands of models available for importing into High Fidelity. Follow these steps for the best experience:

    +
      +
    1. Create an account here or log in as an existing user.
    2. +
    3. Choose a model from the list and click “Download to High Fidelity”.
    4. +
    +
    +
    From ed1ed4f3e0e16b6abc65d185cfa675f1460a6a04 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 16:58:23 -0700 Subject: [PATCH 261/280] Blocks drag does not work yet --- interface/src/Application.cpp | 23 +++++++++++++++---- interface/src/Application.h | 4 ++-- .../src/FileScriptingInterface.cpp | 21 +++++++++-------- .../src/FileScriptingInterface.h | 4 ++-- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4fa973f68c..be926cd7ac 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6263,12 +6263,21 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { addAssetToWorldError(filename, "Couldn't unzip file " + filename + "."); } -void Application::addAssetToWorld(QString filePath) { +void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; + qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString path = QUrl(filePath).toLocalFile(); QString filename = filenameFromPath(path); - QString mapping = "/" + filename; + QString mapping; + if (isBlocks) { + QString assetFolder = zipFile.section("/", -1); + assetFolder.remove(".zip"); + mapping = "/" + assetFolder + "/" + filename; + } else { + mapping = "/" + filename; + } // Test repeated because possibly different code paths. if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { @@ -6639,15 +6648,19 @@ void Application::onAssetToWorldMessageBoxClosed() { } -void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) { +void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { if (autoAdd) { if (!unzipFile.isEmpty()) { - addAssetToWorld(unzipFile); + qCDebug(interfaceapp) << "My folder contents: " << unzipFile; + for (int i = 0; i < unzipFile.length(); i++) { + qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); + addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); + } } else { addAssetToWorldUnzipFailure(zipFile); } } else { - showAssetServerWidget(unzipFile); + showAssetServerWidget(unzipFile.first()); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index dcd1a9a532..033cca8452 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -331,14 +331,14 @@ public slots: // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); void addAssetToWorldFromURLRequestFinished(); - void addAssetToWorld(QString filePath); + void addAssetToWorld(QString filePath, QString zipFile, bool isBlocks); void addAssetToWorldUnzipFailure(QString filePath); void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy); void addAssetToWorldUpload(QString filePath, QString mapping); void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash); void addAssetToWorldAddEntity(QString filePath, QString mapping); - void handleUnzip(QString sourceFile, QString destinationFile, bool autoAdd); + void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isBlocks); FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; } diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 7d8458598d..5ba2868365 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -51,18 +51,21 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool return; } - QString file = unzipFile(path, tempDir); - qCDebug(scriptengine) << "Unzipped file: " << file; - QString filename = QUrl::fromLocalFile(file).toString(); - if (file != "") { + QStringList fileList = unzipFile(path, tempDir); + qCDebug(scriptengine) << "Unzipped file list: " << fileList; + QString filename = QUrl::fromLocalFile(fileList.first()).toString(); + + if (filename != "") { qCDebug(scriptengine) << "File to upload: " + filename; - } else { + } + else { qCDebug(scriptengine) << "Unzip failed"; } - emit unzipResult(path, filename, autoAdd); + emit unzipResult(path, fileList, autoAdd, isBlocks); + } -QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { +QStringList FileScriptingInterface::unzipFile(QString path, QString tempDir) { QDir dir(path); QString dirName = dir.path(); @@ -73,11 +76,11 @@ QString FileScriptingInterface::unzipFile(QString path, QString tempDir) { qCDebug(scriptengine) << list; if (!list.isEmpty()) { - return list.front(); + return list; } else { qCDebug(scriptengine) << "Extraction failed"; - return ""; + return list; } } diff --git a/libraries/script-engine/src/FileScriptingInterface.h b/libraries/script-engine/src/FileScriptingInterface.h index dc5ffdace8..806fa7fd9b 100644 --- a/libraries/script-engine/src/FileScriptingInterface.h +++ b/libraries/script-engine/src/FileScriptingInterface.h @@ -28,11 +28,11 @@ public slots: QString getTempDir(); signals: - void unzipResult(QString zipFile, QString unzipFile, bool autoAdd); + void unzipResult(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks); private: bool isTempDir(QString tempDir); - QString unzipFile(QString path, QString tempDir); + QStringList unzipFile(QString path, QString tempDir); void recursiveFileScan(QFileInfo file, QString* dirName); void downloadZip(QString path, const QString link); From c3d943ff72a2aea26e5dec70249496b9328b9a66 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 26 Jul 2017 18:57:19 -0700 Subject: [PATCH 262/280] Drag zip folder works --- interface/src/Application.cpp | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index be926cd7ac..b52c3321b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2788,7 +2788,7 @@ void Application::onPresent(quint32 frameCount) { bool Application::importFromZIP(const QString& filePath) { qDebug() << "A zip file has been dropped in: " << filePath; QUrl empty = ""; - qApp->getFileDownloadInterface()->runUnzip(filePath, empty, false, true); + qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true); return true; } @@ -6266,6 +6266,38 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; + qCDebug(interfaceapp) << "Original zip folder: " << zipFile; + QString mapping; + QString path = filePath; + QString filename = filenameFromPath(filePath); + if (isBlocks) { + QString assetFolder = zipFile.section("/", -1); + assetFolder.remove(".zip"); + mapping = "/" + assetFolder + "/" + filenameFromPath(filename); + } + else { + path = QUrl(filePath).toLocalFile(); + filename = filenameFromPath(path); + mapping = "/" + filename; + } + + // Test repeated because possibly different code paths. + if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { + QString errorInfo = "You do not have permissions to write to the Asset Server."; + qWarning(interfaceapp) << "Error downloading model: " + errorInfo; + addAssetToWorldError(filename, errorInfo); + return; + } + + addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); + + addAssetToWorldWithNewMapping(path, mapping, 0); +} + +/**void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { + // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). + qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString path = QUrl(filePath).toLocalFile(); @@ -6290,7 +6322,7 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); addAssetToWorldWithNewMapping(path, mapping, 0); -} +}*/ void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { auto request = DependencyManager::get()->createGetMappingRequest(mapping); From c6d5b1a5e627b285d09c2ead61ec8471a8c062d8 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Thu, 27 Jul 2017 13:33:53 -0700 Subject: [PATCH 263/280] Removed extraneous print statements --- interface/src/Application.cpp | 36 +------------------ .../src/FileScriptingInterface.cpp | 1 - 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b52c3321b6..004b1e91e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6265,9 +6265,6 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) { void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). - - qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; - qCDebug(interfaceapp) << "Original zip folder: " << zipFile; QString mapping; QString path = filePath; QString filename = filenameFromPath(filePath); @@ -6275,8 +6272,7 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); mapping = "/" + assetFolder + "/" + filenameFromPath(filename); - } - else { + } else { path = QUrl(filePath).toLocalFile(); filename = filenameFromPath(path); mapping = "/" + filename; @@ -6295,35 +6291,6 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc addAssetToWorldWithNewMapping(path, mapping, 0); } -/**void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBlocks) { - // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). - - qCDebug(interfaceapp) << "File about to be uploaded: " << filePath; - qCDebug(interfaceapp) << "Original zip folder: " << zipFile; - QString path = QUrl(filePath).toLocalFile(); - QString filename = filenameFromPath(path); - QString mapping; - if (isBlocks) { - QString assetFolder = zipFile.section("/", -1); - assetFolder.remove(".zip"); - mapping = "/" + assetFolder + "/" + filename; - } else { - mapping = "/" + filename; - } - - // Test repeated because possibly different code paths. - if (!DependencyManager::get()->getThisNodeCanWriteAssets()) { - QString errorInfo = "You do not have permissions to write to the Asset Server."; - qWarning(interfaceapp) << "Error downloading model: " + errorInfo; - addAssetToWorldError(filename, errorInfo); - return; - } - - addAssetToWorldInfo(filename, "Adding " + mapping.mid(1) + " to the Asset Server."); - - addAssetToWorldWithNewMapping(path, mapping, 0); -}*/ - void Application::addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy) { auto request = DependencyManager::get()->createGetMappingRequest(mapping); @@ -6683,7 +6650,6 @@ void Application::onAssetToWorldMessageBoxClosed() { void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isBlocks) { if (autoAdd) { if (!unzipFile.isEmpty()) { - qCDebug(interfaceapp) << "My folder contents: " << unzipFile; for (int i = 0; i < unzipFile.length(); i++) { qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i); addAssetToWorld(unzipFile.at(i), zipFile, isBlocks); diff --git a/libraries/script-engine/src/FileScriptingInterface.cpp b/libraries/script-engine/src/FileScriptingInterface.cpp index 5ba2868365..af1cf7994a 100644 --- a/libraries/script-engine/src/FileScriptingInterface.cpp +++ b/libraries/script-engine/src/FileScriptingInterface.cpp @@ -52,7 +52,6 @@ void FileScriptingInterface::runUnzip(QString path, QUrl url, bool autoAdd, bool } QStringList fileList = unzipFile(path, tempDir); - qCDebug(scriptengine) << "Unzipped file list: " << fileList; QString filename = QUrl::fromLocalFile(fileList.first()).toString(); if (filename != "") { From 9d2be28a8a2c4d0cff2d1737fd090535e5db49bd Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 28 Jul 2017 10:21:03 -0700 Subject: [PATCH 264/280] troubleshooting Blocks injection --- scripts/system/html/js/marketplacesInject.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0ee337a2ef..0fbd9a778e 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -96,11 +96,13 @@ function injectBlocksCode() { // Make space for marketplaces footer in Blocks pages. - $("head").append( - '' + /*$("body").append( + '
    ' + + '' + + '
    ' + );*/ + $("body").append( + 'style= "bottom: 135px" ' ); } From 692aa4c4b9f05fced7f2110fa1d377d2eb048a9b Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Fri, 28 Jul 2017 15:13:24 -0700 Subject: [PATCH 265/280] Clara download working again, still can drag in Blocks zip --- interface/src/Application.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b1e91e0..963ef22482 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6267,14 +6267,12 @@ void Application::addAssetToWorld(QString filePath, QString zipFile, bool isBloc // Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget(). QString mapping; QString path = filePath; - QString filename = filenameFromPath(filePath); + QString filename = filenameFromPath(path); if (isBlocks) { QString assetFolder = zipFile.section("/", -1); assetFolder.remove(".zip"); - mapping = "/" + assetFolder + "/" + filenameFromPath(filename); + mapping = "/" + assetFolder + "/" + filename; } else { - path = QUrl(filePath).toLocalFile(); - filename = filenameFromPath(path); mapping = "/" + filename; } From 06ff76695288009a5f04241fc2df801001c59d91 Mon Sep 17 00:00:00 2001 From: Elisa Lupin-Jimenez Date: Wed, 2 Aug 2017 10:51:40 -0700 Subject: [PATCH 266/280] Drag zip cleaned up, marketplaces stylized, back button fixed --- interface/src/Application.cpp | 11 +++++---- interface/src/Application.h | 2 -- scripts/system/html/js/marketplacesInject.js | 16 +++++++++---- scripts/system/html/marketplaces.html | 24 +++++++++++--------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 963ef22482..90f4198383 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -229,8 +229,6 @@ static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; static const QString AVA_JSON_EXTENSION = ".ava.json"; static const QString WEB_VIEW_TAG = "noDownload=true"; - -// temporary zip handling for Emily static const QString ZIP_EXTENSION = ".zip"; static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; @@ -264,8 +262,6 @@ const QHash Application::_acceptedExtensi { JSON_EXTENSION, &Application::importJSONFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, { FST_EXTENSION, &Application::askToSetAvatarUrl }, - - // temporary zip handling for Emily { ZIP_EXTENSION, &Application::importFromZIP } }; @@ -6355,7 +6351,12 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q qWarning(interfaceapp) << "Error downloading model: " + errorInfo; addAssetToWorldError(filenameFromPath(filePath), errorInfo); } else { - addAssetToWorldAddEntity(filePath, mapping); + // to prevent files that aren't models from being loaded into world automatically + if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) { + addAssetToWorldAddEntity(filePath, mapping); + } else { + addAssetToWorldInfoDone(filenameFromPath(filePath)); + } } request->deleteLater(); }); diff --git a/interface/src/Application.h b/interface/src/Application.h index 033cca8452..6731cd8611 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -481,8 +481,6 @@ private: bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); - - // temporary zip handling for Emily bool importFromZIP(const QString& filePath); bool nearbyEntitiesAreReadyForPhysics(); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 0fbd9a778e..92fa869242 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -65,7 +65,10 @@ // Footer actions. $("#back-button").on("click", function () { - window.history.back(); + if (window.history.state != null) window.history.back(); + // to fix back button issue when in directory + //else window.location = "http://www.highfidelity.com/marketplace"; + else window.location = "https://metaverse.highfidelity.com/marketplace?"; }); $("#all-markets").on("click", function () { EventBridge.emitWebEvent(GOTO_DIRECTORY); @@ -79,9 +82,11 @@ letUsKnow.replaceWith(letUsKnow.html()); // Add button links. + + /* Blocks not yet implemented $('#exploreBlocksMarketplace').on('click', function () { window.location = "https://vr.google.com/objects"; - }); + });*/ $('#exploreClaraMarketplace').on('click', function () { window.location = "https://clara.io/library?gameCheck=true&public=true"; }); @@ -102,7 +107,7 @@ '
    ' );*/ $("body").append( - 'style= "bottom: 135px" ' + '

    hello

    ' ); } @@ -345,6 +350,7 @@ if (location.href.indexOf("google.com/") !== -1) { pageType = BLOCKS; } if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } + //if (pageType != BLOCKS) injectCommonCode(pageType === DIRECTORY); switch (pageType) { case DIRECTORY: @@ -354,7 +360,9 @@ injectHiFiCode(); break; case BLOCKS: - injectBlocksCode(); + console.log("in Blocks"); + //injectBlocksCode(); + console.log("blocks injection"); break; case CLARA: injectClaraCode(); diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 8c5fe15429..11c9e208ca 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -25,24 +25,26 @@