From 7022c4009fafa72fffa0fcf45aac9919557a280f Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 15:18:06 -0700 Subject: [PATCH 001/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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/148] 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 35f27313baeadb9ca3312f1c30ed55d84ac0c50d Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Jun 2017 12:05:44 -0400 Subject: [PATCH 014/148] persist command history to data folder; fix warning message color --- interface/src/ui/JSConsole.cpp | 32 ++++++++++++++++++++++++-------- interface/src/ui/JSConsole.h | 1 + 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 79314ce49a..820ed4207d 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -23,13 +23,14 @@ #include "ScriptHighlighting.h" const int NO_CURRENT_HISTORY_COMMAND = -1; -const int MAX_HISTORY_SIZE = 64; +const int MAX_HISTORY_SIZE = 256; +const QString HISTORY_FILENAME = "JSConsole.history.txt"; const QString COMMAND_STYLE = "color: #266a9b;"; const QString RESULT_SUCCESS_STYLE = "color: #677373;"; const QString RESULT_INFO_STYLE = "color: #223bd1;"; -const QString RESULT_WARNING_STYLE = "color: #d13b22;"; +const QString RESULT_WARNING_STYLE = "color: #999922;"; const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; @@ -37,14 +38,26 @@ const QString GUTTER_ERROR = "X"; const QString JSConsole::_consoleFileName { "about:console" }; +QList _readLines(const QString& filename) { + QFile file(filename); + file.open(QFile::ReadOnly); + return QTextStream(&file).readAll().split("\r\n"); +} + +void _writeLines(const QString& filename, const QList& lines) { + QFile file(filename); + file.open(QFile::WriteOnly); + QTextStream(&file) << lines.join("\r\n"); +} + JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), - _commandHistory(), + _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), + _commandHistory(_readLines(_savedHistoryFilename)), _ownScriptEngine(scriptEngine == NULL), _scriptEngine(NULL) { - _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -100,9 +113,12 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } void JSConsole::executeCommand(const QString& command) { - _commandHistory.prepend(command); - if (_commandHistory.length() > MAX_HISTORY_SIZE) { - _commandHistory.removeLast(); + if (_commandHistory.constFirst() != command) { + _commandHistory.prepend(command); + if (_commandHistory.length() > MAX_HISTORY_SIZE) { + _commandHistory.removeLast(); + } + _writeLines(_savedHistoryFilename, _commandHistory); } _ui->promptTextEdit->setDisabled(true); @@ -181,7 +197,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { // a new QTextBlock isn't created. keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier); } else { - QString command = _ui->promptTextEdit->toPlainText().trimmed(); + QString command = _ui->promptTextEdit->toPlainText().replace("\r\n","\n").trimmed(); if (!command.isEmpty()) { QTextCursor cursor = _ui->promptTextEdit->textCursor(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 864f847071..59280f65aa 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -63,6 +63,7 @@ private: QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; + QString _savedHistoryFilename; QList _commandHistory; // Keeps track if the script engine is created inside the JSConsole bool _ownScriptEngine; From c10475186c32bef0331a686cc3d5eb0c8603c457 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Jun 2017 18:48:29 -0400 Subject: [PATCH 015/148] 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 016/148] 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 017/148] 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 018/148] 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 019/148] 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 020/148] 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 021/148] 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 022/148] 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 023/148] 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 024/148] 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 025/148] 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 026/148] 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 027/148] 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 028/148] 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 029/148] 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 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 030/148] 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 031/148] 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 9c610f52eca11a94a637a9f408fb6f0c597a0628 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 15:34:41 -0400 Subject: [PATCH 032/148] switch history file format to JSON --- interface/src/ui/JSConsole.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 820ed4207d..ef38ea6d29 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -24,7 +24,7 @@ const int NO_CURRENT_HISTORY_COMMAND = -1; const int MAX_HISTORY_SIZE = 256; -const QString HISTORY_FILENAME = "JSConsole.history.txt"; +const QString HISTORY_FILENAME = "JSConsole.history.json"; const QString COMMAND_STYLE = "color: #266a9b;"; @@ -38,16 +38,25 @@ const QString GUTTER_ERROR = "X"; const QString JSConsole::_consoleFileName { "about:console" }; +const QString JSON_KEY = "entries"; QList _readLines(const QString& filename) { QFile file(filename); file.open(QFile::ReadOnly); - return QTextStream(&file).readAll().split("\r\n"); + auto json = QTextStream(&file).readAll().toUtf8(); + auto root = QJsonDocument::fromJson(json).object(); + // TODO: check root["version"] + return root[JSON_KEY].toVariant().toStringList(); } void _writeLines(const QString& filename, const QList& lines) { QFile file(filename); file.open(QFile::WriteOnly); - QTextStream(&file) << lines.join("\r\n"); + auto root = QJsonObject(); + root["version"] = 1.0; + root["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + root[JSON_KEY] = QJsonArray::fromStringList(lines); + auto json = QJsonDocument(root).toJson(); + QTextStream(&file) << json; } JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : From 4ad36ccec9bdf1bd8951fd41cfe15063409c1b32 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Jul 2017 16:21:28 -0700 Subject: [PATCH 033/148] Trying to resolve QML / Audio deadlocks --- interface/resources/qml/hifi/audio/Audio.qml | 52 +++++++++++--------- interface/src/scripting/Audio.cpp | 16 +++++- interface/src/scripting/Audio.h | 2 + interface/src/scripting/AudioDevices.cpp | 5 +- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 519499e35c..03d27e3831 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -117,26 +117,28 @@ Rectangle { delegate: Item { width: parent.width; height: 36; + + AudioControls.CheckBox { + id: checkbox + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + text: display; + wrap: false; + checked: selected; + enabled: false; + } - RowLayout { - width: parent.width; + MouseArea { + anchors.fill: checkbox + onClicked: Audio.setInputDevice(info); + } - AudioControls.CheckBox { - Layout.maximumWidth: parent.width - level.width - 40; - text: display; - wrap: false; - checked: selected; - onClicked: { - selected = checked; - checked = Qt.binding(function() { return selected; }); // restore binding - } - } - InputLevel { - id: level; - Layout.alignment: Qt.AlignRight; - Layout.rightMargin: 30; - visible: selected; - } + InputLevel { + id: level; + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 30 + visible: selected; } } } @@ -174,13 +176,19 @@ Rectangle { delegate: Item { width: parent.width; height: 36; + AudioControls.CheckBox { + id: checkbox + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left text: display; checked: selected; - onClicked: { - selected = checked; - checked = Qt.binding(function() { return selected; }); // restore binding - } + enabled: false; + } + + MouseArea { + anchors.fill: checkbox + onClicked: Audio.setOutputDevice(info); } } } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 8125f9a9f0..860fe4f2a5 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -133,4 +133,18 @@ void Audio::setReverb(bool enable) { void Audio::setReverbOptions(const AudioEffectOptions* options) { DependencyManager::get()->setReverbOptions(options); -} \ No newline at end of file +} + +void Audio::setInputDevice(const QAudioDeviceInfo& device) { + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(const QAudioDeviceInfo&, device)); +} + +void Audio::setOutputDevice(const QAudioDeviceInfo& device) { + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioOutput), + Q_ARG(const QAudioDeviceInfo&, device)); +} diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index ca89521489..acf101159b 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,6 +50,8 @@ public: void showMicMeter(bool show); void setInputVolume(float volume); + Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device); + Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device); Q_INVOKABLE void setReverb(bool enable); Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index e26ebac3f1..2813f75110 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -38,7 +38,8 @@ Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { QHash AudioDeviceList::_roles { { Qt::DisplayRole, "display" }, - { Qt::CheckStateRole, "selected" } + { Qt::CheckStateRole, "selected" }, + { Qt::UserRole, "info" } }; Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; @@ -51,6 +52,8 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { return _devices.at(index.row()).display; } else if (role == Qt::CheckStateRole) { return _devices.at(index.row()).selected; + } else if (role == Qt::UserRole) { + return QVariant::fromValue(_devices.at(index.row()).info); } else { return QVariant(); } From aeabfe84f0cec8ea0f908da96eeda84f3bbd277d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Jul 2017 18:54:52 -0700 Subject: [PATCH 034/148] Cleanup dead code, fix startup behavior --- interface/src/scripting/Audio.cpp | 10 +- interface/src/scripting/AudioDevices.cpp | 114 ++++++++--------------- interface/src/scripting/AudioDevices.h | 10 +- 3 files changed, 43 insertions(+), 91 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 860fe4f2a5..9719c23885 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -136,15 +136,9 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) { } void Audio::setInputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioInput), - Q_ARG(const QAudioDeviceInfo&, device)); + _devices.chooseInputDevice(device); } void Audio::setOutputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioOutput), - Q_ARG(const QAudioDeviceInfo&, device)); + _devices.chooseOutputDevice(device); } diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 2813f75110..a284e38dac 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -59,61 +59,17 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { } } -bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) { - if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) { - return false; - } - - // only allow switching to a new device, not deactivating an in-use device - auto selected = value.toBool(); - if (!selected) { - return false; - } - - return setDevice(index.row(), true); -} - -bool AudioDeviceList::setDevice(int row, bool fromUser) { - bool success = false; - auto& device = _devices[row]; - _userSelection = fromUser; - - // skip if already selected - if (!device.selected) { - auto client = DependencyManager::get(); - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, _mode), - Q_ARG(const QAudioDeviceInfo&, device.info)); - } - - emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); - return success; -} void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { - bool success { false }; - - // try to set the last selected device - if (!device.isNull()) { - auto i = 0; - for (; i < rowCount(); ++i) { - if (device == _devices[i].info.deviceName()) { - break; - } - } - if (i < rowCount()) { - success = setDevice(i, false); - } - - // the selection failed - reset it - if (!success) { - emit deviceSelected(); - } - } + auto client = DependencyManager::get().data(); + auto deviceName = getSetting(contextIsHMD, _mode).get(); + bool switchResult = false; + QMetaObject::invokeMethod(client, "switchAudioDevice", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, switchResult), + Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); // try to set to the default device for this mode - if (!success) { - auto client = DependencyManager::get().data(); + if (!switchResult) { if (contextIsHMD) { QString deviceName; if (_mode == QAudio::AudioInput) { @@ -144,11 +100,6 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { } } - if (_userSelection) { - _userSelection = false; - emit deviceSelected(_selectedDevice, oldDevice); - } - emit deviceChanged(_selectedDevice); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } @@ -183,13 +134,6 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput)); _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); - - connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { - onDeviceSelected(QAudio::AudioInput, device, previousDevice); - }); - connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { - onDeviceSelected(QAudio::AudioOutput, device, previousDevice); - }); } void AudioDevices::onContextChanged(const QString& context) { @@ -245,22 +189,40 @@ void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& de } void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { - static bool initialized { false }; - auto initialize = [&]{ - if (initialized) { - onContextChanged(QString()); - } else { - initialized = true; - } - }; - + static std::once_flag once; if (mode == QAudio::AudioInput) { _inputs.onDevicesChanged(devices); - static std::once_flag inputFlag; - std::call_once(inputFlag, initialize); } else { // if (mode == QAudio::AudioOutput) _outputs.onDevicesChanged(devices); - static std::once_flag outputFlag; - std::call_once(outputFlag, initialize); + } + std::call_once(once, [&] { onContextChanged(QString()); }); +} + + +void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) { + auto client = DependencyManager::get(); + bool success = false; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(const QAudioDeviceInfo&, device)); + + if (success) { + onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); + } +} + +void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) { + auto client = DependencyManager::get(); + bool success = false; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, QAudio::AudioOutput), + Q_ARG(const QAudioDeviceInfo&, device)); + + if (success) { + onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 8e82ddc4fb..a17c577535 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -37,14 +37,11 @@ public: // get/set devices through a QML ListView QVariant data(const QModelIndex& index, int role) const override; - bool setData(const QModelIndex& index, const QVariant &value, int role) override; // reset device to the last selected device in this context, or the default void resetDevice(bool contextIsHMD, const QString& device); signals: - void deviceSelected(const QAudioDeviceInfo& device = QAudioDeviceInfo(), - const QAudioDeviceInfo& previousDevice = QAudioDeviceInfo()); void deviceChanged(const QAudioDeviceInfo& device); private slots: @@ -54,12 +51,9 @@ private slots: private: friend class AudioDevices; - bool setDevice(int index, bool fromUser); - static QHash _roles; static Qt::ItemFlags _flags; - bool _userSelection { false }; - QAudio::Mode _mode; + const QAudio::Mode _mode; QAudioDeviceInfo _selectedDevice; QList _devices; }; @@ -73,6 +67,8 @@ class AudioDevices : public QObject { public: AudioDevices(bool& contextIsHMD); + void chooseInputDevice(const QAudioDeviceInfo& device); + void chooseOutputDevice(const QAudioDeviceInfo& device); signals: void nop(); From 02363e06de87f32a9997d4df62deba6c99a30007 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 18 Jul 2017 08:58:17 -0700 Subject: [PATCH 035/148] Ensure input and output device containers are protected by mutex --- libraries/audio-client/src/AudioClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 3d9b1de10f..bc02da1cc4 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -92,6 +92,7 @@ void AudioClient::checkDevices() { auto inputDevices = getAvailableDevices(QAudio::AudioInput); auto outputDevices = getAvailableDevices(QAudio::AudioOutput); + Lock lock(_deviceMutex); if (inputDevices != _inputDevices) { _inputDevices.swap(inputDevices); emit devicesChanged(QAudio::AudioInput, _inputDevices); From 4b94d24c2922a3028cf9898f367b6b7077530260 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 18 Jul 2017 23:51:19 +0200 Subject: [PATCH 036/148] Change default mouse speeds for avatar --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f9a4d491c8..a6958c9089 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -69,8 +69,8 @@ const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets add const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this -const float YAW_SPEED_DEFAULT = 120.0f; // degrees/sec -const float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec +const float YAW_SPEED_DEFAULT = 60.0f; // degrees/sec +const float PITCH_SPEED_DEFAULT = 45.0f; // degrees/sec // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. From 009df176c511a7acde67a4baaa709263bfe0936d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 18 Jul 2017 13:44:58 -0700 Subject: [PATCH 037/148] Ensure we continue calling idle when minimized Conflicts: interface/src/Application.cpp --- interface/src/Application.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a214068239..0cb4b90174 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2742,6 +2742,16 @@ bool Application::event(QEvent* event) { static_cast(event)->call(); return true; + // 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(); From c56450b986e59270730b1de48df087fa5f35ce39 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Wed, 19 Jul 2017 01:49:39 -0700 Subject: [PATCH 038/148] 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 a8698c2fbc164071320a64df805e64ca50ad9dde Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Jul 2017 08:59:09 -0700 Subject: [PATCH 039/148] EntityScriptingInterface::getMeshes support for Model entities --- .../src/RenderableModelEntityItem.cpp | 7 ++++ .../src/RenderableModelEntityItem.h | 2 ++ libraries/model/src/model/Geometry.cpp | 2 +- libraries/model/src/model/Geometry.h | 2 +- libraries/render-utils/src/Model.cpp | 35 +++++++++++++++++++ libraries/render-utils/src/Model.h | 2 ++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 0b6271a6b1..5c0c6101a3 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1282,3 +1282,10 @@ void RenderableModelEntityItem::mapJoints(const QStringList& modelJointNames) { } } } + +bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { + if (!_model || !_model->isLoaded()) { + return false; + } + return _model->getMeshes(result); +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 2d240c01a6..a5ba7bedda 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -116,6 +116,8 @@ public: return _animation; } + bool getMeshes(MeshProxyList& result) override; + private: QVariantMap parseTexturesToMap(QString textures); void remapTextures(); diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index f88c8233ea..ac4c5dc188 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -137,7 +137,7 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { model::MeshPointer Mesh::map(std::function vertexFunc, std::function normalFunc, - std::function indexFunc) { + std::function indexFunc) const { // vertex data const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index a3198eed26..f273435545 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -124,7 +124,7 @@ public: // create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions MeshPointer map(std::function vertexFunc, std::function normalFunc, - std::function indexFunc); + std::function indexFunc) const; void forEach(std::function vertexFunc, std::function normalFunc, diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 45be09b701..5e3c9c8ecd 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -462,6 +463,40 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } +bool Model::getMeshes(MeshProxyList& result) { + const Geometry::Pointer& renderGeometry = getGeometry(); + const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); + + if (!isLoaded()) { + return false; + } + + Transform offset; + offset.setScale(_scale); + // not set -- far to the right + // offset.postTranslate(_offset); // far to right + // offset.postTranslate(-_offset); // a bit to left + glm::mat4 offsetMat = offset.getMatrix(); + + for (std::shared_ptr mesh : meshes) { + if (!mesh) { + continue; + } + + MeshProxy* meshProxy = new SimpleMeshProxy( + mesh->map([=](glm::vec3 position) { + const glm::vec3 DEFAULT_ENTITY_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); + glm::vec3 regis = _registrationPoint - DEFAULT_ENTITY_REGISTRATION_POINT; + return glm::vec3(offsetMat * glm::vec4(position + _offset, 1.0f)) + regis; // very close + }, + [=](glm::vec3 normal){ return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); }, + [&](uint32_t index){ return index; })); + result << meshProxy; + } + + return true; +} + void Model::calculateTriangleSets() { PROFILE_RANGE(render, __FUNCTION__); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3eb796b763..1738af661b 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -257,6 +257,8 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } + bool getMeshes(MeshProxyList& result); + public slots: void loadURLFinished(bool success); From d9c6126000765f1783b36997831cf9cf80ac2c54 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Jul 2017 13:33:32 -0700 Subject: [PATCH 040/148] 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 041/148] 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 750b7192afb70e582e4978f6e7bced1d07b6f488 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Jul 2017 13:58:03 -0700 Subject: [PATCH 042/148] trying to get the resulting model to line up with the originals --- .../entities/src/EntityScriptingInterface.cpp | 2 +- libraries/render-utils/src/Model.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2cefd647cb..1dc3cf8f75 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1738,7 +1738,7 @@ glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - entity->getRegistrationPoint()); - result = translation * rotation * registration; + result = translation * rotation /* * registration */; } }); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5e3c9c8ecd..6219e83bf1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -473,11 +473,16 @@ bool Model::getMeshes(MeshProxyList& result) { Transform offset; offset.setScale(_scale); - // not set -- far to the right - // offset.postTranslate(_offset); // far to right - // offset.postTranslate(-_offset); // a bit to left + // offset.postTranslate(_offset); glm::mat4 offsetMat = offset.getMatrix(); + Extents modelExtents = getUnscaledMeshExtents(); + glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; + + const glm::vec3 DEFAULT_ENTITY_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); + glm::vec3 regis = (DEFAULT_ENTITY_REGISTRATION_POINT - _registrationPoint); + glm::vec3 regisOffset = dimensions * regis; + for (std::shared_ptr mesh : meshes) { if (!mesh) { continue; @@ -485,9 +490,7 @@ bool Model::getMeshes(MeshProxyList& result) { MeshProxy* meshProxy = new SimpleMeshProxy( mesh->map([=](glm::vec3 position) { - const glm::vec3 DEFAULT_ENTITY_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); - glm::vec3 regis = _registrationPoint - DEFAULT_ENTITY_REGISTRATION_POINT; - return glm::vec3(offsetMat * glm::vec4(position + _offset, 1.0f)) + regis; // very close + return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); }, [=](glm::vec3 normal){ return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); }, [&](uint32_t index){ return index; })); From be48268be827edecf9702b556353cd5f0928dcbb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Jul 2017 15:02:40 -0700 Subject: [PATCH 043/148] getMeshes handles registration point --- libraries/entities/src/EntityScriptingInterface.cpp | 8 ++------ libraries/render-utils/src/Model.cpp | 9 +-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 1dc3cf8f75..9d2cb30c6e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1736,9 +1736,7 @@ glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { if (entity) { glm::mat4 translation = glm::translate(entity->getPosition()); glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); - glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - - entity->getRegistrationPoint()); - result = translation * rotation /* * registration */; + result = translation * rotation; } }); } @@ -1753,9 +1751,7 @@ glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityI if (entity) { glm::mat4 translation = glm::translate(entity->getLocalPosition()); glm::mat4 rotation = glm::mat4_cast(entity->getLocalOrientation()); - glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - - entity->getRegistrationPoint()); - result = translation * rotation * registration; + result = translation * rotation; } }); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6219e83bf1..e38a556487 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -473,16 +473,9 @@ bool Model::getMeshes(MeshProxyList& result) { Transform offset; offset.setScale(_scale); - // offset.postTranslate(_offset); + offset.postTranslate(_offset); glm::mat4 offsetMat = offset.getMatrix(); - Extents modelExtents = getUnscaledMeshExtents(); - glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum; - - const glm::vec3 DEFAULT_ENTITY_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); - glm::vec3 regis = (DEFAULT_ENTITY_REGISTRATION_POINT - _registrationPoint); - glm::vec3 regisOffset = dimensions * regis; - for (std::shared_ptr mesh : meshes) { if (!mesh) { continue; From 3936ee93f0e08da594a3279fe7505c09994658f5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Jul 2017 16:34:04 -0700 Subject: [PATCH 044/148] use default scripts path in EditTabView to fix load on OS X --- .../resources/qml/hifi/tablet/EditTabView.qml | 20 +++++++++---------- libraries/shared/src/PathUtils.h | 3 +++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index e4a20a0316..e94325f399 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -21,7 +21,7 @@ TabView { enabled: true property string originalUrl: "" - Rectangle { + Rectangle { color: "#404040" Text { @@ -180,7 +180,7 @@ TabView { WebView { id: entityListToolWebView - url: "../../../../../scripts/system/html/entityList.html" + url: Paths.defaultScripts + "/system/html/entityList.html" anchors.fill: parent enabled: true } @@ -194,7 +194,7 @@ TabView { WebView { id: entityPropertiesWebView - url: "../../../../../scripts/system/html/entityProperties.html" + url: Paths.defaultScripts + "/system/html/entityProperties.html" anchors.fill: parent enabled: true } @@ -208,7 +208,7 @@ TabView { WebView { id: gridControlsWebView - url: "../../../../../scripts/system/html/gridControls.html" + url: Paths.defaultScripts + "/system/html/gridControls.html" anchors.fill: parent enabled: true } @@ -222,7 +222,7 @@ TabView { WebView { id: particleExplorerWebView - url: "../../../../../scripts/system/particle_explorer/particleExplorer.html" + url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" anchors.fill: parent enabled: true } @@ -293,16 +293,16 @@ TabView { break; case 'list': editTabView.currentIndex = 1; - break; + break; case 'properties': editTabView.currentIndex = 2; - break; + break; case 'grid': editTabView.currentIndex = 3; - break; + break; case 'particle': editTabView.currentIndex = 4; - break; + break; default: console.warn('Attempt to switch to invalid tab:', id); } @@ -310,4 +310,4 @@ TabView { console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id)); } } -} \ No newline at end of file +} diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 14eb81dd9a..577c215c9e 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -13,6 +13,8 @@ #define hifi_PathUtils_h #include +#include + #include "DependencyManager.h" /**jsdoc @@ -24,6 +26,7 @@ class PathUtils : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY Q_PROPERTY(QString resources READ resourcesPath) + Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation) public: static const QString& resourcesPath(); From 0a64a1e3b0dff67cf5c1452cbe659f25bc399145 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Wed, 19 Jul 2017 18:26:35 -0700 Subject: [PATCH 045/148] 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 4f1489f8b43b7b97b63bba044e8667876dcd9ec0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:18:19 -0700 Subject: [PATCH 046/148] 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 d88d7dda2bb1d2a0460fc16a6f37411b245754c6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:19:59 -0700 Subject: [PATCH 047/148] 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 048/148] 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 3805228b70635e4c18332fd1f941bffb8d6539a5 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 20 Jul 2017 16:26:51 +0100 Subject: [PATCH 049/148] fixed grab the tablet in third person --- scripts/system/controllers/handControllerGrab.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index e2de13d4b1..6b671d366f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1034,9 +1034,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp) { function getControllerJointIndex(hand) { if (HMD.isHandControllerAvailable()) { - return MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CONTROLLER_RIGHTHAND" : - "_CONTROLLER_LEFTHAND"); + var controllerJointIndex = -1; + if (Camera.mode === "first person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); + } else if (Camera.mode === "third person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + } + + return controllerJointIndex; } return MyAvatar.getJointIndex("Head"); From fcd3126b85c2f8687d0ffc4f1148720aa28fa747 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:32:48 -0700 Subject: [PATCH 050/148] 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 051/148] 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 052/148] 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 053/148] 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 054/148] 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 3a31805dff12d499be937c9f1a798cf1e0720e69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Jun 2017 17:12:25 -0700 Subject: [PATCH 055/148] 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 7ddac54b37f773f9fc96f9da3bb651ad9f2da2c0 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 13:55:04 -0700 Subject: [PATCH 056/148] 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 a8dac0cb79b49b2646d34780a43374b6322737e0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 15:45:50 -0700 Subject: [PATCH 057/148] 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 058/148] 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 72e5fecb4e6feb98786651a7dd04df66504e38ab Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 20 Jul 2017 16:34:34 -0700 Subject: [PATCH 059/148] do Model::getMeshes on the correct thread. added ModelScriptingInterface::getVertexCount and ModelScriptingInterface::getVertex --- .../src/RenderableModelEntityItem.cpp | 4 ++- libraries/render-utils/src/Model.cpp | 7 ++-- libraries/render-utils/src/Model.h | 2 +- .../src/ModelScriptingInterface.cpp | 35 +++++++++++++++++++ .../src/ModelScriptingInterface.h | 2 ++ 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5c0c6101a3..9884debcce 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" @@ -1287,5 +1288,6 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { if (!_model || !_model->isLoaded()) { return false; } - return _model->getMeshes(result); + BLOCKING_INVOKE_METHOD(_model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); + return !result.isEmpty(); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e38a556487..3755ca7cfc 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -463,12 +463,13 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } -bool Model::getMeshes(MeshProxyList& result) { +MeshProxyList Model::getMeshes() { + MeshProxyList result; const Geometry::Pointer& renderGeometry = getGeometry(); const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); if (!isLoaded()) { - return false; + return result; } Transform offset; @@ -490,7 +491,7 @@ bool Model::getMeshes(MeshProxyList& result) { result << meshProxy; } - return true; + return result; } void Model::calculateTriangleSets() { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1738af661b..da8677aeed 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -257,7 +257,7 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - bool getMeshes(MeshProxyList& result); + Q_INVOKABLE MeshProxyList getMeshes(); public slots: void loadURLFinished(bool success); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 762d9ffb29..64fb365d0c 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -138,6 +138,41 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro return meshToScriptValue(_modelScriptEngine, resultProxy); } +QScriptValue ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) { + if (!meshProxy) { + return QScriptValue(false); + } + MeshPointer mesh = meshProxy->getMeshPointer(); + if (!mesh) { + return QScriptValue(false); + } + + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + + return numVertices; +} + +QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertexIndex) { + if (!meshProxy) { + return QScriptValue(false); + } + MeshPointer mesh = meshProxy->getMeshPointer(); + if (!mesh) { + return QScriptValue(false); + } + + const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + + if (vertexIndex < 0 || vertexIndex >= numVertices) { + return QScriptValue(false); + } + + glm::vec3 pos = vertexBufferView.get(vertexIndex); + return vec3toScriptValue(_modelScriptEngine, pos); +} + + QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices, const QVector& normals, const QVector& faces) { diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index ba23623acf..3c239f006f 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -29,6 +29,8 @@ public: Q_INVOKABLE QScriptValue newMesh(const QVector& vertices, const QVector& normals, const QVector& faces); + Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy); + Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex); private: QScriptEngine* _modelScriptEngine { nullptr }; From 649b974927e0deff5156d1333555b40206e4773d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 21 Jul 2017 01:01:23 +0100 Subject: [PATCH 060/148] 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 061/148] 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 26cc8134eba5d1e1a49ef0a3a7778efdea631968 Mon Sep 17 00:00:00 2001 From: Leander Hasty <1p-cusack@1stplayable.com> Date: Fri, 21 Jul 2017 10:56:24 -0400 Subject: [PATCH 062/148] WL21463 - entitySelectionTool: use multiarg findRayIntersection instead of editOverlay. We're seeing the ignoreRayIntersection flag not take effect before findRayIntersection calls. This may be due to editOverlay and editOverlays becoming non-blocking in 1f7d2b2 . This altered the flow in mousePressEvent significantly; the first block, intended to handle scale/clone only, started handling rotation (should have been second block) and sometimes selection (should have been third block). Similarly, in the various rotate grabbers' onMove methods, the pickRay will no longer intersect anything other than rotateOverlayTarget; this avoids some awful behavior when scrubbing over the size and clone grabbers. This also reverts unnecessary parts of the prior commits to keep the diff for this WL cleaner, and adds a few TODO comments to revisit about redundant statements and incorrect names. In addition, we've noticed but not fixed herein: * There is a minor edgecase near 0 and 180, where it's difficult to get within a degree or two of the poles occasionally. * The scale/clone grabbers don't stay disappeared for rotation in some cases. This doesn't impact usability anymore, but it'd be nice to determine why they come back when they hide briefly. * The addGrabbers for yaw/pitch/roll could be deduplicated, and yaw has some multiselect "reposition" enable/disable logic that pitch and roll lack. Reviewed-by: LaShonda Hopper --- .../system/libraries/entitySelectionTool.js | 61 ++++--------------- 1 file changed, 12 insertions(+), 49 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 725803f824..2d1853fae2 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1560,7 +1560,6 @@ SelectionDisplay = (function() { visible: rotationOverlaysVisible }); - // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, @@ -3615,24 +3614,21 @@ SelectionDisplay = (function() { onMove: function(event) { var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { - ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { - ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { - ignoreRayIntersection: false - }); - var result = Overlays.findRayIntersection(pickRay); + var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]); if (result.intersects) { var center = yawCenter; var zero = yawZero; + // TODO: these vectors are backwards to their names, doesn't matter for this use case (inverted vectors still give same angle) var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); + // TODO: orientedAngle wants normalized centerToZero and centerToIntersect var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); var snapToInner = distanceFromCenter < innerRadius; @@ -3785,17 +3781,12 @@ SelectionDisplay = (function() { onMove: function(event) { var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { - ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { - ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { - ignoreRayIntersection: false - }); - var result = Overlays.findRayIntersection(pickRay); + var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]); if (result.intersects) { var properties = Entities.getEntityProperties(selectionManager.selections[0]); @@ -3947,17 +3938,12 @@ SelectionDisplay = (function() { onMove: function(event) { var pickRay = generalComputePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { - ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(baseOfEntityProjectionOverlay, { - ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { - ignoreRayIntersection: false - }); - var result = Overlays.findRayIntersection(pickRay); + var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]); if (result.intersects) { var properties = Entities.getEntityProperties(selectionManager.selections[0]); @@ -4074,21 +4060,8 @@ SelectionDisplay = (function() { return false; } - // before we do a ray test for grabbers, disable the ray intersection for our selection box - Overlays.editOverlay(selectionBox, { - ignoreRayIntersection: true - }); - Overlays.editOverlay(yawHandle, { - ignoreRayIntersection: true - }); - Overlays.editOverlay(pitchHandle, { - ignoreRayIntersection: true - }); - Overlays.editOverlay(rollHandle, { - ignoreRayIntersection: true - }); - - result = Overlays.findRayIntersection(pickRay); + // ignore ray intersection for our selection box and yaw/pitch/roll + result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] ); if (result.intersects) { if (wantDebug) { print("something intersects... "); @@ -4191,17 +4164,8 @@ SelectionDisplay = (function() { } - // After testing our stretch handles, then check out rotate handles - Overlays.editOverlay(yawHandle, { - ignoreRayIntersection: false - }); - Overlays.editOverlay(pitchHandle, { - ignoreRayIntersection: false - }); - Overlays.editOverlay(rollHandle, { - ignoreRayIntersection: false - }); - var result = Overlays.findRayIntersection(pickRay); + // Only intersect versus yaw/pitch/roll. + var result = Overlays.findRayIntersection(pickRay, true, [ yawHandle, pitchHandle, rollHandle ] ); var overlayOrientation; var overlayCenter; @@ -4306,6 +4270,7 @@ SelectionDisplay = (function() { }); + // TODO: these three duplicate prior three, remove them. Overlays.editOverlay(yawHandle, { visible: false }); @@ -4402,10 +4367,8 @@ SelectionDisplay = (function() { } if (!somethingClicked) { - Overlays.editOverlay(selectionBox, { - ignoreRayIntersection: false - }); - var result = Overlays.findRayIntersection(pickRay); + // Only intersect versus selectionBox. + var result = Overlays.findRayIntersection(pickRay, true, [selectionBox]); if (result.intersects) { switch (result.overlayID) { case selectionBox: From 33a8ab2d58eead3f286ccf3763fd242cb215a613 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 21 Jul 2017 18:18:23 +0200 Subject: [PATCH 063/148] Adjusted according discussion in PR --- .../resources/controllers/keyboardMouse.json | 21 ++++++++++++++++--- interface/src/avatar/MyAvatar.cpp | 4 ++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8baf56684a..2faabf0f91 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -81,7 +81,11 @@ { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, "when": "Keyboard.RightMouseButton", - "to": "Actions.Yaw" + "to": "Actions.Yaw", + "filters": + [ + { "type": "scale", "scale": 0.1 } + ] }, { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, @@ -102,8 +106,19 @@ { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP", + "filters": + [ + { "type": "scale", "scale": 0.1 } + ] + + }, + { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN", + "filters": + [ + { "type": "scale", "scale": 0.1 } + ] + }, { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a6958c9089..b32ef4024e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -69,8 +69,8 @@ const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets add const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this -const float YAW_SPEED_DEFAULT = 60.0f; // degrees/sec -const float PITCH_SPEED_DEFAULT = 45.0f; // degrees/sec +const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec +const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. From d1c2218a31e52305dbabfa151e4499c90b3a631c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Jul 2017 17:06:36 -0700 Subject: [PATCH 064/148] Temporary patch for deadlock --- interface/src/scripting/AudioDevices.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index a284e38dac..3416366558 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -63,6 +63,14 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { auto client = DependencyManager::get().data(); auto deviceName = getSetting(contextIsHMD, _mode).get(); + + // FIXME hack to prevent deadlock on startup. The real fix will be to have the + // audio client emit success and failure messages in response to audio device + // switches made here +#if 1 + QMetaObject::invokeMethod(client, "switchAudioDevice", + Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); +#else bool switchResult = false; QMetaObject::invokeMethod(client, "switchAudioDevice", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, switchResult), @@ -85,6 +93,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode)); } } +#endif } void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { @@ -201,6 +210,14 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList(); + + // FIXME hack to prevent deadlock on startup +#if 1 + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(const QAudioDeviceInfo&, device)); + onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); +#else bool success = false; QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, @@ -211,6 +228,7 @@ void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) { if (success) { onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); } +#endif } void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) { From 5b5e94700ecc20e0cfd939bec833e8f14a4e187d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Jul 2017 18:17:09 -0700 Subject: [PATCH 065/148] Additional audio fixes --- interface/src/scripting/AudioDevices.cpp | 70 +++++++++++------------- interface/src/scripting/AudioDevices.h | 6 +- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 3416366558..d02f4d8fcf 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -36,6 +36,21 @@ Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { } } +static QString getTargetDevice(bool hmd, QAudio::Mode mode) { + QString deviceName; + auto& setting = getSetting(hmd, mode); + if (setting.isSet()) { + deviceName = setting.get(); + } else if (hmd) { + if (mode == QAudio::AudioInput) { + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice(); + } else { // if (_mode == QAudio::AudioOutput) + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); + } + } + return deviceName; +} + QHash AudioDeviceList::_roles { { Qt::DisplayRole, "display" }, { Qt::CheckStateRole, "selected" }, @@ -59,18 +74,15 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { } } - -void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { +void AudioDeviceList::resetDevice(bool contextIsHMD) { auto client = DependencyManager::get().data(); - auto deviceName = getSetting(contextIsHMD, _mode).get(); - - // FIXME hack to prevent deadlock on startup. The real fix will be to have the - // audio client emit success and failure messages in response to audio device - // switches made here -#if 1 + QString deviceName = getTargetDevice(contextIsHMD, _mode); + // FIXME can't use blocking connections here, so we can't determine whether the switch succeeded or not + // We need to have the AudioClient emit signals on switch success / failure QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); -#else + +#if 0 bool switchResult = false; QMetaObject::invokeMethod(client, "switchAudioDevice", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, switchResult), @@ -146,11 +158,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { } void AudioDevices::onContextChanged(const QString& context) { - auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get(); - auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get(); - - _inputs.resetDevice(_contextIsHMD, input); - _outputs.resetDevice(_contextIsHMD, output); + _inputs.resetDevice(_contextIsHMD); + _outputs.resetDevice(_contextIsHMD); } void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { @@ -191,8 +200,16 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) { if (mode == QAudio::AudioInput) { + if (_requestedInputDevice == device) { + onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); + _requestedInputDevice = QAudioDeviceInfo(); + } _inputs.onDeviceChanged(device); } else { // if (mode == QAudio::AudioOutput) + if (_requestedOutputDevice == device) { + onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); + _requestedOutputDevice = QAudioDeviceInfo(); + } _outputs.onDeviceChanged(device); } } @@ -210,37 +227,16 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList(); - - // FIXME hack to prevent deadlock on startup -#if 1 + _requestedInputDevice = device; QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(const QAudioDeviceInfo&, device)); - onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); -#else - bool success = false; - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), - Q_ARG(QAudio::Mode, QAudio::AudioInput), - Q_ARG(const QAudioDeviceInfo&, device)); - - if (success) { - onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); - } -#endif } void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) { auto client = DependencyManager::get(); - bool success = false; + _requestedOutputDevice = device; QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, success), Q_ARG(QAudio::Mode, QAudio::AudioOutput), Q_ARG(const QAudioDeviceInfo&, device)); - - if (success) { - onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); - } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index a17c577535..3278a53374 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -39,7 +39,7 @@ public: QVariant data(const QModelIndex& index, int role) const override; // reset device to the last selected device in this context, or the default - void resetDevice(bool contextIsHMD, const QString& device); + void resetDevice(bool contextIsHMD); signals: void deviceChanged(const QAudioDeviceInfo& device); @@ -87,8 +87,10 @@ private: AudioDeviceList _inputs { QAudio::AudioInput }; AudioDeviceList _outputs { QAudio::AudioOutput }; + QAudioDeviceInfo _requestedOutputDevice; + QAudioDeviceInfo _requestedInputDevice; - bool& _contextIsHMD; + const bool& _contextIsHMD; }; }; From 1e6fc85e3e271fd5ebab64aa1124b5d7ed21b148 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 10:17:10 -0700 Subject: [PATCH 066/148] 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 067/148] 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 068/148] 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 069/148] 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 615a3c52e4f74599912021bd6e067b8ad1a35f47 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 21 Jul 2017 21:26:33 +0200 Subject: [PATCH 070/148] Ooops.. --- interface/resources/controllers/keyboardMouse.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 2faabf0f91..c384817ff6 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -84,7 +84,7 @@ "to": "Actions.Yaw", "filters": [ - { "type": "scale", "scale": 0.1 } + { "type": "scale", "scale": 0.6 } ] }, @@ -109,14 +109,14 @@ { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP", "filters": [ - { "type": "scale", "scale": 0.1 } + { "type": "scale", "scale": 0.6 } ] }, { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN", "filters": [ - { "type": "scale", "scale": 0.1 } + { "type": "scale", "scale": 0.6 } ] }, From 06b4c53d28f0c843ab8b927bbe8ba94695dc15b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Sat, 22 Jul 2017 00:15:26 +0100 Subject: [PATCH 071/148] 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 5e04e849535f484fc60902878886ded483f7fc34 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 22 Jul 2017 11:57:51 -0700 Subject: [PATCH 072/148] make OBJ reader able to handle files where some vertices have colors and others don't --- libraries/fbx/src/OBJReader.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index a171f92907..5ec6a9023d 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -118,7 +118,7 @@ glm::vec3 OBJTokenizer::getVec3() { auto z = getFloat(); auto v = glm::vec3(x, y, z); while (isNextTokenFloat()) { - // the spec(s) get(s) vague here. might be w, might be a color... chop it off. + // ignore any following floats nextToken(); } return v; @@ -130,6 +130,7 @@ bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { auto y = getFloat(); // And order of arguments is different on Windows/Linux. auto z = getFloat(); vertex = glm::vec3(x, y, z); + vertexColor = glm::vec3(1.0f); // default, in case there is not color information auto r = 1.0f, g = 1.0f, b = 1.0f; bool hasVertexColor = false; @@ -139,7 +140,7 @@ bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { // only a single value) that it's a vertex color. r = getFloat(); if (isNextTokenFloat()) { - // Safe to assume the following values are the green/blue components. + // Safe to assume the following values are the green/blue components. g = getFloat(); b = getFloat(); @@ -351,6 +352,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi bool result = true; int originalFaceCountForDebugging = 0; QString currentGroup; + bool anyVertexColor { false }; + int vertexCount { 0 }; setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count())); @@ -416,10 +419,20 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi bool hasVertexColor = tokenizer.getVertex(vertex, vertexColor); vertices.append(vertex); - - if(hasVertexColor) { + + // if any vertex has color, they all need to. + if (hasVertexColor && !anyVertexColor) { + // we've had a gap of zero or more vertices without color, followed + // by one that has color. catch up: + for (int i = 0; i < vertexCount; i++) { + vertexColors.append(glm::vec3(1.0f)); + } + anyVertexColor = true; + } + if (anyVertexColor) { vertexColors.append(vertexColor); } + vertexCount++; } else if (token == "vn") { normals.append(tokenizer.getVec3()); } else if (token == "vt") { From 6f86b266cbd8a972c8bf60ecd84d9f647c46c487 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 23 Jul 2017 08:04:34 -0700 Subject: [PATCH 073/148] support writing of vertex colors --- libraries/fbx/src/OBJWriter.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 034263eb53..e0d3d36b06 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -40,8 +40,6 @@ static QString formatFloat(double n) { } bool writeOBJToTextStream(QTextStream& out, QList meshes) { - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, // so keep track of the start index for each mesh. QList meshVertexStartOffset; @@ -49,10 +47,15 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { int currentVertexStartOffset = 0; int currentNormalStartOffset = 0; - // write out vertices + // write out vertices (and maybe colors) foreach (const MeshPointer& mesh, meshes) { meshVertexStartOffset.append(currentVertexStartOffset); const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); + + const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(gpu::Stream::COLOR); + gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); + gpu::BufferView::Index colorIndex = 0; + int vertexCount = 0; gpu::BufferView::Iterator vertexItr = vertexBuffer.cbegin(); while (vertexItr != vertexBuffer.cend()) { @@ -60,7 +63,15 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << "v "; out << formatFloat(v[0]) << " "; out << formatFloat(v[1]) << " "; - out << formatFloat(v[2]) << "\n"; + out << formatFloat(v[2]); + if (colorIndex < numColors) { + glm::vec3 color = colorsBufferView.get(colorIndex); + out << " " << formatFloat(color[0]); + out << " " << formatFloat(color[1]); + out << " " << formatFloat(color[2]); + colorIndex++; + } + out << "\n"; vertexItr++; vertexCount++; } @@ -72,7 +83,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { bool haveNormals = true; foreach (const MeshPointer& mesh, meshes) { meshNormalStartOffset.append(currentNormalStartOffset); - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); + const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i++) { glm::vec3 normal = normalsBufferView.get(i); From 6e0394865e9ee2f2abc722be2bb6236d77fef53d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 23 Jul 2017 08:05:09 -0700 Subject: [PATCH 074/148] fix bug in getNumElements that kept it from working on buffer-views with a non-zero offset --- libraries/gpu/src/gpu/Buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu/src/gpu/Buffer.h b/libraries/gpu/src/gpu/Buffer.h index 2eb2267f0d..c5df94235c 100644 --- a/libraries/gpu/src/gpu/Buffer.h +++ b/libraries/gpu/src/gpu/Buffer.h @@ -192,7 +192,7 @@ public: BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT); BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT); - Size getNumElements() const { return (_size - _offset) / _stride; } + Size getNumElements() const { return _size / _stride; } //Template iterator with random access on the buffer sysmem template From 47ea32f4d3ce94399f37547e25149e9d6f3a143a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 23 Jul 2017 08:06:26 -0700 Subject: [PATCH 075/148] vertex color support in ModelScriptingInterface --- .../src/RenderablePolyVoxEntityItem.cpp | 1 + libraries/model/src/model/Geometry.cpp | 37 ++++++++++++++++++- libraries/model/src/model/Geometry.h | 2 + libraries/render-utils/src/Model.cpp | 12 +++--- .../src/ModelScriptingInterface.cpp | 28 ++++++++++++-- 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1d309a8e14..6687d4e721 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1663,6 +1663,7 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { // the mesh will be in voxel-space. transform it into object-space meshProxy = new SimpleMeshProxy( _mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [=](glm::vec3 color){ return color; }, [=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); }, [&](uint32_t index){ return index; })); result << meshProxy; diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index ac4c5dc188..5a8f4de0ca 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -11,8 +11,6 @@ #include "Geometry.h" -#include - using namespace model; Mesh::Mesh() : @@ -136,11 +134,13 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { model::MeshPointer Mesh::map(std::function vertexFunc, + std::function colorFunc, std::function normalFunc, std::function indexFunc) const { // vertex data const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); + gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3); unsigned char* resultVertexData = new unsigned char[vertexSize]; unsigned char* vertexDataCursor = resultVertexData; @@ -151,6 +151,23 @@ model::MeshPointer Mesh::map(std::function vertexFunc, vertexDataCursor += sizeof(pos); } + // color data + // static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; + // int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + int attributeTypeColor = gpu::Stream::COLOR; + const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); + gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); + + gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3); + unsigned char* resultColorData = new unsigned char[colorSize]; + unsigned char* colorDataCursor = resultColorData; + + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + glm::vec3 color = colorFunc(colorsBufferView.get(i)); + memcpy(colorDataCursor, &color, sizeof(color)); + colorDataCursor += sizeof(color); + } + // normal data int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); @@ -187,6 +204,12 @@ model::MeshPointer Mesh::map(std::function vertexFunc, gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); result->setVertexBuffer(resultVertexBufferView); + gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData); + gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer); + gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement); + result->addAttribute(attributeTypeColor, resultColorsBufferView); + gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); @@ -215,6 +238,7 @@ model::MeshPointer Mesh::map(std::function vertexFunc, void Mesh::forEach(std::function vertexFunc, + std::function colorFunc, std::function normalFunc, std::function indexFunc) { // vertex data @@ -224,6 +248,15 @@ void Mesh::forEach(std::function vertexFunc, vertexFunc(vertexBufferView.get(i)); } + // color data + int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + // int attributeTypeColor = gpu::Stream::COLOR; + const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); + gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + colorFunc(colorsBufferView.get(i)); + } + // normal data int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); diff --git a/libraries/model/src/model/Geometry.h b/libraries/model/src/model/Geometry.h index f273435545..260de313ab 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/model/src/model/Geometry.h @@ -123,10 +123,12 @@ public: // create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions MeshPointer map(std::function vertexFunc, + std::function colorFunc, std::function normalFunc, std::function indexFunc) const; void forEach(std::function vertexFunc, + std::function colorFunc, std::function normalFunc, std::function indexFunc); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3755ca7cfc..917d284f86 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -483,11 +483,13 @@ MeshProxyList Model::getMeshes() { } MeshProxy* meshProxy = new SimpleMeshProxy( - mesh->map([=](glm::vec3 position) { - return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); - }, - [=](glm::vec3 normal){ return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); }, - [&](uint32_t index){ return index; })); + mesh->map( + [=](glm::vec3 position) { + return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); + }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); }, + [&](uint32_t index) { return index; })); result << meshProxy; } diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 64fb365d0c..3234a079ec 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -38,16 +38,22 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { // figure out the size of the resulting mesh size_t totalVertexCount { 0 }; - size_t totalAttributeCount { 0 }; + size_t totalColorCount { 0 }; + size_t totalNormalCount { 0 }; size_t totalIndexCount { 0 }; foreach (const MeshProxy* meshProxy, in) { MeshPointer mesh = meshProxy->getMeshPointer(); totalVertexCount += mesh->getNumVertices(); + int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(attributeTypeColor); + gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); + totalColorCount += numColors; + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - totalAttributeCount += numNormals; + totalNormalCount += numNormals; totalIndexCount += mesh->getNumIndices(); } @@ -57,7 +63,11 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; unsigned char* combinedVertexDataCursor = combinedVertexData; - gpu::Resource::Size combinedNormalSize = totalAttributeCount * sizeof(glm::vec3); + gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3); + unsigned char* combinedColorData = new unsigned char[combinedColorSize]; + unsigned char* combinedColorDataCursor = combinedColorData; + + gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3); unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; unsigned char* combinedNormalDataCursor = combinedNormalData; @@ -74,6 +84,10 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { memcpy(combinedVertexDataCursor, &position, sizeof(position)); combinedVertexDataCursor += sizeof(position); }, + [&](glm::vec3 color){ + memcpy(combinedColorDataCursor, &color, sizeof(color)); + combinedColorDataCursor += sizeof(color); + }, [&](glm::vec3 normal){ memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); combinedNormalDataCursor += sizeof(normal); @@ -96,6 +110,13 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); result->setVertexBuffer(combinedVertexBufferView); + int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData); + gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer); + gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement); + result->addAttribute(attributeTypeColor, combinedColorsBufferView); + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); @@ -132,6 +153,7 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro } model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [&](glm::vec3 color){ return color; }, [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, [&](uint32_t index){ return index; }); MeshProxy* resultProxy = new SimpleMeshProxy(result); From a1107deef19c713d84bf1a46c9234745bb6bf31f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 23 Jul 2017 08:21:50 -0700 Subject: [PATCH 076/148] cleanups --- libraries/model/src/model/Geometry.cpp | 3 --- libraries/render-utils/src/Model.cpp | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 5a8f4de0ca..5627746c43 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -152,8 +152,6 @@ model::MeshPointer Mesh::map(std::function vertexFunc, } // color data - // static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; - // int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h int attributeTypeColor = gpu::Stream::COLOR; const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); @@ -250,7 +248,6 @@ void Mesh::forEach(std::function vertexFunc, // color data int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h - // int attributeTypeColor = gpu::Stream::COLOR; const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numColors; i++) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 917d284f86..095712f351 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -488,7 +488,9 @@ MeshProxyList Model::getMeshes() { return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); }, [=](glm::vec3 color) { return color; }, - [=](glm::vec3 normal) { return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); }, + [=](glm::vec3 normal) { + return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); + }, [&](uint32_t index) { return index; })); result << meshProxy; } From 300b926464e1412be5744316252ba82a1caa8576 Mon Sep 17 00:00:00 2001 From: humbletim Date: Sun, 23 Jul 2017 14:11:59 -0400 Subject: [PATCH 077/148] only call QList::constFirst when history list isn't empty --- interface/src/ui/JSConsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index ef38ea6d29..394b947372 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -122,7 +122,7 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { } void JSConsole::executeCommand(const QString& command) { - if (_commandHistory.constFirst() != command) { + if (_commandHistory.isEmpty() || _commandHistory.constFirst() != command) { _commandHistory.prepend(command); if (_commandHistory.length() > MAX_HISTORY_SIZE) { _commandHistory.removeLast(); From 630922dd95d39a3c6feeb6bf442f917ad847f92e Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 05:25:52 +0100 Subject: [PATCH 078/148] 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 079/148] 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 1ce1204a64805ca3fde7ed7430362cb649e53be0 Mon Sep 17 00:00:00 2001 From: milad Date: Mon, 24 Jul 2017 10:06:44 -0500 Subject: [PATCH 080/148] WORKLIST ISSUE # 21471 : FIX PARENT JOINT IN EDIT JS In entityPropeties.js line #1079 says: elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex')); The problem should be fixed by changing it to: elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex', 0)); createEmitNumberPropertyUpdateFunction() is parsing the value as a float to a default decimal position of 4. Looks like it only accepts integers. By adding in that 0 as a second argument, it should coerce the value to one that will be recognized by the UI/backend. --- scripts/system/html/js/entityProperties.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 777ef54085..70594d8f1e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1074,7 +1074,7 @@ function loaded() { elDimensionsZ.addEventListener('change', dimensionsChangeFunction); elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID')); - elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex')); + elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex', 0)); var registrationChangeFunction = createEmitVec3PropertyUpdateFunction( 'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ); From 42742ba1f9fda98db0fd11281a5b68b5a471fa09 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 18:31:16 +0100 Subject: [PATCH 081/148] 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 082/148] 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 083/148] 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 084/148] 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 085/148] 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 086/148] 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 9c152ddc6f7d0c86c88b8423a8ee64f295351a29 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 22:30:43 +0100 Subject: [PATCH 087/148] 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 088/148] 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 7c62ed6dac2c324097f18b9fd49efcd832be5b14 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Mon, 24 Jul 2017 14:46:32 -0700 Subject: [PATCH 089/148] made sendRequest depend on hasValidAccessToken being true --- domain-server/src/DomainGatekeeper.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 62f56184f4..6421a50fe3 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -927,9 +927,13 @@ void DomainGatekeeper::getDomainOwnerFriendsList() { callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback"; const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends"; - DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, - QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), - NULL, QVariantMap()); + if (DependencyManager::get()->hasValidAccessToken()) + { + DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, + QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), + NULL, QVariantMap()); + } + } void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply) { From 884bdb3b7e776a21f2216ccd6e248c2e73a7b1b5 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Mon, 24 Jul 2017 14:57:22 -0700 Subject: [PATCH 090/148] Update DomainGatekeeper.cpp --- domain-server/src/DomainGatekeeper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 6421a50fe3..4c9f90b9ae 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -930,8 +930,8 @@ void DomainGatekeeper::getDomainOwnerFriendsList() { if (DependencyManager::get()->hasValidAccessToken()) { DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, - QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), - NULL, QVariantMap()); + QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), + NULL, QVariantMap()); } } From 7de164f1ab6804dedec413320415bcde8784da55 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Mon, 24 Jul 2017 15:06:45 -0700 Subject: [PATCH 091/148] 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 092/148] 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 093/148] 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 82a787a753ddefaa19c3531c37c719a9fcc07320 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 16:04:11 -0700 Subject: [PATCH 094/148] 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 095/148] 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 096/148] 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 097/148] 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 5771ee27e1f510a563b4d25b39f8071d3e93d547 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Mon, 24 Jul 2017 17:33:01 -0700 Subject: [PATCH 098/148] Update DomainGatekeeper.cpp --- domain-server/src/DomainGatekeeper.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 4c9f90b9ae..da31fed387 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -927,8 +927,7 @@ void DomainGatekeeper::getDomainOwnerFriendsList() { callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback"; const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends"; - if (DependencyManager::get()->hasValidAccessToken()) - { + if (DependencyManager::get()->hasValidAccessToken()) { DependencyManager::get()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParams, QByteArray(), NULL, QVariantMap()); From 766c6f48c3b9acf016aae10b8e342eb8b1d39e61 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 17:43:01 -0700 Subject: [PATCH 099/148] 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 100/148] 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 101/148] 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 102/148] 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 103/148] 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 c4d909927a249824ecf73af4061bf36e9474bd2d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 19:23:39 -0700 Subject: [PATCH 104/148] make atp-client work again --- tools/atp-client/src/ATPClientApp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index 3e2f8ca71d..7d091aec74 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "ATPClientApp.h" @@ -137,6 +138,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) : Setting::init(); DependencyManager::registerInheritance(); + DependencyManager::set(); DependencyManager::set([&]{ return QString(HIGH_FIDELITY_ATP_CLIENT_USER_AGENT); }); DependencyManager::set(); DependencyManager::set(NodeType::Agent, _listenPort); From a3f4aeb1826e2249a2f9e1332f0e108bcaf2d730 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 19:41:30 -0700 Subject: [PATCH 105/148] code review --- libraries/fbx/src/OBJReader.cpp | 4 ++-- libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/Model.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 5ec6a9023d..3099782588 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -130,7 +130,6 @@ bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { auto y = getFloat(); // And order of arguments is different on Windows/Linux. auto z = getFloat(); vertex = glm::vec3(x, y, z); - vertexColor = glm::vec3(1.0f); // default, in case there is not color information auto r = 1.0f, g = 1.0f, b = 1.0f; bool hasVertexColor = false; @@ -415,7 +414,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi #endif } } else if (token == "v") { - glm::vec3 vertex, vertexColor; + glm::vec3 vertex; + glm::vec3 vertexColor { glm::vec3(1.0f) }; bool hasVertexColor = tokenizer.getVertex(vertex, vertexColor); vertices.append(vertex); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 095712f351..42bb91ce94 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -463,7 +463,7 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } -MeshProxyList Model::getMeshes() { +MeshProxyList Model::getMeshes() const { MeshProxyList result; const Geometry::Pointer& renderGeometry = getGeometry(); const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index da8677aeed..497346c138 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -257,7 +257,7 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - Q_INVOKABLE MeshProxyList getMeshes(); + Q_INVOKABLE MeshProxyList getMeshes() const; public slots: void loadURLFinished(bool success); From 2f857a6d1a8b58332eabc4e4329e30753abd17a3 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Tue, 25 Jul 2017 06:05:06 +0100 Subject: [PATCH 106/148] 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 107/148] 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 108/148] 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 109/148] 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 110/148] 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 2d0c5ff37a8624bc75f7b94a5409ff841c8b1df7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 11:41:34 -0700 Subject: [PATCH 111/148] 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 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 112/148] 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 b1aa6e19625f6e17c0f2c28cca494832cef2d96e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 13:42:29 -0700 Subject: [PATCH 113/148] 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 e9845784d5a5a6012b1b10c8729f5e5e6798ae94 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 13:43:30 -0700 Subject: [PATCH 114/148] 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 115/148] 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 e5ddded464ed54a4f942807e090fa5408eaf6129 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 16:16:15 -0700 Subject: [PATCH 116/148] 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 65192aa620c13d1fe65c4dc032a42d6f5e9da148 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 15:17:15 -0700 Subject: [PATCH 117/148] 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 118/148] 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 119/148] 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 120/148] 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 d2975c79c9b5f495e365a03fcfedd6f1ed37c308 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 08:41:26 -0700 Subject: [PATCH 121/148] 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 122/148] 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 215fa7b816c943b656f7033eaf7183feb8f64fcb Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 09:34:46 -0700 Subject: [PATCH 123/148] 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 124/148] 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 125/148] 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 a960ea5ccbba13fbeba68c55fcbd3de43bcb46c0 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 13:32:23 -0700 Subject: [PATCH 126/148] 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 9c9cfad4c483ad8d626c2721ad8fb245ec76cfff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Jul 2017 15:56:08 -0700 Subject: [PATCH 127/148] 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 c57660c82d3f17b8759f6dd3271d584c361f72f4 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Wed, 26 Jul 2017 16:52:00 -0700 Subject: [PATCH 128/148] 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 081d8fbec442745aeb14acba95fa05e66bdfbc83 Mon Sep 17 00:00:00 2001 From: anshuman64 Date: Wed, 26 Jul 2017 17:00:48 -0700 Subject: [PATCH 129/148] 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 130/148] 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 f7add1f6c6cd6a443577cf8e7fc93464376a48b1 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 13:17:42 -0700 Subject: [PATCH 131/148] 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 132/148] 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 133/148] 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 134/148] 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 135/148] 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 136/148] 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 55e23a31069087ca6d4db8445c0fc72cff3bc050 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 16:57:08 -0700 Subject: [PATCH 137/148] 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 138/148] 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 c1d179eba102819c43fb4146ef9c0836bfec0551 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Jul 2017 14:20:49 +1200 Subject: [PATCH 139/148] 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 140/148] 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 141/148] 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 3d0c13915a247040acbf631b9e91c68e9ba4152b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 09:33:02 -0700 Subject: [PATCH 142/148] 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 17dd028ec7ffc761920dbcf4226bf691ef66acba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Jul 2017 14:23:18 -0700 Subject: [PATCH 143/148] 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 144/148] 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 145/148] 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 7d18d2e01788e8c7d07cd204edad031f37493c55 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 22:14:18 -0700 Subject: [PATCH 146/148] 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 147/148] 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 148/148] 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) {