From 7022c4009fafa72fffa0fcf45aac9919557a280f Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Thu, 15 Jun 2017 15:18:06 -0700 Subject: [PATCH 001/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] 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/269] Move ThumbUp gesture detection into json map file. --- interface/resources/controllers/vive.json | 12 +++++++++--- plugins/openvr/src/ViveControllerManager.cpp | 11 +++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 349554673d..cc8e2a437a 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -22,6 +22,10 @@ { "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" }, { "from": "Vive.LS", "to": "Standard.LS" }, + { "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp", + "peek": true, + "filters": [ { "type": "not" } ] + }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, { "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" }, @@ -36,6 +40,10 @@ { "from": "Vive.RightGrip", "to": "Standard.RightGrip" }, { "from": "Vive.RS", "to": "Standard.RS" }, + { "from": "Vive.RSTouch", "to": "Standard.RightThumbUp", + "peek": true, + "filters": [ { "type": "not" } ] + }, { "from": "Vive.RSTouch", "to": "Standard.RSTouch" }, { "from": "Vive.LSCenter", "to": "Standard.LeftPrimaryThumb" }, @@ -69,8 +77,6 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, - { "from": "Vive.LeftThumbUp", "to": "Standard.LeftThumbUp" }, - { "from": "Vive.RightThumbUp", "to": "Standard.RightThumbUp" } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 895aff4f03..07b3b2f73d 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -863,11 +863,9 @@ void ViveControllerManager::InputDevice::handleButtonEvent(float deltaTime, uint } } - if (button == vr::k_EButton_SteamVR_Touchpad) { - if (touched) { - _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); - } else { - _buttonPressedMap.insert(isLeftHand ? LEFT_THUMB_UP : RIGHT_THUMB_UP); + if (touched) { + if (button == vr::k_EButton_SteamVR_Touchpad) { + _buttonPressedMap.insert(isLeftHand ? LS_TOUCH : RS_TOUCH); } } } @@ -1172,9 +1170,6 @@ controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableI // app button above trackpad. Input::NamedPair(Input(_deviceID, LEFT_APP_MENU, ChannelType::BUTTON), "LeftApplicationMenu"), Input::NamedPair(Input(_deviceID, RIGHT_APP_MENU, ChannelType::BUTTON), "RightApplicationMenu"), - - makePair(LEFT_THUMB_UP, "LeftThumbUp"), - makePair(RIGHT_THUMB_UP, "RightThumbUp"), }; return availableInputs; From 7bcd9f9f9544256638657ca15c78671c4b7e47d6 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 12:22:11 -0400 Subject: [PATCH 032/269] * integrate support for room scale "moves as you move" * migrate doppleganger debug controls into separate module * misc cleanup --- .../app-doppleganger-attachments.js | 106 +- .../dist/app-doppleganger-marketplace.js | 1501 +++++++++++++++++ .../doppleganger-attachments.js | 53 +- .../doppleganger-debug.js | 158 ++ .../doppleganger-attachments/doppleganger.js | 238 +-- .../doppleganger-attachments/makefile | 11 + .../doppleganger-attachments/model-helper.js | 9 +- .../doppleganger-attachments/package.json | 5 + .../doppleganger-attachments/readme.md | 4 + .../doppleganger-attachments/utils.js | 8 + 10 files changed, 1843 insertions(+), 250 deletions(-) create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/makefile create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/package.json create mode 100644 unpublishedScripts/marketplace/doppleganger-attachments/readme.md diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js index 4617cf47b6..c09ad602f8 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/app-doppleganger-attachments.js @@ -9,21 +9,44 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ -var require = Script.require; +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (typeof require !== 'function') { + require = Script.require; +} +var VERSION = '0.0.0'; var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); var DopplegangerClass = require('./doppleganger.js'), DopplegangerAttachments = require('./doppleganger-attachments.js'), - modelHelper = require('./model-helper.js').modelHelper; + DebugControls = require('./doppleganger-debug.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: require('./doppleganger-i.svg.json'), + activeIcon: require('./doppleganger-a.svg.json'), +}); var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), - button = tablet.addButton({ - icon: Script.resolvePath('./doppleganger-i.svg'), - activeIcon: Script.resolvePath('./doppleganger-a.svg'), - text: 'MIRROR' - }); + button = tablet.addButton(buttonConfig); Script.scriptEnding.connect(function() { tablet.removeButton(button); @@ -65,6 +88,59 @@ var doppleganger = new DopplegangerClass({ } } +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + // hide the doppleganger if this client script is unloaded Script.scriptEnding.connect(doppleganger, 'stop'); @@ -103,15 +179,21 @@ doppleganger.modelLoaded.connect(function(error, result) { // add debug indicators, but only if the user has configured the settings value if (Settings.getValue('debug.doppleganger', false)) { - WANT_DEBUG = true; - DopplegangerClass.addDebugControls(doppleganger); + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { - if (WANT_DEBUG) { - print('app-doppleganger | ' + [].slice.call(arguments).join(' ')); - } + WANT_DEBUG && log.apply(this, arguments); } + // ---------------------------------------------------------------------------- UserActivityLogger.logAction('doppleganger_app_load'); diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js new file mode 100644 index 0000000000..6abd3a0fc5 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -0,0 +1,1501 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +/* eslint-env commonjs */ +/* global console */ + +module.exports = { + version: '0.0.1', + bind: bind, + signal: signal, + assign: assign, + assert: assert +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +// @function - bind a function to a `this` context +// @param {Object} - the `this` context +// @param {Function|String} - function or method name +// @param {value} varargs... - optional curry-right arguments (passed to method after any explicit arguments) +function bind(thiz, method, varargs) { + method = thiz[method] || method; + varargs = [].slice.call(arguments, 2); + return function() { + var args = [].slice.call(arguments).concat(varargs); + return method.apply(thiz, args); + }; +} + +// @function - Qt signal polyfill +function signal(template) { + var callbacks = []; + return Object.defineProperties(function() { + var args = [].slice.call(arguments); + callbacks.forEach(function(obj) { + obj.handler.apply(obj.scope, args); + }); + }, { + connect: { value: function(scope, handler) { + var callback = {scope: scope, handler: scope[handler] || handler || scope}; + if (!callback.handler || !callback.handler.apply) { + throw new Error('invalid arguments to connect:' + [template, scope, handler]); + } + callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); + }}, + disconnect: { value: function(scope, handler) { + var match = {scope: scope, handler: scope[handler] || handler || scope}; + callbacks = callbacks.filter(function(obj) { + return !(obj.scope === match.scope && obj.handler === match.handler); + }); + }} + }); +} + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill +/* eslint-disable */ +function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; +} +/* eslint-enable */ +// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill + +// examples: +// assert(function assertion() { return (conditions === true) }, 'assertion failed!') +// var neededValue = assert(idString, 'idString not specified!'); +// assert(false, 'unexpected state'); +function assert(truthy, message) { + message = message || 'Assertion Failed:'; + + if (typeof truthy === 'function' && truthy.name === 'assertion') { + // extract function body to display with the assertion message + var assertion = (truthy+'').replace(/[\r\n]/g, ' ') + .replace(/^[^{]+\{|\}$|^\s*|\s*$/g, '').trim() + .replace(/^return /,'').replace(/\s[\r\n\t\s]+/g, ' '); + message += ' ' + JSON.stringify(assertion); + try { + truthy = truthy(); + } catch (e) { + message += '(exception: ' + e +')'; + } + } + if (!truthy) { + message += ' ('+truthy+')'; + throw new Error(message); + } + return truthy; +} + + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +// model-helper.js +// +// Created by Timothy Dedischew on 06/01/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module model-helper +// +// This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and +// also initial plumbing helpers to eliminate unnecessary API differences when working with Model Overlays and +// Model Entities at a high-level from scripting. + +var utils = __webpack_require__(0), + assert = utils.assert; + +module.exports = { + version: '0.0.1', + ModelReadyWatcher: ModelReadyWatcher +}; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + +var _objectDeleted = utils.signal(function objectDeleted(objectID){}); +// proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal +var objectDeleted = utils.assign(function objectDeleted(objectID){}, { + connect: function() { + Overlays.overlayDeleted.connect(_objectDeleted); + // Entities.deletingEntity.connect(objectDeleted); + Script.scriptEnding.connect(function() { + Overlays.overlayDeleted.disconnect(_objectDeleted); + // Entities.deletingEntity.disconnect(objectDeleted); + }); + // hereafter _objectDeleted.connect will be used instead + this.connect = utils.bind(_objectDeleted, 'connect'); + return this.connect.apply(this, arguments); + }, + disconnect: utils.bind(_objectDeleted, 'disconnect') +}); + +var modelHelper = module.exports.modelHelper = { + // Entity <-> Overlay property translations + _entityFromOverlay: { + modelURL: function url() { + return this.url; + }, + dimensions: function dimensions() { + return Vec3.multiply(this.scale, this.naturalDimensions); + } + }, + _overlayFromEntity: { + url: function modelURL() { + return this.modelURL; + }, + scale: function scale() { + return this.dimensions && this.naturalDimensions && { + x: this.dimensions.x / this.naturalDimensions.x, + y: this.dimensions.y / this.naturalDimensions.y, + z: this.dimensions.z / this.naturalDimensions.z + }; + } + }, + objectDeleted: objectDeleted, + type: function(objectID) { + // TODO: support Model Entities (by detecting type from objectID, which is already possible) + return !Uuid.isNull(objectID) ? 'overlay' : null; + }, + addObject: function(properties) { + var type = 'overlay'; // this.resolveType(properties) + switch (type) { + case 'overlay': return Overlays.addOverlay(properties.type, this.toOverlayProps(properties)); + } + return false; + }, + editObject: function(objectID, properties) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.editOverlay(objectID, this.toOverlayProps(properties)); + } + return false; + }, + deleteObject: function(objectID) { + return this.type(objectID) === 'overlay' && Overlays.deleteOverlay(objectID); + }, + getProperty: function(objectID, propertyName) { + return this.type(objectID) === 'overlay' && Overlays.getProperty(objectID, propertyName); + }, + getProperties: function(objectID, filter) { + switch (this.type(objectID)) { + case 'overlay': + filter = Array.isArray(filter) ? filter : [ + 'position', 'rotation', 'localPosition', 'localRotation', 'parentID', + 'parentJointIndex', 'scale', 'name', 'visible', 'type', 'url', + 'dimensions', 'naturalDimensions', 'grabbable' + ]; + var properties = filter.reduce(function(out, propertyName) { + out[propertyName] = Overlays.getProperty(objectID, propertyName); + return out; + }, {}); + return this.toEntityProps(properties); + } + return null; + }, + // adapt Entity conventions to Overlay (eg: { modelURL: ... } -> { url: ... }) + toOverlayProps: function(properties) { + var result = {}; + for (var from in this._overlayFromEntity) { + var adapter = this._overlayFromEntity[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + // adapt Overlay conventions to Entity (eg: { url: ... } -> { modelURL: ... }) + toEntityProps: function(properties) { + var result = {}; + for (var from in this._entityToOverlay) { + var adapter = this._entityToOverlay[from]; + result[from] = adapter.call(properties, from, adapter.name); + } + return utils.assign(result, properties); + }, + editObjects: function(updatedObjects) { + var objectIDs = Object.keys(updatedObjects), + type = this.type(objectIDs[0]); + switch (type) { + case 'overlay': + var translated = {}; + for (var objectID in updatedObjects) { + translated[objectID] = this.toOverlayProps(updatedObjects[objectID]); + } + return Overlays.editOverlays(translated); + } + return false; + }, + getJointIndex: function(objectID, name) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames').indexOf(name); + } + return -1; + }, + getJointNames: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointNames'); + } + return []; + }, + // @function - derives mirrored joint names from a list of regular joint names + // @param {Array} - list of joint names to mirror + // @return {Array} - list of mirrored joint names (note: entries for non-mirrored joints will be `undefined`) + deriveMirroredJointNames: function(jointNames) { + return jointNames.map(function(name, i) { + if (/Left/.test(name)) { + return name.replace('Left', 'Right'); + } + if (/Right/.test(name)) { + return name.replace('Right', 'Left'); + } + return undefined; + }); + }, + getJointPosition: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions')[index]; + } + return Vec3.ZERO; + }, + getJointPositions: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointPositions'); + } + return []; + }, + getJointOrientation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations')[index]; + } + return Quat.normalize({}); + }, + getJointOrientations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointOrientations'); + } + }, + getJointTranslation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations')[index]; + } + return Vec3.ZERO; + }, + getJointTranslations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointTranslations'); + } + return []; + }, + getJointRotation: function(objectID, index) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations')[index]; + } + return Quat.normalize({}); + }, + getJointRotations: function(objectID) { + switch (this.type(objectID)) { + case 'overlay': return Overlays.getProperty(objectID, 'jointRotations'); + } + return []; + } +}; // modelHelper + + +// @property {PreconditionFunction} - indicates when the model's jointNames have become available +ModelReadyWatcher.JOINTS = function(state) { + return Array.isArray(state.jointNames); +}; +// @property {PreconditionFunction} - indicates when a model's naturalDimensions have become available +ModelReadyWatcher.DIMENSIONS = function(state) { + return Vec3.length(state.naturalDimensions) > 0; +}; +// @property {PreconditionFunction} - indicates when both a model's naturalDimensions and jointNames have become available +ModelReadyWatcher.JOINTS_AND_DIMENSIONS = function(state) { + // eslint-disable-next-line new-cap + return ModelReadyWatcher.JOINTS(state) && ModelReadyWatcher.DIMENSIONS(state); +}; +// @property {int} - interval used for continually rechecking model readiness, until ready or a timeout occurs +ModelReadyWatcher.RECHECK_MS = 50; +// @property {int} - default wait time before considering a model unready-able. +ModelReadyWatcher.DEFAULT_TIMEOUT_SECS = 10; + +// @private @class - waits for model to become usable inworld and tracks errors/timeouts +// @param [Object} options -- key/value config options: +// @param {ModelResource} options.resource - a ModelCache prefetched resource to observe for determining load state +// @param {Uuid} options.objectID - an inworld object to observe for determining readiness states +// @param {Function} [options.precondition=ModelReadyWatcher.JOINTS] - the precondition used to determine if the model is usable +// @param {int} [options.maxWaitSeconds=10] - max seconds to wait for the model to become usable, after which a timeout error is emitted +// @return {ModelReadyWatcher} +function ModelReadyWatcher(options) { + options = utils.assign({ + precondition: ModelReadyWatcher.JOINTS, + maxWaitSeconds: ModelReadyWatcher.DEFAULT_TIMEOUT_SECS + }, options); + + assert(!Uuid.isNull(options.objectID), 'Error: invalid options.objectID'); + assert(options.resource && 'state' in options.resource, 'Error: invalid options.resource'); + assert(typeof options.precondition === 'function', 'Error: invalid options.precondition'); + + utils.assign(this, { + resource: options.resource, + objectID: options.objectID, + precondition: options.precondition, + + // @signal - emitted when the model becomes ready, or with the error that prevented it + modelReady: utils.signal(function modelReady(error, result){}), + + // @public + ready: false, // tracks readiness state + jointNames: null, // populated with detected jointNames + naturalDimensions: null, // populated with detected naturalDimensions + + _startTime: new Date, + _watchdogTimer: Script.setTimeout(utils.bind(this, function() { + this._watchdogTimer = null; + }), options.maxWaitSeconds * 1000), + _interval: Script.setInterval(utils.bind(this, '_waitUntilReady'), ModelReadyWatcher.RECHECK_MS) + }); + + this.modelReady.connect(this, function(error, result) { + this.ready = !error && result; + }); +} + +ModelReadyWatcher.prototype = { + contructor: ModelReadyWatcher, + // @public method -- cancels monitoring and (if model was not yet ready) emits an error + cancel: function() { + return this._stop() && !this.ready && this.modelReady('cancelled', null); + }, + // stop pending timers + _stop: function() { + var stopped = 0; + if (this._watchdogTimer) { + Script.clearTimeout(this._watchdogTimer); + this._watchdogTimer = null; + stopped++; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = null; + stopped++; + } + return stopped; + }, + // the monitoring thread func + _waitUntilReady: function() { + var error = null, result = null; + if (!this._watchdogTimer) { + error = this.precondition.name || 'timeout'; + } else if (this.resource.state === Resource.State.FAILED) { + error = 'prefetch_failed'; + } else if (this.resource.state === Resource.State.FINISHED) { + // in theory there will always be at least one "joint name" that represents the main submesh + var names = modelHelper.getJointNames(this.objectID); + if (Array.isArray(names) && names.length) { + this.jointNames = names; + } + var props = modelHelper.getProperties(this.objectID, ['naturalDimensions']); + if (props && props.naturalDimensions && Vec3.length(props.naturalDimensions)) { + this.naturalDimensions = props.naturalDimensions; + } + var state = { + resource: this.resource, + objectID: this.objectID, + waitTime: (new Date - this._startTime) / 1000, + jointNames: this.jointNames, + naturalDimensions: this.naturalDimensions + }; + if (this.precondition(state)) { + result = state; + } + } + if (error || result !== null) { + this._stop(); + this.modelReady(error, result); + } + } +}; // ModelReadyWatcher.prototype + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// doppleganger.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env commonjs */ +/* global console */ +// @module doppleganger +// +// This module contains the `Doppleganger` class implementation for creating an inspectable replica of +// an Avatar (as a model directly in front of and facing them). Joint positions and rotations are copied +// over in an update thread, so that the model automatically mirrors the Avatar's joint movements. +// An Avatar can then for example walk around "themselves" and examine from the back, etc. +// +// This should be helpful for inspecting your own look and debugging avatars, etc. +// +// The doppleganger is created as an overlay so that others do not see it -- and this also allows for the +// highest possible update rate when keeping joint data in sync. + +module.exports = Doppleganger; + +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +// @property {bool} - toggle verbose debug logging on/off +Doppleganger.WANT_DEBUG = false; + +// @property {bool} - when set true, Script.update will be used instead of setInterval for syncing joint data +Doppleganger.USE_SCRIPT_UPDATE = false; + +// @property {int} - the frame rate to target when using setInterval for joint updates +Doppleganger.TARGET_FPS = 60; + +// @class Doppleganger - Creates a new instance of a Doppleganger. +// @param {Avatar} [options.avatar=MyAvatar] - Avatar used to retrieve position and joint data. +// @param {bool} [options.mirrored=true] - Apply "symmetric mirroring" of Left/Right joints. +// @param {bool} [options.autoUpdate=true] - Automatically sync joint data. +function Doppleganger(options) { + this.options = options = options || {}; + this.avatar = options.avatar || MyAvatar; + this.mirrored = 'mirrored' in options ? options.mirrored : true; + this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; + + // @public + this.active = false; // whether doppleganger is currently being displayed/updated + this.objectID = null; // current doppleganger's Overlay or Entity id + this.frame = 0; // current joint update frame + + // @signal - emitted when .active state changes + this.activeChanged = utils.signal(function(active, reason) {}); + // @signal - emitted once model is either loaded or errors out + this.modelLoaded = utils.signal(function(error, result){}); + // @signal - emitted each time the model's joint data has been synchronized + this.jointsUpdated = utils.signal(function(objectID){}); +} + +Doppleganger.prototype = { + // @public @method - toggles doppleganger on/off + toggle: function() { + if (this.active) { + debugPrint('toggling off'); + this.stop(); + } else { + debugPrint('toggling on'); + this.start(); + } + return this.active; + }, + + // @public @method - synchronize the joint data between Avatar / doppleganger + update: function() { + this.frame++; + try { + if (!this.objectID) { + throw new Error('!this.objectID'); + } + + if (this.avatar.skeletonModelURL !== this.skeletonModelURL) { + return this.stop('avatar_changed'); + } + + var rotations = this.avatar.getJointRotations(); + var translations = this.avatar.getJointTranslations(); + var size = rotations.length; + + // note: this mismatch can happen when the avatar's model is actively changing + if (size !== translations.length || + (this.jointStateCount && size !== this.jointStateCount)) { + debugPrint('mismatched joint counts (avatar model likely changed)', size, translations.length, this.jointStateCount); + this.stop('avatar_changed_joints'); + return; + } + this.jointStateCount = size; + + if (this.mirrored) { + var mirroredIndexes = this.mirroredIndexes; + var outRotations = new Array(size); + var outTranslations = new Array(size); + for (var i=0; i < size; i++) { + var index = mirroredIndexes[i]; + if (index < 0 || index === false) { + index = i; + } + var rot = rotations[index]; + var trans = translations[index]; + trans.x *= -1; + rot.y *= -1; + rot.z *= -1; + outRotations[i] = rot; + outTranslations[i] = trans; + } + rotations = outRotations; + translations = outTranslations; + } + var jointUpdates = { + jointRotations: rotations, + jointTranslations: translations + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); + } catch (e) { + log('.update error: '+ e, index, e.stack); + this.stop('update_error'); + throw e; + } + }, + + // @public @method - show the doppleganger (and start the update thread, if options.autoUpdate was specified). + // @param {vec3} [options.position=(in front of avatar)] - starting position + // @param {quat} [options.orientation=avatar.orientation] - starting orientation + start: function(options) { + options = utils.assign(this.options, options); + if (this.objectID) { + log('start() called but object model already exists', this.objectID); + return; + } + var avatar = this.avatar; + if (!avatar.jointNames.length) { + return this.stop('joints_unavailable'); + } + + this.frame = 0; + var localPosition = Vec3.multiply(2, Quat.getForward(avatar.orientation)); + this.position = options.position || Vec3.sum(avatar.position, localPosition); + this.orientation = options.orientation || avatar.orientation; + this.skeletonModelURL = avatar.skeletonModelURL; + this.scale = avatar.scale || 1.0; + this.jointStateCount = 0; + this.jointNames = avatar.jointNames; + this.type = options.type || 'overlay'; + this.mirroredNames = modelHelper.deriveMirroredJointNames(this.jointNames); + this.mirroredIndexes = this.mirroredNames.map(function(name) { + return name ? avatar.getJointIndex(name) : false; + }); + + this.objectID = modelHelper.addObject({ + type: this.type === 'overlay' ? 'model' : 'Model', + modelURL: this.skeletonModelURL, + position: this.position, + rotation: this.orientation, + scale: this.scale + }); + Script.scriptEnding.connect(this, function() { + modelHelper.deleteObject(this.objectID); + }); + debugPrint('doppleganger created; objectID =', this.objectID); + + // trigger clean up (and stop updates) if the object gets deleted + this.onObjectDeleted = function(uuid) { + if (uuid === this.objectID) { + log('onObjectDeleted', uuid); + this.stop('object_deleted'); + } + }; + modelHelper.objectDeleted.connect(this, 'onObjectDeleted'); + + if ('onLoadComplete' in avatar) { + // stop the current doppleganger if Avatar loads a different model URL + this.onLoadComplete = function() { + if (avatar.skeletonModelURL !== this.skeletonModelURL) { + this.stop('avatar_changed_load'); + } + }; + avatar.onLoadComplete.connect(this, 'onLoadComplete'); + } + + this.onModelLoaded = function(error, result) { + if (error) { + return this.stop(error); + } + debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); + var props = { visible: true }; + if (naturalDimensions) { + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); + } + debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); + modelHelper.editObject(this.objectID, props); + if (!options.position) { + this.syncVerticalPosition(); + } + if (this.autoUpdate) { + this._createUpdateThread(); + } + }; + + this._resource = ModelCache.prefetch(this.skeletonModelURL); + this._modelReadier = new ModelReadyWatcher({ + resource: this._resource, + objectID: this.objectID + }); + this._modelReadier.modelReady.connect(this, 'onModelLoaded'); + this.activeChanged(this.active = true, 'start'); + }, + + // @public @method - hide the doppleganger + // @param {String} [reason=stop] - the reason stop was called + stop: function(reason) { + reason = reason || 'stop'; + if (this.onUpdate) { + Script.update.disconnect(this, 'onUpdate'); + delete this.onUpdate; + } + if (this._interval) { + Script.clearInterval(this._interval); + this._interval = undefined; + } + if (this.onObjectDeleted) { + modelHelper.objectDeleted.disconnect(this, 'onObjectDeleted'); + delete this.onObjectDeleted; + } + if (this.onLoadComplete) { + this.avatar.onLoadComplete.disconnect(this, 'onLoadComplete'); + delete this.onLoadComplete; + } + if (this.onModelLoaded) { + this._modelReadier && this._modelReadier.modelReady.disconnect(this, 'onModelLoaded'); + this._modelReadier = this.onModelLoaded = undefined; + } + if (this.objectID) { + modelHelper.deleteObject(this.objectID); + this.objectID = undefined; + } + if (this.active) { + this.activeChanged(this.active = false, reason); + } else if (reason) { + debugPrint('already stopped so not triggering another activeChanged; latest reason was:', reason); + } + }, + // @public @method - Reposition the doppleganger so it sees "eye to eye" with the Avatar. + // @param {String} [byJointName=Hips] - the reference joint used to align the Doppleganger and Avatar + syncVerticalPosition: function(byJointName) { + byJointName = byJointName || 'Hips'; + var positions = modelHelper.getJointPositions(this.objectID), + properties = modelHelper.getProperties(this.objectID), + dopplePosition = properties.position, + doppleJointIndex = modelHelper.getJointIndex(this.objectID, byJointName),// names.indexOf(byJointName), + doppleJointPosition = positions[doppleJointIndex]; + + debugPrint('........... doppleJointPosition', JSON.stringify({ + byJointName: byJointName, + dopplePosition: dopplePosition, + doppleJointIndex: doppleJointIndex, + doppleJointPosition: doppleJointPosition, + properties: properties.type, + positions: positions[0] + },0,2)); + var avatarPosition = this.avatar.position, + avatarJointIndex = this.avatar.getJointIndex(byJointName), + avatarJointPosition = this.avatar.getJointPosition(avatarJointIndex); + + var offset = (avatarJointPosition.y - doppleJointPosition.y); + debugPrint('adjusting for offset', offset); + if (properties.type === 'model') { + dopplePosition.y = avatarPosition.y + offset; + } else { + dopplePosition.y = avatarPosition.y - offset; + } + + this.position = dopplePosition; + modelHelper.editObject(this.objectID, { position: this.position }); + }, + + // @private @method - creates the update thread to synchronize joint data + _createUpdateThread: function() { + if (Doppleganger.USE_SCRIPT_UPDATE) { + debugPrint('creating Script.update thread'); + this.onUpdate = this.update; + Script.update.connect(this, 'onUpdate'); + } else { + debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); + var timeout = 1000 / Doppleganger.TARGET_FPS; + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); + } + } + +}; + +// @function - debug logging +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + Doppleganger.WANT_DEBUG && log.apply(this, arguments); +} + + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +// doppleganger-app.js +// +// Created by Timothy Dedischew on 04/21/2017. +// Copyright 2017 High Fidelity, Inc. +// +// This Client script creates an instance of a Doppleganger that can be toggled on/off via tablet button. +// (for more info see doppleganger.js) +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/* eslint-env commonjs */ +/* global DriveKeys, require:true, console */ +/* eslint-disable comma-dangle */ + +// decomment next line for automatic require cache-busting +// var require = function require(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); }; +if (false) { + require = Script.require; +} + +var VERSION = '0.0.0'; +var WANT_DEBUG = false; +var DEBUG_MOVE_AS_YOU_MOVE = false; +var ROTATE_AS_YOU_MOVE = false; + +log(VERSION); + +var DopplegangerClass = __webpack_require__(2), + DopplegangerAttachments = __webpack_require__(4), + DebugControls = __webpack_require__(5), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +// eslint-disable-next-line camelcase +var isWebpack = typeof __webpack_require__ === 'function'; + +var buttonConfig = utils.assign({ + text: 'MIRROR', +}, !isWebpack ? { + icon: Script.resolvePath('./doppleganger-i.svg'), + activeIcon: Script.resolvePath('./doppleganger-a.svg'), +} : { + icon: __webpack_require__(6), + activeIcon: __webpack_require__(7), +}); + +var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system'), + button = tablet.addButton(buttonConfig); + +Script.scriptEnding.connect(function() { + tablet.removeButton(button); + button = null; +}); + +var doppleganger = new DopplegangerClass({ + avatar: MyAvatar, + mirrored: false, + autoUpdate: true, + type: 'overlay' +}); + +// add support for displaying regular (non-soft) attachments on the doppleganger +{ + var RECHECK_ATTACHMENT_MS = 1000; + var dopplegangerAttachments = new DopplegangerAttachments(doppleganger), + attachmentInterval = null, + lastHash = dopplegangerAttachments.getAttachmentsHash(); + + // monitor for attachment changes, but only when the doppleganger is active + doppleganger.activeChanged.connect(function(active, reason) { + if (attachmentInterval) { + Script.clearInterval(attachmentInterval); + } + if (active) { + attachmentInterval = Script.setInterval(checkAttachmentsHash, RECHECK_ATTACHMENT_MS); + } else { + attachmentInterval = null; + } + }); + function checkAttachmentsHash() { + var currentHash = dopplegangerAttachments.getAttachmentsHash(); + if (currentHash !== lastHash) { + lastHash = currentHash; + debugPrint('app-doppleganger | detect attachment change'); + dopplegangerAttachments.refreshAttachments(); + } + } +} + +// add support for "move as you move" +{ + var movementKeys = 'W,A,S,D,Up,Down,Right,Left'.split(','); + var controllerKeys = 'LX,LY,RY'.split(','); + var translationKeys = Object.keys(DriveKeys).filter(function(p) { + return /translate/i.test(p); + }); + var startingPosition; + + // returns an array of any active driving keys (eg: ['W', 'TRANSLATE_Z']) + function currentDrivers() { + return [].concat( + movementKeys.map(function(key) { + return Controller.getValue(Controller.Hardware.Keyboard[key]) && key; + }) + ).concat( + controllerKeys.map(function(key) { + return Controller.getValue(Controller.Standard[key]) !== 0.0 && key; + }) + ).concat( + translationKeys.map(function(key) { + return MyAvatar.getRawDriveKey(DriveKeys[key]) !== 0.0 && key; + }) + ).filter(Boolean); + } + + doppleganger.jointsUpdated.connect(function(objectID) { + var drivers = currentDrivers(), + isDriving = drivers.length > 0; + if (isDriving) { + if (startingPosition) { + debugPrint('resetting startingPosition since drivers == ', drivers.join('|')); + startingPosition = null; + } + } else if (HMD.active || DEBUG_MOVE_AS_YOU_MOVE) { + startingPosition = startingPosition || MyAvatar.position; + var movement = Vec3.subtract(MyAvatar.position, startingPosition); + startingPosition = MyAvatar.position; + // Vec3.length(movement) > 0.0001 && Vec3.print('+avatarMovement', movement); + + // "mirror" the relative translation vector + movement.x *= -1; + movement.z *= -1; + var props = {}; + props.position = doppleganger.position = Vec3.sum(doppleganger.position, movement); + if (ROTATE_AS_YOU_MOVE) { + props.rotation = doppleganger.orientation = MyAvatar.orientation; + } + modelHelper.editObject(doppleganger.objectID, props); + } + }); +} + +// hide the doppleganger if this client script is unloaded +Script.scriptEnding.connect(doppleganger, 'stop'); + +// hide the doppleganger if the user switches domains (which might place them arbitrarily far away in world space) +function onDomainChanged() { + if (doppleganger.active) { + doppleganger.stop('domain_changed'); + } +} +Window.domainChanged.connect(onDomainChanged); +Window.domainConnectionRefused.connect(onDomainChanged); +Script.scriptEnding.connect(function() { + Window.domainChanged.disconnect(onDomainChanged); + Window.domainConnectionRefused.disconnect(onDomainChanged); +}); + +// toggle on/off via tablet button +button.clicked.connect(doppleganger, 'toggle'); + +// highlight tablet button based on current doppleganger state +doppleganger.activeChanged.connect(function(active, reason) { + if (button) { + button.editProperties({ isActive: active }); + debugPrint('doppleganger.activeChanged', active, reason); + } +}); + +// alert the user if there was an error applying their skeletonModelURL +doppleganger.modelLoaded.connect(function(error, result) { + if (doppleganger.active && error) { + Window.alert('doppleganger | ' + error + '\n' + doppleganger.skeletonModelURL); + } +}); + +// ---------------------------------------------------------------------------- + +// add debug indicators, but only if the user has configured the settings value +if (Settings.getValue('debug.doppleganger', false)) { + WANT_DEBUG = WANT_DEBUG || true; + DopplegangerClass.WANT_DEBUG = WANT_DEBUG; + DopplegangerAttachments.WANT_DEBUG = WANT_DEBUG; + new DebugControls(doppleganger); +} + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('app-doppleganger | ' + [].slice.call(arguments).join(' ')); +} + +function debugPrint() { + WANT_DEBUG && log.apply(this, arguments); +} + +// ---------------------------------------------------------------------------- + +UserActivityLogger.logAction('doppleganger_app_load'); +doppleganger.activeChanged.connect(function(active, reason) { + if (active) { + UserActivityLogger.logAction('doppleganger_enable'); + } else { + if (reason === 'stop') { + // user intentionally toggled the doppleganger + UserActivityLogger.logAction('doppleganger_disable'); + } else { + debugPrint('doppleganger stopped:', reason); + UserActivityLogger.logAction('doppleganger_autodisable', { reason: reason }); + } + } +}); +dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { + UserActivityLogger.logAction('doppleganger_attachments', { count: attachments.length }); +}); + + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } +module.exports = DopplegangerAttachments; + +DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.WANT_DEBUG = false; + +var _modelHelper = __webpack_require__(1), + modelHelper = _modelHelper.modelHelper, + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = __webpack_require__(0); + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); +} +log(DopplegangerAttachments.version); + +function debugPrint() { + DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); +} + +function DopplegangerAttachments(doppleganger, options) { + utils.assign(this, { + _options: options, + doppleganger: doppleganger, + attachments: undefined, + manualJointSync: true, + attachmentsUpdated: utils.signal(function attachmentsUpdated(currentAttachments, previousAttachments){}), + }); + this._initialize(); + debugPrint('DopplegangerAttachments...', JSON.stringify(options)); +} +DopplegangerAttachments.prototype = { + // "hash" the current attachments (so that changes can be detected) + getAttachmentsHash: function() { + return JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant()); + }, + // create a pure Object copy of the current native attachments variant + _cloneAttachmentsVariant: function() { + return JSON.parse(JSON.stringify(this.doppleganger.avatar.getAttachmentsVariant())); + }, + // fetch and resolve attachments to include jointIndex and other relevant $metadata + _getResolvedAttachments: function() { + var attachments = this._cloneAttachmentsVariant(), + objectID = this.doppleganger.objectID; + function toString() { + return '[attachment #' + this.$index + ' ' + this.$path + ' @ ' + this.jointName + '{' + this.$jointIndex + '}]'; + } + return attachments.map(function(attachment, i) { + var jointIndex = modelHelper.getJointIndex(objectID, attachment.jointName), + path = (attachment.modelUrl+'').split(/[?#]/)[0].split('/').slice(-3).join('/'); + return Object.defineProperties(attachment, { + $hash: { value: JSON.stringify(attachment) }, + $index: { value: i }, + $jointIndex: { value: jointIndex }, + $path: { value: path }, + toString: { value: toString }, + }); + }); + }, + // compare before / after attachment sets to determine which ones need to be (re)created + refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } + var before = this.attachments || [], + beforeIndex = before.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + var after = this._getResolvedAttachments(), + afterIndex = after.reduce(function(out, att, index) { + out[att.$hash] = index; return out; + }, {}); + + Object.keys(beforeIndex).concat(Object.keys(afterIndex)).forEach(function(hash) { + if (hash in beforeIndex && hash in afterIndex) { + // print('reusing previous attachment', hash); + after[afterIndex[hash]] = before[beforeIndex[hash]]; + } else if (!(hash in afterIndex)) { + var attachment = before[beforeIndex[hash]]; + attachment.properties && attachment.properties.objectID && + modelHelper.deleteObject(attachment.properties.objectID); + delete attachment.properties; + } + }); + this.attachments = after; + this._createAttachmentObjects(); + this.attachmentsUpdated(after, before); + }, + _createAttachmentObjects: function() { + try { + var attachments = this.attachments, + parentID = this.doppleganger.objectID, + jointNames = this.doppleganger.jointNames, + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); + debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ + modelType: modelType, + attachments: attachments.length, + parentID: parentID, + jointNames: jointNames.join(' | '), + },0,2)); + return attachments.map(utils.bind(this, function(attachment, i) { + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); + return attachment; + } + var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), + scale = this.doppleganger.avatar.scale * (attachment.scale||1.0); + + attachment.properties = utils.assign({ + name: attachment.toString(), + type: modelType, + modelURL: attachment.modelUrl, + scale: scale, + dimensions: modelHelper.type(parentID) === 'entity' ? + Vec3.multiply(attachment.scale||1.0, Vec3.ONE) : undefined, + visible: false, + collisionless: true, + dynamic: false, + shapeType: 'none', + lifetime: 60, + grabbable: true, + }, !this.manualJointSync && { + parentID: parentID, + parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), + }); + var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); + attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); + attachment._modelReadier = new ModelReadyWatcher({ + resource: attachment._resource, + objectID: objectID, + }); + this.doppleganger.activeChanged.connect(attachment._modelReadier, '_stop'); + + attachment._modelReadier.modelReady.connect(this, function(err, result) { + if (err) { + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); + modelHelper.deleteObject(objectID); + return objectID = null; + } + debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); + var properties = modelHelper.getProperties(result.objectID), + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { + dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, + }); + this.onJointsUpdated(parentID); // trigger once to initialize position/rotation + // give time for model overlay to "settle", then make it visible + Script.setTimeout(utils.bind(this, function() { + modelHelper.editObject(objectID, { + visible: true, + }); + attachment._loaded = true; + }), 100); + }); + return attachment; + })); + } catch (e) { + log('_createAttachmentObjects ERROR:', e.stack || e, e.fileName, e.lineNumber); + } + }, + + _removeAttachmentObjects: function() { + if (this.attachments) { + this.attachments.forEach(function(attachment) { + if (attachment.properties) { + if (attachment.properties.objectID) { + modelHelper.deleteObject(attachment.properties.objectID); + } + delete attachment.properties.objectID; + } + }); + delete this.attachments; + } + }, + + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { + var jointOrientations = modelHelper.getJointOrientations(objectID), + jointPositions = modelHelper.getJointPositions(objectID), + parentID = objectID, + avatarScale = this.doppleganger.scale, + manualJointSync = this.manualJointSync; + + if (!this.attachments) { + this.refreshAttachments(); + } + var updatedObjects = this.attachments.reduce(function(updates, attachment, i) { + if (!attachment.properties || !attachment._loaded) { + return updates; + } + var objectID = attachment.properties.objectID, + jointIndex = attachment.$jointIndex, + jointOrientation = jointOrientations[jointIndex], + jointPosition = jointPositions[jointIndex]; + + var translation = Vec3.multiply(avatarScale, attachment.translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + localRotation = rotation; + + updates[objectID] = manualJointSync ? { + visible: true, + position: Vec3.sum(jointPosition, localPosition), + rotation: Quat.multiply(jointOrientation, localRotation), + scale: avatarScale * attachment.scale, + } : { + visible: true, + parentID: parentID, + parentJointIndex: jointIndex, + localRotation: localRotation, + localPosition: localPosition, + scale: attachment.scale, + }; + return updates; + }, {}); + modelHelper.editObjects(updatedObjects); + }, + + _initialize: function() { + var doppleganger = this.doppleganger; + if ('$attachmentControls' in doppleganger) { + throw new Error('only one set of attachment controls can be added per doppleganger'); + } + doppleganger.$attachmentControls = this; + doppleganger.activeChanged.connect(this, function(active) { + if (active) { + doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } else { + doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + this._removeAttachmentObjects(); + } + }); + + Script.scriptEnding.connect(this, '_removeAttachmentObjects'); + }, +}; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + + +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = __webpack_require__(2), + modelHelper = __webpack_require__(1).modelHelper, + utils = __webpack_require__(0); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype + + +/***/ }), +/* 6 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index a3b3873c2d..5dee264fae 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -1,10 +1,12 @@ "use strict"; /* eslint-env commonjs */ /* eslint-disable comma-dangle */ +/* global console */ +// var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.0'; +DopplegangerAttachments.version = '0.0.1a'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -13,8 +15,10 @@ var _modelHelper = require('./model-helper.js'), utils = require('./utils.js'); function log() { - print('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-attachments | ' + [].slice.call(arguments).join(' ')); } +log(DopplegangerAttachments.version); function debugPrint() { DopplegangerAttachments.WANT_DEBUG && log.apply(this, arguments); @@ -61,6 +65,9 @@ DopplegangerAttachments.prototype = { }, // compare before / after attachment sets to determine which ones need to be (re)created refreshAttachments: function() { + if (!this.doppleganger.objectID) { + return log('refreshAttachments -- canceling; !this.doppleganger.objectID'); + } var before = this.attachments || [], beforeIndex = before.reduce(function(out, att, index) { out[att.$hash] = index; return out; @@ -90,18 +97,19 @@ DopplegangerAttachments.prototype = { var attachments = this.attachments, parentID = this.doppleganger.objectID, jointNames = this.doppleganger.jointNames, - properties = modelHelper.getProperties(this.doppleganger.objectID); - + properties = modelHelper.getProperties(this.doppleganger.objectID), + modelType = properties && properties.type; + utils.assert(modelType === 'model' || modelType === 'Model', 'unrecognized doppleganger modelType:' + modelType); debugPrint('DopplegangerAttachments..._createAttachmentObjects', JSON.stringify({ - type: properties.type, + modelType: modelType, attachments: attachments.length, parentID: parentID, jointNames: jointNames.join(' | '), },0,2)); return attachments.map(utils.bind(this, function(attachment, i) { - var type = modelHelper.type(attachment.properties && attachment.properties.objectID); - if (type === 'overlay') { - debugPrint('skipping already-provisioned attachment object', type, attachment.properties && attachment.properties.name); + var objectType = modelHelper.type(attachment.properties && attachment.properties.objectID); + if (objectType === 'overlay') { + debugPrint('skipping already-provisioned attachment object', objectType, attachment.properties && attachment.properties.name); return attachment; } var jointIndex = attachment.$jointIndex, // jointNames.indexOf(attachment.jointName), @@ -109,7 +117,7 @@ DopplegangerAttachments.prototype = { attachment.properties = utils.assign({ name: attachment.toString(), - type: properties.type, + type: modelType, modelURL: attachment.modelUrl, scale: scale, dimensions: modelHelper.type(parentID) === 'entity' ? @@ -123,10 +131,13 @@ DopplegangerAttachments.prototype = { }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, + localPosition: attachment.translation, + localRotation: Quat.fromVec3Degrees(attachment.rotation), }); var objectID = attachment.properties.objectID = modelHelper.addObject(attachment.properties); + utils.assert(!Uuid.isNull(objectID), 'could not create attachment: ' + [objectID, JSON.stringify(attachment.properties,0,2)]); attachment._resource = ModelCache.prefetch(attachment.properties.modelURL); - attachment._modelReadier = new ModelReadyWatcher( { + attachment._modelReadier = new ModelReadyWatcher({ resource: attachment._resource, objectID: objectID, }); @@ -134,21 +145,23 @@ DopplegangerAttachments.prototype = { attachment._modelReadier.modelReady.connect(this, function(err, result) { if (err) { - log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.modelUrl); + log('>>>>> modelReady ERROR <<<<<: ' + err, attachment.properties.modelURL); modelHelper.deleteObject(objectID); return objectID = null; } debugPrint('attachment model ('+modelHelper.type(result.objectID)+') is ready; # joints ==', - result.jointNames && result.jointNames.length, result.naturalDimensions, result.objectID); + result.jointNames && result.jointNames.length, JSON.stringify(result.naturalDimensions), result.objectID); var properties = modelHelper.getProperties(result.objectID), - naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions; - modelHelper.editObject(result.objectID, { + naturalDimensions = attachment.properties.naturalDimensions = properties.naturalDimensions || result.naturalDimensions; + modelHelper.editObject(objectID, { dimensions: naturalDimensions ? Vec3.multiply(attachment.scale, naturalDimensions) : undefined, + localRotation: Quat.normalize({}), + localPosition: Vec3.ZERO, }); this.onJointsUpdated(parentID); // trigger once to initialize position/rotation // give time for model overlay to "settle", then make it visible Script.setTimeout(utils.bind(this, function() { - modelHelper.editObject(result.objectID, { + modelHelper.editObject(objectID, { visible: true, }); attachment._loaded = true; @@ -175,7 +188,7 @@ DopplegangerAttachments.prototype = { } }, - onJointsUpdated: function onJointsUpdated(objectID) { + onJointsUpdated: function onJointsUpdated(objectID, jointUpdates) { var jointOrientations = modelHelper.getJointOrientations(objectID), jointPositions = modelHelper.getJointPositions(objectID), parentID = objectID, @@ -195,8 +208,9 @@ DopplegangerAttachments.prototype = { jointPosition = jointPositions[jointIndex]; var translation = Vec3.multiply(avatarScale, attachment.translation), - rotation = Quat.fromVec3Degrees(attachment.rotation), - localPosition = Vec3.multiplyQbyV(jointOrientation, translation), + rotation = Quat.fromVec3Degrees(attachment.rotation); + + var localPosition = Vec3.multiplyQbyV(jointOrientation, translation), localRotation = rotation; updates[objectID] = manualJointSync ? { @@ -212,7 +226,6 @@ DopplegangerAttachments.prototype = { localPosition: localPosition, scale: attachment.scale, }; - onJointsUpdated[objectID] = updates[objectID]; return updates; }, {}); modelHelper.editObjects(updatedObjects); @@ -221,7 +234,7 @@ DopplegangerAttachments.prototype = { _initialize: function() { var doppleganger = this.doppleganger; if ('$attachmentControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); + throw new Error('only one set of attachment controls can be added per doppleganger'); } doppleganger.$attachmentControls = this; doppleganger.activeChanged.connect(this, function(active) { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js new file mode 100644 index 0000000000..b0a83117fb --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-debug.js @@ -0,0 +1,158 @@ +// -- ADVANCED DEBUGGING -- +// @function - Add debug joint indicators / extra debugging info. +// @param {Doppleganger} - existing Doppleganger instance to add controls to +// +// @note: +// * rightclick toggles mirror mode on/off +// * shift-rightclick toggles the debug indicators on/off +// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. +// +// Example use: +// var doppleganger = new DopplegangerClass(); +// DopplegangerClass.addDebugControls(doppleganger); + +"use strict"; +/* eslint-env commonjs */ +/* eslint-disable comma-dangle */ +/* global console */ + +var DopplegangerClass = require('./doppleganger.js'), + modelHelper = require('./model-helper.js').modelHelper, + utils = require('./utils.js'); + +module.exports = DebugControls; +// mixin addDebugControls to DopplegangerClass for backwards-compatibility +DopplegangerClass.addDebugControls = function(doppleganger) { + new DebugControls(doppleganger); + return doppleganger; +}; + +DebugControls.version = '0.0.0'; +DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; +DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; + +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger-debug | ' + [].slice.call(arguments).join(' ')); +} + +function DebugControls(doppleganger) { + this.enableIndicators = true; + this.selectedJointName = null; + this.debugOverlayIDs = undefined; + this.jointSelected = utils.signal(function(result) {}); + this.doppleganger = doppleganger; + this._initialize(); +} +DebugControls.prototype = { + start: function() { + if (!this.onMousePressEvent) { + this.onMousePressEvent = this._onMousePressEvent; + Controller.mousePressEvent.connect(this, 'onMousePressEvent'); + this.doppleganger.jointsUpdated.connect(this, 'onJointsUpdated'); + } + }, + + stop: function() { + this.removeIndicators(); + if (this.onMousePressEvent) { + this.doppleganger.jointsUpdated.disconnect(this, 'onJointsUpdated'); + Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); + delete this.onMousePressEvent; + } + }, + + createIndicators: function(jointNames) { + this.jointNames = jointNames; + return jointNames.map(function(name, i) { + return Overlays.addOverlay('shape', { + shape: 'Icosahedron', + scale: 0.1, + solid: false, + alpha: 0.5 + }); + }); + }, + + removeIndicators: function() { + if (this.debugOverlayIDs) { + this.debugOverlayIDs.forEach(Overlays.deleteOverlay); + this.debugOverlayIDs = undefined; + } + }, + + onJointsUpdated: function(overlayID) { + if (!this.enableIndicators) { + return; + } + var jointNames = Overlays.getProperty(overlayID, 'jointNames'), + jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), + jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), + selectedIndex = jointNames.indexOf(this.selectedJointName); + + if (!this.debugOverlayIDs) { + this.debugOverlayIDs = this.createIndicators(jointNames); + } + + // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) + var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { + updates[id] = { + position: jointPositions[i], + rotation: jointOrientations[i], + color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, + solid: i === selectedIndex + }; + return updates; + }, {}); + Overlays.editOverlays(updatedOverlays); + }, + + _onMousePressEvent: function(evt) { + if (evt.isLeftButton) { + if (!this.enableIndicators || !this.debugOverlayIDs) { + return; + } + var ray = Camera.computePickRay(evt.x, evt.y), + hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); + + hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); + hit.jointName = this.jointNames[hit.jointIndex]; + this.jointSelected(hit); + } else if (evt.isRightButton) { + if (evt.isShifted) { + this.enableIndicators = !this.enableIndicators; + if (!this.enableIndicators) { + this.removeIndicators(); + } + } else { + this.doppleganger.mirrored = !this.doppleganger.mirrored; + } + } + }, + + _initialize: function() { + if ('$debugControls' in this.doppleganger) { + throw new Error('only one set of debug controls can be added per doppleganger'); + } + this.doppleganger.$debugControls = this; + + this.doppleganger.activeChanged.connect(this, function(active) { + if (active) { + this.start(); + } else { + this.stop(); + } + }); + + this.jointSelected.connect(this, function(hit) { + this.selectedJointName = hit.jointName; + if (hit.jointIndex < 0) { + return; + } + hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; + log('selected joint:', JSON.stringify(hit, 0, 2)); + }); + + Script.scriptEnding.connect(this, 'removeIndicators'); + }, +}; // DebugControls.prototype diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js index bebd36df45..190a8aa69e 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js @@ -10,6 +10,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module doppleganger // // This module contains the `Doppleganger` class implementation for creating an inspectable replica of @@ -24,9 +25,13 @@ module.exports = Doppleganger; +Doppleganger.version = '0.0.1a'; +log(Doppleganger.version); + var _modelHelper = require('./model-helper.js'), modelHelper = _modelHelper.modelHelper, - ModelReadyWatcher = _modelHelper.ModelReadyWatcher; + ModelReadyWatcher = _modelHelper.ModelReadyWatcher, + utils = require('./utils.js'); // @property {bool} - toggle verbose debug logging on/off Doppleganger.WANT_DEBUG = false; @@ -48,16 +53,16 @@ function Doppleganger(options) { this.autoUpdate = 'autoUpdate' in options ? options.autoUpdate : true; // @public - this.active = false; // whether doppleganger is currently being displayed/updated + this.active = false; // whether doppleganger is currently being displayed/updated this.objectID = null; // current doppleganger's Overlay or Entity id - this.frame = 0; // current joint update frame + this.frame = 0; // current joint update frame // @signal - emitted when .active state changes - this.activeChanged = signal(function(active, reason) {}); + this.activeChanged = utils.signal(function(active, reason) {}); // @signal - emitted once model is either loaded or errors out - this.modelLoaded = signal(function(error, result){}); + this.modelLoaded = utils.signal(function(error, result){}); // @signal - emitted each time the model's joint data has been synchronized - this.jointsUpdated = signal(function(objectID){}); + this.jointsUpdated = utils.signal(function(objectID){}); } Doppleganger.prototype = { @@ -118,11 +123,12 @@ Doppleganger.prototype = { rotations = outRotations; translations = outTranslations; } - modelHelper.editObject(this.objectID, { + var jointUpdates = { jointRotations: rotations, jointTranslations: translations - }); - this.jointsUpdated(this.objectID); + }; + modelHelper.editObject(this.objectID, jointUpdates); + this.jointsUpdated(this.objectID, jointUpdates); } catch (e) { log('.update error: '+ e, index, e.stack); this.stop('update_error'); @@ -134,7 +140,7 @@ Doppleganger.prototype = { // @param {vec3} [options.position=(in front of avatar)] - starting position // @param {quat} [options.orientation=avatar.orientation] - starting orientation start: function(options) { - options = assign(this.options, options); + options = utils.assign(this.options, options); if (this.objectID) { log('start() called but object model already exists', this.objectID); return; @@ -194,11 +200,11 @@ Doppleganger.prototype = { return this.stop(error); } debugPrint('model ('+modelHelper.type(this.objectID)+')' + ' is ready; # joints == ' + result.jointNames.length); - var naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; + var naturalDimensions = this.naturalDimensions = modelHelper.getProperties(this.objectID, ['naturalDimensions']).naturalDimensions; debugPrint('naturalDimensions:', JSON.stringify(naturalDimensions)); var props = { visible: true }; if (naturalDimensions) { - props.dimensions = Vec3.multiply(this.scale, naturalDimensions); + props.dimensions = this.dimensions = Vec3.multiply(this.scale, naturalDimensions); } debugPrint('scaledDimensions:', this.scale, JSON.stringify(props.dimensions)); modelHelper.editObject(this.objectID, props); @@ -296,220 +302,18 @@ Doppleganger.prototype = { } else { debugPrint('creating Script.setInterval thread @ ~', Doppleganger.TARGET_FPS +'fps'); var timeout = 1000 / Doppleganger.TARGET_FPS; - this._interval = Script.setInterval(bind(this, 'update'), timeout); + this._interval = Script.setInterval(utils.bind(this, 'update'), timeout); } } }; -// @function - bind a function to a `this` context -// @param {Object} - the `this` context -// @param {Function|String} - function or method name -function bind(thiz, method) { - method = thiz[method] || method; - return function() { - return method.apply(thiz, arguments); - }; -} - -// @function - Qt signal polyfill -function signal(template) { - var callbacks = []; - return Object.defineProperties(function() { - var args = [].slice.call(arguments); - callbacks.forEach(function(obj) { - obj.handler.apply(obj.scope, args); - }); - }, { - connect: { value: function(scope, handler) { - var callback = {scope: scope, handler: scope[handler] || handler || scope}; - if (!callback.handler || !callback.handler.apply) { - throw new Error('invalid arguments to connect:' + [template, scope, handler]); - } - callbacks.push({scope: scope, handler: scope[handler] || handler || scope}); - }}, - disconnect: { value: function(scope, handler) { - var match = {scope: scope, handler: scope[handler] || handler || scope}; - callbacks = callbacks.filter(function(obj) { - return !(obj.scope === match.scope && obj.handler === match.handler); - }); - }} - }); -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill -/* eslint-disable */ -function assign(target, varArgs) { // .length of function is 2 - 'use strict'; - if (target == null) { // TypeError if undefined or null - throw new TypeError('Cannot convert undefined or null to object'); - } - - var to = Object(target); - - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - - if (nextSource != null) { // Skip over if undefined or null - for (var nextKey in nextSource) { - // Avoid bugs when hasOwnProperty is shadowed - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; -} -/* eslint-enable */ -// //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill - // @function - debug logging function log() { - print('doppleganger | ' + [].slice.call(arguments).join(' ')); + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('doppleganger | ' + [].slice.call(arguments).join(' ')); } function debugPrint() { Doppleganger.WANT_DEBUG && log.apply(this, arguments); } - -// -- ADVANCED DEBUGGING -- -// @function - Add debug joint indicators / extra debugging info. -// @param {Doppleganger} - existing Doppleganger instance to add controls to -// -// @note: -// * rightclick toggles mirror mode on/off -// * shift-rightclick toggles the debug indicators on/off -// * clicking on an indicator displays the joint name and mirrored joint name in the debug log. -// -// Example use: -// var doppleganger = new Doppleganger(); -// Doppleganger.addDebugControls(doppleganger); -Doppleganger.addDebugControls = function(doppleganger) { - DebugControls.COLOR_DEFAULT = { red: 255, blue: 255, green: 255 }; - DebugControls.COLOR_SELECTED = { red: 0, blue: 255, green: 0 }; - - function DebugControls() { - this.enableIndicators = true; - this.selectedJointName = null; - this.debugOverlayIDs = undefined; - this.jointSelected = signal(function(result) {}); - } - DebugControls.prototype = { - start: function() { - if (!this.onMousePressEvent) { - this.onMousePressEvent = this._onMousePressEvent; - Controller.mousePressEvent.connect(this, 'onMousePressEvent'); - } - }, - - stop: function() { - this.removeIndicators(); - if (this.onMousePressEvent) { - Controller.mousePressEvent.disconnect(this, 'onMousePressEvent'); - delete this.onMousePressEvent; - } - }, - - createIndicators: function(jointNames) { - this.jointNames = jointNames; - return jointNames.map(function(name, i) { - return Overlays.addOverlay('shape', { - shape: 'Icosahedron', - scale: 0.1, - solid: false, - alpha: 0.5 - }); - }); - }, - - removeIndicators: function() { - if (this.debugOverlayIDs) { - this.debugOverlayIDs.forEach(Overlays.deleteOverlay); - this.debugOverlayIDs = undefined; - } - }, - - onJointsUpdated: function(overlayID) { - if (!this.enableIndicators) { - return; - } - var jointNames = Overlays.getProperty(overlayID, 'jointNames'), - jointOrientations = Overlays.getProperty(overlayID, 'jointOrientations'), - jointPositions = Overlays.getProperty(overlayID, 'jointPositions'), - selectedIndex = jointNames.indexOf(this.selectedJointName); - - if (!this.debugOverlayIDs) { - this.debugOverlayIDs = this.createIndicators(jointNames); - } - - // batch all updates into a single call (using the editOverlays({ id: {props...}, ... }) API) - var updatedOverlays = this.debugOverlayIDs.reduce(function(updates, id, i) { - updates[id] = { - position: jointPositions[i], - rotation: jointOrientations[i], - color: i === selectedIndex ? DebugControls.COLOR_SELECTED : DebugControls.COLOR_DEFAULT, - solid: i === selectedIndex - }; - return updates; - }, {}); - Overlays.editOverlays(updatedOverlays); - }, - - _onMousePressEvent: function(evt) { - if (!evt.isLeftButton || !this.enableIndicators || !this.debugOverlayIDs) { - return; - } - var ray = Camera.computePickRay(evt.x, evt.y), - hit = Overlays.findRayIntersection(ray, true, this.debugOverlayIDs); - - hit.jointIndex = this.debugOverlayIDs.indexOf(hit.overlayID); - hit.jointName = this.jointNames[hit.jointIndex]; - this.jointSelected(hit); - } - }; - - if ('$debugControls' in doppleganger) { - throw new Error('only one set of debug controls can be added per doppleganger'); - } - var debugControls = new DebugControls(); - doppleganger.$debugControls = debugControls; - - function onMousePressEvent(evt) { - if (evt.isRightButton) { - if (evt.isShifted) { - debugControls.enableIndicators = !debugControls.enableIndicators; - if (!debugControls.enableIndicators) { - debugControls.removeIndicators(); - } - } else { - doppleganger.mirrored = !doppleganger.mirrored; - } - } - } - - doppleganger.activeChanged.connect(function(active) { - if (active) { - debugControls.start(); - doppleganger.jointsUpdated.connect(debugControls, 'onJointsUpdated'); - Controller.mousePressEvent.connect(onMousePressEvent); - } else { - Controller.mousePressEvent.disconnect(onMousePressEvent); - doppleganger.jointsUpdated.disconnect(debugControls, 'onJointsUpdated'); - debugControls.stop(); - } - }); - - debugControls.jointSelected.connect(function(hit) { - debugControls.selectedJointName = hit.jointName; - if (hit.jointIndex < 0) { - return; - } - hit.mirroredJointName = modelHelper.deriveMirroredJointNames([hit.jointName])[0]; - log('selected joint:', JSON.stringify(hit, 0, 2)); - }); - - Script.scriptEnding.connect(debugControls, 'removeIndicators'); - - return doppleganger; -}; diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/makefile b/unpublishedScripts/marketplace/doppleganger-attachments/makefile new file mode 100644 index 0000000000..eaf02dbed9 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/makefile @@ -0,0 +1,11 @@ +all: + @echo "make dist" + +dist: doppleganger-a.svg.json doppleganger-i.svg.json dist/app-doppleganger-marketplace.js + @echo "OK" + +%.svg.json: %.svg + cat $< | jq -sR '"data:image/svg+xml;xml,"+.' > $@ + +dist/app-doppleganger-marketplace.js: *.js + ./node_modules/.bin/webpack --verbose app-doppleganger-attachments.js $@ diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js index 2dda2c12ec..8edaf50cb7 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/model-helper.js @@ -8,6 +8,7 @@ // /* eslint-env commonjs */ +/* global console */ // @module model-helper // // This module provides ModelReadyWatcher (a helper class for knowing when a model becomes usable inworld) and @@ -18,10 +19,16 @@ var utils = require('./utils.js'), assert = utils.assert; module.exports = { - version: '0.0.0', + version: '0.0.1', ModelReadyWatcher: ModelReadyWatcher }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('model-helper | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + var _objectDeleted = utils.signal(function objectDeleted(objectID){}); // proxy for _objectDeleted that only binds deletion tracking if script actually connects to the unified signal var objectDeleted = utils.assign(function objectDeleted(objectID){}, { diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/package.json b/unpublishedScripts/marketplace/doppleganger-attachments/package.json new file mode 100644 index 0000000000..f18136fc1b --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "webpack": "^3.0.0" + } +} diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/readme.md b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md new file mode 100644 index 0000000000..d0ebf2d6e1 --- /dev/null +++ b/unpublishedScripts/marketplace/doppleganger-attachments/readme.md @@ -0,0 +1,4 @@ +note: to rebuild webpack version: +* install `jq` https://stedolan.github.io/jq (used to encode the icon.svg's as Data URI JSON strings) +* `npm install` +* `make dist` diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js index 76c6e1ef7f..b34af1e632 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/utils.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/utils.js @@ -1,12 +1,20 @@ /* eslint-env commonjs */ +/* global console */ module.exports = { + version: '0.0.1', bind: bind, signal: signal, assign: assign, assert: assert }; +function log() { + // eslint-disable-next-line no-console + (typeof console === 'object' ? console.log : print)('utils | ' + [].slice.call(arguments).join(' ')); +} +log(module.exports.version); + // @function - bind a function to a `this` context // @param {Object} - the `this` context // @param {Function|String} - function or method name From 9c610f52eca11a94a637a9f408fb6f0c597a0628 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 17 Jul 2017 15:34:41 -0400 Subject: [PATCH 033/269] 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 96f52a9812bd77d3931c46c03a91883a73002147 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:05:16 -0700 Subject: [PATCH 034/269] First pass at getting overlays to show up on hover --- interface/src/Application.cpp | 2 +- .../src/ui/overlays/HoverOverlayInterface.cpp | 72 +++++++++++++++++++ .../src/ui/overlays}/HoverOverlayInterface.h | 17 ++++- .../src/ui/overlays}/HoverOverlayLogging.cpp | 2 +- .../src/ui/overlays}/HoverOverlayLogging.h | 2 +- .../src/EntityTreeRenderer.cpp | 7 -- .../entities/src/HoverOverlayInterface.cpp | 30 -------- 7 files changed, 90 insertions(+), 42 deletions(-) create mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayInterface.h (67%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.cpp (92%) rename {libraries/entities/src => interface/src/ui/overlays}/HoverOverlayLogging.h (93%) delete mode 100644 libraries/entities/src/HoverOverlayInterface.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c73d6614f1..bc9fa25af0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include +#include "ui/overlays/HoverOverlayInterface.h" #include #include #include diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp new file mode 100644 index 0000000000..8576c0b622 --- /dev/null +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -0,0 +1,72 @@ +// +// HoverOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HoverOverlayInterface.h" +#include "Application.h" +#include + +HoverOverlayInterface::HoverOverlayInterface() { + // "hover_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { + _hoverOverlay = std::make_shared(); + _hoverOverlay->setAlpha(1.0f); + _hoverOverlay->setBorderSize(1.0f); + _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); + _hoverOverlay->setIsSolid(true); + _hoverOverlay->setPulseMin(0.5); + _hoverOverlay->setPulseMax(1.0); + _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setIgnoreRayIntersection(false); + _hoverOverlay->setDrawInFront(false); + _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); + } + + _hoverOverlay->setPosition(entityProperties.getPosition()); + _hoverOverlay->setRotation(entityProperties.getRotation()); + _hoverOverlay->setVisible(true); +} + +void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; + setCurrentHoveredEntity(QUuid()); + + qApp->getOverlays().deleteOverlay(_hoverOverlayID); + _hoverOverlay = NULL; + _hoverOverlayID = UNKNOWN_OVERLAY_ID; +} + +void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { + HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); +} diff --git a/libraries/entities/src/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h similarity index 67% rename from libraries/entities/src/HoverOverlayInterface.h rename to interface/src/ui/overlays/HoverOverlayInterface.h index 1e56cc03dd..8322c3e75c 100644 --- a/libraries/entities/src/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -1,6 +1,6 @@ // // HoverOverlayInterface.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-14. // Copyright 2017 High Fidelity, Inc. @@ -18,14 +18,24 @@ #include #include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Overlays.h" #include "EntityTree.h" #include "HoverOverlayLogging.h" +/**jsdoc +* @namespace HoverOverlay +*/ class HoverOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); @@ -34,11 +44,14 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyHoverOverlay(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; - QUuid _currentHoveredEntity{}; + QUuid _currentHoveredEntity {}; + }; #endif // hifi_HoverOverlayInterface_h diff --git a/libraries/entities/src/HoverOverlayLogging.cpp b/interface/src/ui/overlays/HoverOverlayLogging.cpp similarity index 92% rename from libraries/entities/src/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/HoverOverlayLogging.cpp index 99a2dff782..69c977a58c 100644 --- a/libraries/entities/src/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/HoverOverlayLogging.cpp @@ -1,6 +1,6 @@ // // HoverOverlayLogging.cpp -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities/src/HoverOverlayLogging.h b/interface/src/ui/overlays/HoverOverlayLogging.h similarity index 93% rename from libraries/entities/src/HoverOverlayLogging.h rename to interface/src/ui/overlays/HoverOverlayLogging.h index f0a024bba9..965a1c6d03 100644 --- a/libraries/entities/src/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/HoverOverlayLogging.h @@ -1,6 +1,6 @@ // // HoverOverlayLogging.h -// libraries/entities/src +// interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 // Copyright 2017 High Fidelity, Inc. diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 475471b0fb..a8eca41077 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include "RenderableEntityItem.h" @@ -453,8 +452,6 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) { - auto hoverOverlayInterface = DependencyManager::get().data(); - connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); @@ -464,12 +461,8 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity); connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); - connect(this, &EntityTreeRenderer::hoverEnterEntity, hoverOverlayInterface, &HoverOverlayInterface::createHoverOverlay); - connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); - connect(this, &EntityTreeRenderer::hoverLeaveEntity, hoverOverlayInterface, &HoverOverlayInterface::destroyHoverOverlay); connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); diff --git a/libraries/entities/src/HoverOverlayInterface.cpp b/libraries/entities/src/HoverOverlayInterface.cpp deleted file mode 100644 index 85b2738fc4..0000000000 --- a/libraries/entities/src/HoverOverlayInterface.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// HoverOverlayInterface.cpp -// libraries/entities/src -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); -} From 67c8ddfd57c0861af35209f5269395f3405b700a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Jul 2017 17:09:40 -0700 Subject: [PATCH 035/269] Merge in changes from laser PR --- interface/src/Application.cpp | 2 ++ scripts/system/controllers/handControllerGrab.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc9fa25af0..327643df1d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2124,6 +2124,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5826,6 +5827,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); + scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 04921fe14d..78c4b2960e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var entitiesWithHoverOverlays = []; + var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2201,6 +2203,15 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID); + entitiesWithHoverOverlays.push(rayPickInfo.entityID); + } + var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); @@ -3763,6 +3774,11 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); + entitiesWithHoverOverlays.forEach(function (element) { + HoverOverlay.destroyHoverOverlay(element); + }); + entitiesWithHoverOverlays = []; + if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 4ad36ccec9bdf1bd8951fd41cfe15063409c1b32 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Jul 2017 16:21:28 -0700 Subject: [PATCH 036/269] 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 2b1a0921da5d0961c446064cd7ecf6d16059a2b0 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:01:16 -0700 Subject: [PATCH 037/269] Initial work on clickable overlays --- interface/src/Application.cpp | 9 ++++- .../src/ui/overlays/HoverOverlayInterface.cpp | 10 +++-- .../src/ui/overlays/HoverOverlayInterface.h | 2 +- interface/src/ui/overlays/Overlays.cpp | 12 +++--- interface/src/ui/overlays/Overlays.h | 12 +++--- .../system/controllers/handControllerGrab.js | 39 +++++++++++++------ 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 327643df1d..b9a76ef58c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1323,12 +1323,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Keyboard focus handling for Web overlays. auto overlays = &(qApp->getOverlays()); - connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) { + connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); }); - connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) { + connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { if (overlayID == _keyboardFocusedOverlay.get()) { setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); } @@ -1343,6 +1343,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); + connect(overlays, + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 8576c0b622..655f167074 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -54,10 +54,6 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, _hoverOverlay->setVisible(true); } -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::createHoverOverlay(entityItemID, PointerEvent()); -} - void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; setCurrentHoveredEntity(QUuid()); @@ -70,3 +66,9 @@ void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); } + +void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _hoverOverlayID) { + qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 8322c3e75c..7cda22a0c1 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -44,9 +44,9 @@ public: public slots: void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void createHoverOverlay(const EntityItemID& entityItemID); void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyHoverOverlay(const EntityItemID& entityItemID); + void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 72682fcb8c..b3ab4599a5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -705,27 +705,27 @@ bool Overlays::isAddedOverlay(OverlayID id) { return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); } -void Overlays::sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mousePressOnOverlay(overlayID, event); } -void Overlays::sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseReleaseOnOverlay(overlayID, event); } -void Overlays::sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event) { +void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { emit mouseMoveOnOverlay(overlayID, event); } -void Overlays::sendHoverEnterOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverEnterOverlay(id, event); } -void Overlays::sendHoverOverOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverOverOverlay(id, event); } -void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) { +void Overlays::sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event) { emit hoverLeaveOverlay(id, event); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 100f853a96..3d9ac93c81 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -288,13 +288,13 @@ public slots: #endif - void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); - void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); + void sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event); - void sendHoverEnterOverlay(OverlayID id, PointerEvent event); - void sendHoverOverOverlay(OverlayID id, PointerEvent event); - void sendHoverLeaveOverlay(OverlayID id, PointerEvent event); + void sendHoverEnterOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverOverOverlay(const OverlayID& id, const PointerEvent& event); + void sendHoverLeaveOverlay(const OverlayID& id, const PointerEvent& event); OverlayID getKeyboardFocusOverlay(); void setKeyboardFocusOverlay(OverlayID id); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 78c4b2960e..dc9d2eb521 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entitiesWithHoverOverlays = []; +var entityWithHoverOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -1391,6 +1391,11 @@ function MyController(hand) { color: color, endParentID: farParentID }); + + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2203,13 +2208,21 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && entitiesWithHoverOverlays.indexOf(rayPickInfo.entityID) == -1) { - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID); - entitiesWithHoverOverlays.push(rayPickInfo.entityID); + if (rayPickInfo.entityID) + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + } + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); + entityWithHoverOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3484,6 +3497,8 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); + //HoverOverlay + this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3774,10 +3789,10 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - entitiesWithHoverOverlays.forEach(function (element) { - HoverOverlay.destroyHoverOverlay(element); - }); - entitiesWithHoverOverlays = []; + if (entityWithHoverOverlay) { + HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + entityWithHoverOverlay = false; + } if (this.grabbedThingID !== null) { From aeabfe84f0cec8ea0f908da96eeda84f3bbd277d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 17 Jul 2017 18:54:52 -0700 Subject: [PATCH 038/269] 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 039/269] 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 084f4d2ab023a20b1dc0827122fb99696e7ed360 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 14:42:59 -0700 Subject: [PATCH 040/269] Why were Web3DOverlays done this way? --- interface/src/Application.cpp | 8 +++++-- interface/src/ui/overlays/Overlays.cpp | 31 +++++++++++++------------- interface/src/ui/overlays/Overlays.h | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9a76ef58c..eacd9bf794 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1324,8 +1324,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto overlays = &(qApp->getOverlays()); connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { - setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); - setKeyboardFocusOverlay(overlayID); + auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); + // Only Web overlays can have focus. + if (thisOverlay) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + setKeyboardFocusOverlay(overlayID); + } }); connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) { diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b3ab4599a5..862152bea9 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,7 +818,7 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { @@ -875,12 +875,13 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + PointerEvent pointerEvent; + if (thisWeb3DOverlay) { + pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); } + emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mousePressOffOverlay(); return false; @@ -895,9 +896,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { _currentClickingOnOverlayID = rayPickResult.overlayID; // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -914,9 +915,9 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { if (rayPickResult.intersects) { // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Release); + auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); + if (thisWeb3DOverlay) { + auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } } @@ -935,14 +936,14 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { // Only Web overlays can have focus. auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); // If previously hovering over a different overlay then leave hover on that overlay. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } } @@ -962,7 +963,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); if (thisOverlay) { - auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); + auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3d9ac93c81..7235937077 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From 4b94d24c2922a3028cf9898f367b6b7077530260 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 18 Jul 2017 23:51:19 +0200 Subject: [PATCH 041/269] 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 265f978a06ff26c1a6645eed3d205146843c5e4a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 15:40:10 -0700 Subject: [PATCH 042/269] It's working! --- .../src/ui/overlays/HoverOverlayInterface.cpp | 16 +++---- .../src/ui/overlays/HoverOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 44 +++++++------------ 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp index 655f167074..1ccbbafb1b 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ b/interface/src/ui/overlays/HoverOverlayInterface.cpp @@ -36,16 +36,16 @@ void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); + _hoverOverlay = std::make_shared(); _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setBorderSize(1.0f); - _hoverOverlay->setColor({ 0xFF, 0xEF, 0x00 }); - _hoverOverlay->setIsSolid(true); - _hoverOverlay->setPulseMin(0.5); - _hoverOverlay->setPulseMax(1.0); - _hoverOverlay->setColorPulse(1.0); + _hoverOverlay->setPulseMin(0.75f); + _hoverOverlay->setPulseMax(1.0f); + _hoverOverlay->setColorPulse(1.0f); _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(false); + _hoverOverlay->setDrawInFront(true); + _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _hoverOverlay->setIsFacingAvatar(true); + _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); } diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h index 7cda22a0c1..4ae14ffd1d 100644 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ b/interface/src/ui/overlays/HoverOverlayInterface.h @@ -19,7 +19,7 @@ #include #include #include "EntityScriptingInterface.h" -#include "ui/overlays/Cube3DOverlay.h" +#include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" #include "EntityTree.h" @@ -35,7 +35,7 @@ class HoverOverlayInterface : public QObject, public Dependency { QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; + std::shared_ptr _hoverOverlay { nullptr }; public: HoverOverlayInterface(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index dc9d2eb521..7ee9a33485 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1391,11 +1391,6 @@ function MyController(hand) { color: color, endParentID: farParentID }); - - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; - } } else { Overlays.editOverlay(this.overlayLine, { length: Vec3.distance(farPoint, closePoint), @@ -2208,14 +2203,14 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID) + if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { if (entityWithHoverOverlay) { HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); } var pointerEvent = { type: "Move", id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, @@ -2479,27 +2474,24 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if (Overlays.getProperty(overlay, "type") != "web3d") { - return false; - } - if (Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; - - pointerEvent = { - type: "Move", - id: HARDWARE_MOUSE_ID, - pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); } + pointerEvent = { + type: "Move", + id: HARDWARE_MOUSE_ID, + pos2D: projectOntoOverlayXYPlane(overlay, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && @@ -3497,8 +3489,6 @@ function MyController(hand) { Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent); Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent); - //HoverOverlay - this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; this.touchingEnterPointerEvent.button = "None"; @@ -3595,7 +3585,7 @@ function MyController(hand) { }; this.overlayLaserTouchingEnter = function () { - // Test for intersection between controller laser and Web overlay plane. + // Test for intersection between controller laser and overlay plane. var controllerLocation = getControllerWorldLocation(this.handToController(), true); var intersectInfo = handLaserIntersectOverlay(this.grabbedOverlay, controllerLocation); if (intersectInfo) { From 009df176c511a7acde67a4baaa709263bfe0936d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 18 Jul 2017 13:44:58 -0700 Subject: [PATCH 043/269] 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 8f6af3a1ab3bc0ee5c8746aa45d44d0b29980fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:11:43 -0700 Subject: [PATCH 044/269] ContextOverlay --- interface/src/Application.cpp | 14 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 74 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 57 ++++++++++++++ ...yLogging.cpp => ContextOverlayLogging.cpp} | 6 +- ...erlayLogging.h => ContextOverlayLogging.h} | 10 +-- .../src/ui/overlays/HoverOverlayInterface.cpp | 74 ------------------- .../src/ui/overlays/HoverOverlayInterface.h | 57 -------------- .../system/controllers/handControllerGrab.js | 18 ++--- 8 files changed, 155 insertions(+), 155 deletions(-) create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.cpp create mode 100644 interface/src/ui/overlays/ContextOverlayInterface.h rename interface/src/ui/overlays/{HoverOverlayLogging.cpp => ContextOverlayLogging.cpp} (67%) rename interface/src/ui/overlays/{HoverOverlayLogging.h => ContextOverlayLogging.h} (62%) delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.cpp delete mode 100644 interface/src/ui/overlays/HoverOverlayInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eacd9bf794..8689d17032 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -69,7 +69,7 @@ #include #include #include -#include "ui/overlays/HoverOverlayInterface.h" +#include "ui/overlays/ContextOverlayInterface.h" #include #include #include @@ -589,7 +589,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -1325,7 +1325,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) { auto thisOverlay = std::dynamic_pointer_cast(overlays->getOverlay(overlayID)); - // Only Web overlays can have focus. + // Only Web overlays can have keyboard focus. if (thisOverlay) { setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); setKeyboardFocusOverlay(overlayID); @@ -1349,8 +1349,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickHoverOverlay(const OverlayID&, const PointerEvent&))); + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; @@ -2133,7 +2133,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); - surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get().data()); + surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get().data()); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get())); @@ -5836,7 +5836,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri auto entityScriptServerLog = DependencyManager::get(); scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data()); scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance()); - scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get().data()); qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp new file mode 100644 index 0000000000..cb15c544c7 --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -0,0 +1,74 @@ +// +// ContextOverlayInterface.cpp +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ContextOverlayInterface.h" +#include "Application.h" +#include + +ContextOverlayInterface::ContextOverlayInterface() { + // "context_overlay" debug log category disabled by default. + // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable + // if you'd like to enable/disable certain categories. + // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories + QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); + + _entityScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; + _entityPropertyFlags += PROP_ROTATION; + + auto entityTreeRenderer = DependencyManager::get().data(); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); +} + +void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; +} + +void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +} + +void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (overlayID == _contextOverlayID) { + qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h new file mode 100644 index 0000000000..05215e3e5c --- /dev/null +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -0,0 +1,57 @@ +// +// ContextOverlayInterface.h +// interface/src/ui/overlays +// +// Created by Zach Fox on 2017-07-14. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_ContextOverlayInterface_h +#define hifi_ContextOverlayInterface_h + +#include +#include + +#include +#include +#include "EntityScriptingInterface.h" +#include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Overlays.h" + +#include "EntityTree.h" +#include "ContextOverlayLogging.h" + +/**jsdoc +* @namespace ContextOverlay +*/ +class ContextOverlayInterface : public QObject, public Dependency { + Q_OBJECT + + Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + QSharedPointer _entityScriptingInterface; + EntityPropertyFlags _entityPropertyFlags; + OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + std::shared_ptr _contextOverlay { nullptr }; +public: + ContextOverlayInterface(); + + Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } + void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + +public slots: + void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void destroyContextOverlay(const EntityItemID& entityItemID); + void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + +private: + bool _verboseLogging { true }; + QUuid _currentEntityWithContextOverlay{}; + +}; + +#endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/HoverOverlayLogging.cpp b/interface/src/ui/overlays/ContextOverlayLogging.cpp similarity index 67% rename from interface/src/ui/overlays/HoverOverlayLogging.cpp rename to interface/src/ui/overlays/ContextOverlayLogging.cpp index 69c977a58c..c2c3fb7734 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.cpp +++ b/interface/src/ui/overlays/ContextOverlayLogging.cpp @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.cpp +// ContextOverlayLogging.cpp // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -9,6 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "HoverOverlayLogging.h" +#include "ContextOverlayLogging.h" -Q_LOGGING_CATEGORY(hover_overlay, "hifi.hover_overlay") +Q_LOGGING_CATEGORY(context_overlay, "hifi.context_overlay") diff --git a/interface/src/ui/overlays/HoverOverlayLogging.h b/interface/src/ui/overlays/ContextOverlayLogging.h similarity index 62% rename from interface/src/ui/overlays/HoverOverlayLogging.h rename to interface/src/ui/overlays/ContextOverlayLogging.h index 965a1c6d03..182ebc1425 100644 --- a/interface/src/ui/overlays/HoverOverlayLogging.h +++ b/interface/src/ui/overlays/ContextOverlayLogging.h @@ -1,5 +1,5 @@ // -// HoverOverlayLogging.h +// ContextOverlayLogging.h // interface/src/ui/overlays // // Created by Zach Fox on 2017-07-17 @@ -10,11 +10,11 @@ // #pragma once -#ifndef hifi_HoverOverlayLogging_h -#define hifi_HoverOverlayLogging_h +#ifndef hifi_ContextOverlayLogging_h +#define hifi_ContextOverlayLogging_h #include -Q_DECLARE_LOGGING_CATEGORY(hover_overlay) +Q_DECLARE_LOGGING_CATEGORY(context_overlay) -#endif // hifi_HoverOverlayLogging_h +#endif // hifi_ContextOverlayLogging_h diff --git a/interface/src/ui/overlays/HoverOverlayInterface.cpp b/interface/src/ui/overlays/HoverOverlayInterface.cpp deleted file mode 100644 index 1ccbbafb1b..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// -// HoverOverlayInterface.cpp -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HoverOverlayInterface.h" -#include "Application.h" -#include - -HoverOverlayInterface::HoverOverlayInterface() { - // "hover_overlay" debug log category disabled by default. - // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable - // if you'd like to enable/disable certain categories. - // More details: http://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories - QLoggingCategory::setFilterRules(QStringLiteral("hifi.hover_overlay.debug=false")); - - _entityScriptingInterface = DependencyManager::get(); - _entityPropertyFlags += PROP_POSITION; - _entityPropertyFlags += PROP_ROTATION; - - auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createHoverOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyHoverOverlay(const EntityItemID&, const PointerEvent&))); -} - -void HoverOverlayInterface::createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Creating Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(entityItemID); - - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - - if (_hoverOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_hoverOverlayID)) { - _hoverOverlay = std::make_shared(); - _hoverOverlay->setAlpha(1.0f); - _hoverOverlay->setPulseMin(0.75f); - _hoverOverlay->setPulseMax(1.0f); - _hoverOverlay->setColorPulse(1.0f); - _hoverOverlay->setIgnoreRayIntersection(false); - _hoverOverlay->setDrawInFront(true); - _hoverOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _hoverOverlay->setIsFacingAvatar(true); - _hoverOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _hoverOverlayID = qApp->getOverlays().addOverlay(_hoverOverlay); - } - - _hoverOverlay->setPosition(entityProperties.getPosition()); - _hoverOverlay->setRotation(entityProperties.getRotation()); - _hoverOverlay->setVisible(true); -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(hover_overlay) << "Destroying Hover Overlay on top of entity with ID: " << entityItemID; - setCurrentHoveredEntity(QUuid()); - - qApp->getOverlays().deleteOverlay(_hoverOverlayID); - _hoverOverlay = NULL; - _hoverOverlayID = UNKNOWN_OVERLAY_ID; -} - -void HoverOverlayInterface::destroyHoverOverlay(const EntityItemID& entityItemID) { - HoverOverlayInterface::destroyHoverOverlay(entityItemID, PointerEvent()); -} - -void HoverOverlayInterface::clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _hoverOverlayID) { - qCDebug(hover_overlay) << "Clicked Hover Overlay. Entity ID:" << _currentHoveredEntity << "Overlay ID:" << overlayID; - } -} diff --git a/interface/src/ui/overlays/HoverOverlayInterface.h b/interface/src/ui/overlays/HoverOverlayInterface.h deleted file mode 100644 index 4ae14ffd1d..0000000000 --- a/interface/src/ui/overlays/HoverOverlayInterface.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// HoverOverlayInterface.h -// interface/src/ui/overlays -// -// Created by Zach Fox on 2017-07-14. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once -#ifndef hifi_HoverOverlayInterface_h -#define hifi_HoverOverlayInterface_h - -#include -#include - -#include -#include -#include "EntityScriptingInterface.h" -#include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Overlays.h" - -#include "EntityTree.h" -#include "HoverOverlayLogging.h" - -/**jsdoc -* @namespace HoverOverlay -*/ -class HoverOverlayInterface : public QObject, public Dependency { - Q_OBJECT - - Q_PROPERTY(QUuid currentHoveredEntity READ getCurrentHoveredEntity WRITE setCurrentHoveredEntity) - QSharedPointer _entityScriptingInterface; - EntityPropertyFlags _entityPropertyFlags; - OverlayID _hoverOverlayID { UNKNOWN_OVERLAY_ID }; - std::shared_ptr _hoverOverlay { nullptr }; -public: - HoverOverlayInterface(); - - Q_INVOKABLE QUuid getCurrentHoveredEntity() { return _currentHoveredEntity; } - void setCurrentHoveredEntity(const QUuid& entityID) { _currentHoveredEntity = entityID; } - -public slots: - void createHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyHoverOverlay(const EntityItemID& entityItemID); - void clickHoverOverlay(const OverlayID& overlayID, const PointerEvent& event); - -private: - bool _verboseLogging { true }; - QUuid _currentHoveredEntity {}; - -}; - -#endif // hifi_HoverOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 7ee9a33485..1ab49d223e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var entityWithHoverOverlay = false; +var entityWithContextOverlay = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -2203,9 +2203,9 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithHoverOverlay !== rayPickInfo.entityID)) { - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); + if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); } var pointerEvent = { type: "Move", @@ -2216,8 +2216,8 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "None" }; - HoverOverlay.createHoverOverlay(rayPickInfo.entityID, pointerEvent); - entityWithHoverOverlay = rayPickInfo.entityID; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + entityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3779,9 +3779,9 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithHoverOverlay) { - HoverOverlay.destroyHoverOverlay(entityWithHoverOverlay); - entityWithHoverOverlay = false; + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } if (this.grabbedThingID !== null) { From 82111a8f6afc83f3347494a863ff214bc5d0a58a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 16:40:18 -0700 Subject: [PATCH 045/269] Timer for hand controller lasers --- .../system/controllers/handControllerGrab.js | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1ab49d223e..2b84c3d507 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -224,6 +224,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", enterMethod: "searchEnter", + exitMethod: "searchExit", updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { @@ -2174,6 +2175,13 @@ function MyController(hand) { } }; + this.searchExit = function () { + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + }; + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -2206,17 +2214,22 @@ function MyController(hand) { if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; } - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + Script.setTimeout(function() { + if (rayPickInfo.entityID === entityWithContextOverlay) { + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + } + }, 500); entityWithContextOverlay = rayPickInfo.entityID; } @@ -3779,11 +3792,6 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } - if (this.grabbedThingID !== null) { Messages.sendMessage('Hifi-Teleport-Ignore-Remove', this.grabbedThingID); From 761d35d6335fa87468a09f2e6dec24500a91fb6c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Jul 2017 17:10:23 -0700 Subject: [PATCH 046/269] Getting there! --- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 +- .../system/controllers/handControllerGrab.js | 8 +-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cb15c544c7..33d492bc7e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -25,33 +25,36 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; auto entityTreeRenderer = DependencyManager::get().data(); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(destroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } -void ContextOverlayInterface::createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); +void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { + if (event.getButton() == PointerEvent::SecondaryButton) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(entityItemID); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); + } else { + destroyContextOverlay(entityItemID, event); } - - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { @@ -68,7 +71,8 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 05215e3e5c..01129ee4f5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -43,7 +43,7 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } public slots: - void createContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); void destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2b84c3d507..8cd71131ce 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -370,7 +370,9 @@ function projectOntoOverlayXYPlane(overlayID, worldPos) { dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); - dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + if (dimensions.z) { + dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + } } return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); @@ -2225,9 +2227,9 @@ function MyController(hand) { pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, direction: rayPickInfo.searchRay.direction, - button: "None" + button: "Secondary" }; - ContextOverlay.createContextOverlay(rayPickInfo.entityID, pointerEvent); + ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); entityWithContextOverlay = rayPickInfo.entityID; From c56450b986e59270730b1de48df087fa5f35ce39 Mon Sep 17 00:00:00 2001 From: Mike Moody Date: Wed, 19 Jul 2017 01:49:39 -0700 Subject: [PATCH 047/269] 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 048/269] 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 f06ec715f23f6b54531940bce3dcbd1feb0eb1a7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 09:59:44 -0700 Subject: [PATCH 049/269] Quick bugfix --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 33d492bc7e..9182346352 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); - } else { + } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } } From c866bd814d6300442a05a23c1b43c7d63166384a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 12:21:06 -0700 Subject: [PATCH 050/269] Make clicks work right --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlays.cpp | 87 +++++++------------ interface/src/ui/overlays/Overlays.h | 2 +- interface/src/ui/overlays/Planar3DOverlay.h | 1 + .../system/controllers/handControllerGrab.js | 23 +++-- 5 files changed, 50 insertions(+), 65 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9182346352..915ddca7bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,10 +45,10 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 862152bea9..3f175ffcc7 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -818,15 +818,17 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -PointerEvent Overlays::calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, +PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { + auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (!overlay) { + return PointerEvent(); + } + glm::vec3 position = overlay->getPosition(); + glm::quat rotation = overlay->getRotation(); + glm::vec2 dimensions = overlay->getSize(); - auto thisOverlay = std::dynamic_pointer_cast(overlay); - - auto position = thisOverlay->getPosition(); - auto rotation = thisOverlay->getRotation(); - auto dimensions = thisOverlay->getSize(); glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult); @@ -874,12 +876,7 @@ bool Overlays::mousePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - PointerEvent pointerEvent; - if (thisWeb3DOverlay) { - pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - } + PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); return true; } @@ -895,13 +892,9 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(_currentClickingOnOverlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Press); - emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; - } + auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); + emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); + return true; } emit mouseDoublePressOffOverlay(); return false; @@ -913,13 +906,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { - - // Only Web overlays can have focus. - auto thisWeb3DOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisWeb3DOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisWeb3DOverlay, ray, rayPickResult, event, PointerEvent::Release); - emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); + emit mouseReleaseOnOverlay(rayPickResult.overlayID, pointerEvent); } _currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID; @@ -932,40 +920,29 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { PickRay ray = qApp->computePickRay(event->x(), event->y()); RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); if (rayPickResult.intersects) { + auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); + emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - // Only Web overlays can have focus. - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(rayPickResult.overlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit mouseMoveOnOverlay(rayPickResult.overlayID, pointerEvent); - - // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } - } - - // If hovering over a new overlay then enter hover on that overlay. - if (rayPickResult.overlayID != _currentHoverOverOverlayID) { - emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); - } - - // Hover over current overlay. - emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); - - _currentHoverOverOverlayID = rayPickResult.overlayID; + // If previously hovering over a different overlay then leave hover on that overlay. + if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); } + + // If hovering over a new overlay then enter hover on that overlay. + if (rayPickResult.overlayID != _currentHoverOverOverlayID) { + emit hoverEnterOverlay(rayPickResult.overlayID, pointerEvent); + } + + // Hover over current overlay. + emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); + + _currentHoverOverOverlayID = rayPickResult.overlayID; } else { // If previously hovering an overlay then leave hover. if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) { - auto thisOverlay = std::dynamic_pointer_cast(getOverlay(_currentHoverOverOverlayID)); - if (thisOverlay) { - auto pointerEvent = calculateWeb3DPointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Move); - emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); - } + auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); + emit hoverLeaveOverlay(_currentHoverOverOverlayID, pointerEvent); _currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID; } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 7235937077..d52f6d5947 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent calculateWeb3DPointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 9c502ab75e..2ccf2c4513 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,6 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; + glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f173295a1e..5fd4df0e4d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -188,6 +188,7 @@ var USE_BLACKLIST = true; var blacklist = []; var entityWithContextOverlay = false; +var contextualHand = -1; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; @@ -352,7 +353,9 @@ function projectOntoXYPlane(worldPos, position, rotation, dimensions, registrati function projectOntoEntityXYPlane(entityID, worldPos) { var props = entityPropertiesCache.getProps(entityID); - return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + if (props) { + return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); + } } function projectOntoOverlayXYPlane(overlayID, worldPos) { @@ -2186,10 +2189,7 @@ function MyController(hand) { }; this.searchExit = function () { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + contextualHand = -1; }; this.search = function(deltaTime, timestamp) { @@ -2226,11 +2226,11 @@ function MyController(hand) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; } - Script.setTimeout(function() { - if (rayPickInfo.entityID === entityWithContextOverlay) { + Script.setTimeout(function () { + if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { var pointerEvent = { type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse + id: contextualHand + 1, // 0 is reserved for hardware mouse pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), pos3D: rayPickInfo.intersection, normal: rayPickInfo.normal, @@ -2241,6 +2241,7 @@ function MyController(hand) { } }, 500); entityWithContextOverlay = rayPickInfo.entityID; + contextualHand = this.hand; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3488,6 +3489,11 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + entityWithContextOverlay = false; + } + if (isInEditMode()) { this.searchSphereDistance = existingSearchDistance; } @@ -3624,6 +3630,7 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); + entityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From d9c6126000765f1783b36997831cf9cf80ac2c54 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 19 Jul 2017 13:33:32 -0700 Subject: [PATCH 051/269] 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 052/269] 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 053/269] 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 a8ab115b1240ea0caa6b4e20152f4af4bfcfe6fb Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:11:14 -0700 Subject: [PATCH 054/269] Open marketplace when we know the marketplaceID of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 24 +++++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 915ddca7bd..f887aae7ff 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,7 +11,10 @@ #include "ContextOverlayInterface.h" #include "Application.h" +#include "scripting/HMDScriptingInterface.h" + #include +#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -73,6 +76,27 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; + openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } +static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; + +void ContextOverlayInterface::openMarketplace() { + // lets open the tablet and go to the current item in + // the marketplace (if the current entity has a + // marketplaceID) + if (!_currentEntityWithContextOverlay.isNull()) { + auto hmd = DependencyManager::get(); + auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); + + if (entity->getMarketplaceID().length() > 0) { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); + tablet->gotoWebScreen(url); + hmd->openTablet(); + } + } +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 01129ee4f5..cd2d00f0a9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - + void openMarketplace(); }; #endif // hifi_ContextOverlayInterface_h From f0091f16a18669de27c08ad99da25e34645c71ac Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 14:46:10 -0700 Subject: [PATCH 055/269] cr feedback --- .../src/ui/overlays/ContextOverlayInterface.cpp | 12 +++++------- interface/src/ui/overlays/ContextOverlayInterface.h | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f887aae7ff..948ade840e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -11,10 +11,8 @@ #include "ContextOverlayInterface.h" #include "Application.h" -#include "scripting/HMDScriptingInterface.h" #include -#include ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. @@ -24,6 +22,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QLoggingCategory::setFilterRules(QStringLiteral("hifi.context_overlay.debug=false")); _entityScriptingInterface = DependencyManager::get(); + _hmdScriptingInterface = DependencyManager::get(); + _tabletScriptingInterface = DependencyManager::get(); + _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; @@ -87,16 +88,13 @@ void ContextOverlayInterface::openMarketplace() { // the marketplace (if the current entity has a // marketplaceID) if (!_currentEntityWithContextOverlay.isNull()) { - auto hmd = DependencyManager::get(); auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); tablet->gotoWebScreen(url); - hmd->openTablet(); + _hmdScriptingInterface->openTablet(); } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index cd2d00f0a9..4959b3d985 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -18,9 +18,12 @@ #include #include +#include + #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" #include "ui/overlays/Overlays.h" +#include "scripting/HMDScriptingInterface.h" #include "EntityTree.h" #include "ContextOverlayLogging.h" @@ -34,6 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; + QSharedPointer _hmdScriptingInterface; + QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; public: From be48268be827edecf9702b556353cd5f0928dcbb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 19 Jul 2017 15:02:40 -0700 Subject: [PATCH 056/269] 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 44c9f0e65ea7d948bc805c17f39f922b372d4231 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:13:26 -0700 Subject: [PATCH 057/269] Overlay appears only on marketplace items --- .../ui/overlays/ContextOverlayInterface.cpp | 53 ++++++++++--------- .../src/ui/overlays/ContextOverlayInterface.h | 2 + 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..c2473feccc 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -27,6 +27,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; + _entityPropertyFlags += PROP_MARKETPLACE_ID; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -35,27 +36,30 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(entityItemID); EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + if (entityProperties.getMarketplaceID().length() != 0) { + _marketplaceID = entityProperties.getMarketplaceID(); + setCurrentEntityWithContextOverlay(entityItemID); - if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { - _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); - _contextOverlay->setPulseMin(0.75f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setIgnoreRayIntersection(false); - _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); - _contextOverlay->setIsFacingAvatar(true); - _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { + _contextOverlay = std::make_shared(); + _contextOverlay->setAlpha(1.0f); + _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMax(1.0f); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setIgnoreRayIntersection(false); + _contextOverlay->setDrawInFront(true); + _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setIsFacingAvatar(true); + _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); + } + + _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setRotation(entityProperties.getRotation()); + _contextOverlay->setVisible(true); } - - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); - _contextOverlay->setRotation(entityProperties.getRotation()); - _contextOverlay->setVisible(true); } else if (_currentEntityWithContextOverlay == entityItemID) { destroyContextOverlay(entityItemID, event); } @@ -87,14 +91,11 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull()) { - auto entity = qApp->getEntities()->getTree()->findEntityByID(_currentEntityWithContextOverlay); - if (entity->getMarketplaceID().length() > 0) { - auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + entity->getMarketplaceID(); - tablet->gotoWebScreen(url); - _hmdScriptingInterface->openTablet(); - } + if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = MARKETPLACE_BASE_URL + _marketplaceID; + tablet->gotoWebScreen(url); + _hmdScriptingInterface->openTablet(); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 4959b3d985..f3eb8c2ba9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,6 +56,8 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; + QString _marketplaceID; + void openMarketplace(); }; From 4374d4d1e5cebf1ca14a5eb0a5ca7d07166c96db Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 15:27:48 -0700 Subject: [PATCH 058/269] Small improvements to overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Overlay.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 948ade840e..3ab01ea8b1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.2f, 0.2f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index b650da3522..675dff7e93 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -20,7 +20,7 @@ Overlay::Overlay() : _renderItemID(render::Item::INVALID_ITEM_ID), _isLoaded(true), _alpha(DEFAULT_ALPHA), - _pulse(0.0f), + _pulse(1.0f), _pulseMax(0.0f), _pulseMin(0.0f), _pulsePeriod(1.0f), From ecd419e0d393cf8a9ae85d356f5f8aa2210fed2b Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:32:03 -0700 Subject: [PATCH 059/269] cr feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c2473feccc..79edc7f863 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -35,10 +35,10 @@ ContextOverlayInterface::ContextOverlayInterface() { void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { - qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _marketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); @@ -72,6 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _marketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { From 920ac17caf0d7b002c5548995e15cb5b2fc9c1f0 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 15:46:28 -0700 Subject: [PATCH 060/269] minor renaming --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 ++++---- interface/src/ui/overlays/ContextOverlayInterface.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 9a86d1efde..148d511acb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,7 +39,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; - _marketplaceID = entityProperties.getMarketplaceID(); + _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -72,7 +72,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - _marketplaceID.clear(); + _entityMarketplaceID.clear(); } void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { @@ -92,10 +92,10 @@ void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in // the marketplace (if the current entity has a // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull() && _marketplaceID.length() > 0) { + if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + _marketplaceID; + QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f3eb8c2ba9..c79ee247ab 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -56,7 +56,7 @@ public slots: private: bool _verboseLogging { true }; QUuid _currentEntityWithContextOverlay{}; - QString _marketplaceID; + QString _entityMarketplaceID; void openMarketplace(); }; From 3936ee93f0e08da594a3279fe7505c09994658f5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Jul 2017 16:34:04 -0700 Subject: [PATCH 061/269] 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 40ec770bc10f80802a8b69a2d58213b8e519720f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:06:17 -0700 Subject: [PATCH 062/269] Highlight using simple yellow bounding box --- .../src/ui/overlays/ContextOverlayInterface.cpp | 17 +++++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..1582f7f53f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -28,10 +28,12 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_POSITION; _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; + _entityPropertyFlags += PROP_DIMENSIONS; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } +static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (event.getButton() == PointerEvent::SecondaryButton) { @@ -42,6 +44,18 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _entityMarketplaceID = entityProperties.getMarketplaceID(); setCurrentEntityWithContextOverlay(entityItemID); + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setVisible(true); + if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(1.0f); @@ -70,8 +84,11 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt setCurrentEntityWithContextOverlay(QUuid()); qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); _contextOverlay = NULL; + _bbOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c79ee247ab..b20b9b35d5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -22,6 +22,7 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" +#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -40,7 +41,9 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; + OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; + std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); From 2dc38441168ee0ff1b953d0425b657b6e4f60699 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:07:57 -0700 Subject: [PATCH 063/269] Improve reliability of hand lasers; increase size of target --- .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- .../system/controllers/handControllerGrab.js | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3ab01ea8b1..c52ef1bb16 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -52,7 +52,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5fd4df0e4d..9a1540cc78 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,6 +187,7 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; +var potentialEntityWithContextOverlay = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2221,13 +2222,13 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && (entityWithContextOverlay !== rayPickInfo.entityID)) { - if (entityWithContextOverlay) { - ContextOverlay.destroyContextOverlay(entityWithContextOverlay); - entityWithContextOverlay = false; - } + if (rayPickInfo.entityID && !entityWithContextOverlay) { Script.setTimeout(function () { - if (rayPickInfo.entityID === entityWithContextOverlay && contextualHand !== -1) { + if (rayPickInfo.entityID === potentialEntityWithContextOverlay && + !entityWithContextOverlay + && contextualHand !== -1) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2240,8 +2241,8 @@ function MyController(hand) { ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); } }, 500); - entityWithContextOverlay = rayPickInfo.entityID; contextualHand = this.hand; + potentialEntityWithContextOverlay = rayPickInfo.entityID; } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3492,6 +3493,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; } if (isInEditMode()) { @@ -3631,6 +3633,7 @@ function MyController(hand) { Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); entityWithContextOverlay = false; + potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 2dce2549289584106deea64f87e7b8dbbf7620d4 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 19 Jul 2017 17:11:45 -0700 Subject: [PATCH 064/269] Actually increase target size --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 148d511acb..378d8937bb 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -55,7 +55,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); From 00238329385ce2cbd2dc0786ea2ad9068ab29050 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 19 Jul 2017 17:38:19 -0700 Subject: [PATCH 065/269] simple fixes --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bb1424b86a..83ab4c6b13 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -69,7 +70,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); + _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); _contextOverlay->setPosition(entityProperties.getPosition()); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); @@ -97,7 +98,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { + if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From 0a64a1e3b0dff67cf5c1452cbe659f25bc399145 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Wed, 19 Jul 2017 18:26:35 -0700 Subject: [PATCH 066/269] Added a page in installer for express/custom installation --- cmake/templates/NSIS.template.in | 92 +++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index f44c8185d8..1f28d9c1aa 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -354,7 +354,8 @@ SectionEnd !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @CPACK_NSIS_PAGE_COMPONENTS@ - + + Page custom InstallTypesPage ReadInstallTypes Page custom PostInstallOptionsPage ReadPostInstallOptions !insertmacro MUI_PAGE_INSTFILES @@ -442,6 +443,10 @@ Var CleanInstallCheckbox Var CurrentOffset Var OffsetUnits Var CopyFromProductionCheckbox +Var ExpressInstallRadioButton +Var CustomInstallRadioButton +Var InstallTypeDialog +Var Express !macro SetPostInstallOption Checkbox OptionName Default ; reads the value for the given post install option to the registry @@ -459,6 +464,52 @@ Var CopyFromProductionCheckbox ${EndIf} !macroend +Function InstallTypesPage + !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" + + nsDialogs::Create 1018 + Pop $InstallTypeDialog + + ${If} $InstallTypeDialog == error + Abort + ${EndIf} + + StrCpy $CurrentOffset 0 + StrCpy $OffsetUnits u + StrCpy $Express "0" + + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox" + pop $ExpressInstallRadioButton + ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel + IntOp $CurrentOffset $CurrentOffset + 15 + + ${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)" + pop $CustomInstallRadioButton + ${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel + ${EndIf} + + ; Express Install selected by default + ${NSD_Check} $ExpressInstallRadioButton + Call ChangeExpressLabel + + nsDialogs::Show +FunctionEnd + +Function ChangeExpressLabel + Push $R1 + GetDlgItem $R1 $HWNDPARENT 1 + SendMessage $R1 ${WM_SETTEXT} 0 "STR:Install" + Pop $R1 +FunctionEnd + +Function ChangeCustomLabel + Push $R1 + GetDlgItem $R1 $HWNDPARENT 1 + SendMessage $R1 ${WM_SETTEXT} 0 "STR:Next >" + Pop $R1 +FunctionEnd + Function PostInstallOptionsPage !insertmacro MUI_HEADER_TEXT "Setup Options" "" @@ -552,6 +603,12 @@ Function PostInstallOptionsPage ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} ${EndIf} + ; Check if Express is set, if so, abort the post install options page + Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults + StrCmp $Express "1" 0 end + Abort + end: + nsDialogs::Show FunctionEnd @@ -567,6 +624,16 @@ Var LaunchServerNowState Var LaunchClientNowState Var CopyFromProductionState Var CleanInstallState +Var ExpressInstallState +Var CustomInstallState + +Function ReadInstallTypes +${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} + ; check if the user asked for express/custom install + ${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState + ${NSD_GetState} $CustomInstallRadioButton $CustomInstallState +${EndIf} +FunctionEnd Function ReadPostInstallOptions ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} @@ -603,6 +670,28 @@ Function ReadPostInstallOptions ${EndIf} FunctionEnd +Function HandleInstallTypes + ${If} $ExpressInstallState == ${BST_CHECKED} + + StrCpy $Express "1" + + ; over ride custom checkboxes and select defaults + ${NSD_SetState} $DesktopClientCheckbox ${BST_CHECKED} + ${NSD_SetState} $ServerStartupCheckbox ${BST_CHECKED} + ${NSD_SetState} $LaunchServerNowCheckbox ${BST_CHECKED} + ${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED} + + ${If} @PR_BUILD@ == 1 + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} + ${EndIf} + + ; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected + Call ReadPostInstallOptions + Call HandlePostInstallOptions + + ${EndIf} +FunctionEnd + Function HandlePostInstallOptions ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a desktop shortcut to High Fidelity @@ -624,6 +713,7 @@ Function HandlePostInstallOptions !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now From b236a21adc3d98f5e4b5c5525ef77c793af284e5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 07:28:45 -0700 Subject: [PATCH 067/269] no clicks on 'highlight' overlay --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83ab4c6b13..0ef5a0ad43 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,7 +49,6 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(false); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); @@ -98,7 +97,7 @@ void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { - if ((overlayID == _contextOverlayID || overlayID == _bbOverlayID) && event.getButton() == PointerEvent::PrimaryButton) { + if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); From 110efd63c835038d19bc813268895c452d9156b6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:08:49 -0700 Subject: [PATCH 068/269] enabled property so we can turn it off during edit, etc... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 0ef5a0ad43..e66b8ee39e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,10 +33,11 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); } + static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (event.getButton() == PointerEvent::SecondaryButton) { + if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); if (entityProperties.getMarketplaceID().length() != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b20b9b35d5..92173de050 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -36,6 +36,7 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -49,6 +50,8 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + void setEnabled(bool enabled) { _enabled = enabled; } + bool getEnabled() { return _enabled; } public slots: void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -58,6 +61,7 @@ public slots: private: bool _verboseLogging { true }; + bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; From 4f1489f8b43b7b97b63bba044e8667876dcd9ec0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:18:19 -0700 Subject: [PATCH 069/269] less magic --- libraries/shared/src/SpatiallyNestable.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4fe9cda825..afc295a9fd 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -999,7 +999,9 @@ bool SpatiallyNestable::computePuffedQueryAACube() { bool success; AACube currentAACube = getMaximumAACube(success); // make an AACube with edges thrice as long and centered on the object - _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(currentAACube.getScale()), currentAACube.getScale() * 3.0f); + const float PUFF_COEFICIENT = _children.size() > 0 ? 3.0f : 1.0f; + float scale = currentAACube.getScale(); + _queryAACube = AACube(currentAACube.getCorner() - glm::vec3(scale), scale * PUFF_COEFICIENT); _queryAACubeSet = true; getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) { From 0544c1e56173e2f12c09142b0381f88eacddebce Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 08:18:37 -0700 Subject: [PATCH 070/269] build issue --- interface/src/ui/overlays/Overlays.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index d52f6d5947..a6cc300b67 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -131,7 +131,7 @@ public slots: OverlayID cloneOverlay(OverlayID id); /**jsdoc - * Edit an overlay's properties. + * Edit an overlay's properties. * * @function Overlays.editOverlay * @param {Overlays.OverlayID} overlayID The ID of the overlay to edit. @@ -337,7 +337,7 @@ private: #endif bool _enabled = true; - PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, + PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; From d88d7dda2bb1d2a0460fc16a6f37411b245754c6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:19:59 -0700 Subject: [PATCH 071/269] 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 072/269] update queryAACube on server when it probably changed --- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3e5976d2a8..c6a8e6bdcf 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -435,7 +435,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& entity->rememberHasSimulationOwnershipBid(); } } - if (properties.queryAACubeRelatedPropertyChanged() && entity->computePuffedQueryAACube()) { + if (properties.queryAACubeRelatedPropertyChanged() || entity->computePuffedQueryAACube()) { properties.setQueryAACube(entity->getQueryAACube()); } entity->setLastBroadcast(usecTimestampNow()); From fcd3126b85c2f8687d0ffc4f1148720aa28fa747 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 08:32:48 -0700 Subject: [PATCH 073/269] 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 074/269] 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 075/269] 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 076/269] 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 077/269] Update NSIS.template.in --- cmake/templates/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 7e90daa8f9..04eef1e476 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -712,6 +712,7 @@ Function HandlePostInstallOptions ${Else} !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now From b7be727b4ce89e4556ca2716e66378ff78e8ca69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 11:30:55 -0700 Subject: [PATCH 078/269] turn off ContextOverlay for pal, edit. Improve clicks too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++++-- scripts/system/edit.js | 5 +++-- scripts/system/pal.js | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..35ed81bf03 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -29,6 +29,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_ROTATION; _entityPropertyFlags += PROP_MARKETPLACE_ID; _entityPropertyFlags += PROP_DIMENSIONS; + _entityPropertyFlags += PROP_REGISTRATION_POINT; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -40,6 +41,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 position = entityProperties.getPosition(); if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -50,11 +52,13 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setIsSolid(false); _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(entityProperties.getPosition()); + _bbOverlay->setPosition(position); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -67,11 +71,12 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); + _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + _contextOverlay->setPosition(position); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 6bb0675bc8..57cb8b2407 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -14,7 +14,7 @@ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE - + "use strict"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -388,7 +388,6 @@ var toolBar = (function () { function initialize() { Script.scriptEnding.connect(cleanup); - Window.domainChanged.connect(function () { that.setActive(false); that.clearEntityList(); @@ -614,6 +613,7 @@ var toolBar = (function () { }; that.setActive = function (active) { + ContextOverlay.enabled = !active; Settings.setValue(EDIT_SETTING, active); if (active) { Controller.captureEntityClickEvents(); @@ -2184,6 +2184,7 @@ var PopupMenu = function () { }; function cleanup() { + ContextOverlay.enabled = true; for (var i = 0; i < overlays.length; i++) { Overlays.deleteOverlay(overlays[i]); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 8ea22192fc..112e1a7fd8 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -732,7 +732,9 @@ function onTabletButtonClicked() { if (onPalScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); + ContextOverlay.enabled = true; } else { + ContextOverlay.enabled = false; tablet.loadQMLSource(PAL_QML_SOURCE); tablet.tabletShownChanged.connect(tabletVisibilityChanged); Users.requestsDomainListData = true; @@ -883,6 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } + ContextOverlay.enable = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From 3a31805dff12d499be937c9f1a798cf1e0720e69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Jun 2017 17:12:25 -0700 Subject: [PATCH 079/269] Fix warnings in Qt 59 / VS 2017 build --- CMakeLists.txt | 27 +++++++++++-------- cmake/externals/quazip/CMakeLists.txt | 4 +-- cmake/macros/SetPackagingParameters.cmake | 10 +++++-- .../gpu-gl/src/gpu/gl41/GL41BackendInput.cpp | 2 +- libraries/shared/src/shared/Storage.cpp | 5 ++-- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 4 +-- plugins/openvr/src/OpenVrHelpers.cpp | 2 +- 7 files changed, 32 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c90256134..9712b2d32e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION 3.2) +if (WIN32) + cmake_minimum_required(VERSION 3.7) +else() + cmake_minimum_required(VERSION 3.2) +endif() if (USE_ANDROID_TOOLCHAIN) set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake") @@ -33,6 +37,10 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") find_package( Threads ) if (WIN32) + if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + message( FATAL_ERROR "Only 64 bit builds supported." ) + endif() + add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) if (NOT WINDOW_SDK_PATH) @@ -41,16 +49,13 @@ if (WIN32) # sets path for Microsoft SDKs # if you get build error about missing 'glu32' this path is likely wrong - if (MSVC10) - set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 " CACHE PATH "Windows SDK PATH") - elseif (MSVC12) - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(WINDOW_SDK_FOLDER "x64") - else() - set(WINDOW_SDK_FOLDER "x86") - endif() + if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017 + set(WINDOW_SDK_PATH "C:/Program Files (x86)/Windows Kits/10/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64" CACHE PATH "Windows SDK PATH") + elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013 set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH") - endif () + else() + message( FATAL_ERROR "Visual Studio 2013 or higher required." ) + endif() if (DEBUG_DISCOVERED_SDK_PATH) message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}") @@ -103,7 +108,7 @@ else () endif () if (APPLE) - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++0x") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11") set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++") endif () diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index 7af13dafa7..d44c9ffde3 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -21,8 +21,8 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip - URL_MD5 2955176048a31262c09259ca8d309d19 + URL https://s3.amazonaws.com/Oculus/quazip-0.7.3.zip + URL_MD5 ed03754d39b9da1775771819b8001d45 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} LOG_DOWNLOAD 1 diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 82a4a7d080..6f35b76f1d 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -126,8 +126,14 @@ macro(SET_PACKAGING_PARAMETERS) # check if we need to find signtool if (PRODUCTION_BUILD OR PR_BUILD) - find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64") - + if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017 + find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/10" PATH_SUFFIXES "bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64") + elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013 + find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64") + else() + message( FATAL_ERROR "Visual Studio 2013 or higher required." ) + endif() + if (NOT SIGNTOOL_EXECUTABLE) message(FATAL_ERROR "Code signing of executables was requested but signtool.exe could not be found.") endif () diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp index 43b48f721f..e8ebcbe05c 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp @@ -95,7 +95,7 @@ void GL41Backend::updateInput() { // GLenum perLocationStride = strides[bufferNum]; GLenum perLocationStride = attrib._element.getLocationSize(); GLuint stride = (GLuint)strides[bufferNum]; - GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); + uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]); GLboolean isNormalized = attrib._element.isNormalized(); for (size_t locNum = 0; locNum < locationCount; ++locNum) { diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index b07f896df0..7e9f86b049 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -92,9 +92,8 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { FileStorage::~FileStorage() { if (_mapped) { - if (!_file.unmap(_mapped)) { - throw std::runtime_error("Unable to unmap file"); - } + _file.unmap(_mapped); + _mapped = nullptr; } if (_file.isOpen()) { _file.close(); diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 7a73c91c7d..b31f55edeb 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -282,7 +282,7 @@ public: static const vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static const vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; - vr::Texture_t texture{ (void*)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto }; + vr::Texture_t texture{ (void*)(uintptr_t)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &texture, &leftBounds); vr::VRCompositor()->Submit(vr::Eye_Right, &texture, &rightBounds); _plugin._presentRate.increment(); @@ -643,7 +643,7 @@ void OpenVrDisplayPlugin::hmdPresent() { _submitThread->waitForPresent(); } else { GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0)); - vr::Texture_t vrTexture { (void*)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; + vr::Texture_t vrTexture { (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT); vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT); vr::VRCompositor()->PostPresentHandoff(); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 7e287a16c3..c8a0cb5f8b 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -129,7 +129,7 @@ void releaseOpenVrSystem() { #endif // HACK: workaround openvr crash, call submit with an invalid texture, right before VR_Shutdown. - const GLuint INVALID_GL_TEXTURE_HANDLE = -1; + const void* INVALID_GL_TEXTURE_HANDLE = (void*)(uintptr_t)-1; vr::Texture_t vrTexture{ (void*)INVALID_GL_TEXTURE_HANDLE, vr::TextureType_OpenGL, vr::ColorSpace_Auto }; static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 }; From f515a24568b7f3690f1364111b64849faa1f0fee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:00:13 -0700 Subject: [PATCH 080/269] UX improvements --- .../ui/overlays/ContextOverlayInterface.cpp | 51 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 ++-- .../system/controllers/handControllerGrab.js | 7 +-- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e66b8ee39e..bdbbc3f5e8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,7 +36,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; -void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { +bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); @@ -59,7 +59,7 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setAlpha(0.85f); _contextOverlay->setPulseMin(0.75f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); @@ -69,32 +69,45 @@ void ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - - _contextOverlay->setDimensions(glm::vec2(0.05f, 0.05f) * glm::distance(entityProperties.getPosition(), qApp->getCamera().getPosition())); - _contextOverlay->setPosition(entityProperties.getPosition()); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 contextOverlayPosition; + if (distanceToEntity > 1.5f) { + contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + } else { + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + } + _contextOverlay->setPosition(contextOverlayPosition); + _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } - } else if (_currentEntityWithContextOverlay == entityItemID) { - destroyContextOverlay(entityItemID, event); + } else { + return destroyContextOverlay(entityItemID, event); } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - setCurrentEntityWithContextOverlay(QUuid()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; - _entityMarketplaceID.clear(); + if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { + qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + setCurrentEntityWithContextOverlay(QUuid()); + qApp->getOverlays().deleteOverlay(_contextOverlayID); + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _contextOverlay = NULL; + _bbOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + _bbOverlayID = UNKNOWN_OVERLAY_ID; + _entityMarketplaceID.clear(); + return true; + } + return false; } -void ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { - ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); +bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID) { + return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 92173de050..25179e99a8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,6 +29,10 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /**jsdoc * @namespace ContextOverlay */ @@ -54,9 +58,9 @@ public: bool getEnabled() { return _enabled; } public slots: - void createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); - void destroyContextOverlay(const EntityItemID& entityItemID); + bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); + bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a1540cc78..fde237e697 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2227,8 +2227,6 @@ function MyController(hand) { if (rayPickInfo.entityID === potentialEntityWithContextOverlay && !entityWithContextOverlay && contextualHand !== -1) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2238,7 +2236,10 @@ function MyController(hand) { direction: rayPickInfo.searchRay.direction, button: "Secondary" }; - ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent); + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + potentialEntityWithContextOverlay = false; + } } }, 500); contextualHand = this.hand; From f365ce8abd545e0aa9f52c3f1a2dc67e06802fab Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 12:58:06 -0700 Subject: [PATCH 081/269] typo --- scripts/system/pal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 112e1a7fd8..7f0629abaa 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -885,7 +885,7 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enable = true; + ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From a1a7ab945a048a4b7c4981d5257cb34520a80bab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 12:56:46 -0700 Subject: [PATCH 082/269] Make dimensions better --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- interface/src/ui/overlays/ContextOverlayInterface.h | 4 ---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bdbbc3f5e8..d5244140d9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -72,13 +72,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); } else { - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -30.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + // If the entity is too close to the camera, rotate the context overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); - _contextOverlay->setDimensions(glm::vec2(0.1f, 0.1f) * glm::distance(contextOverlayPosition, cameraPosition)); + _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); return true; @@ -90,7 +95,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { - if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 25179e99a8..5ba70d7614 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -29,10 +29,6 @@ #include "EntityTree.h" #include "ContextOverlayLogging.h" -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /**jsdoc * @namespace ContextOverlay */ From 46d09a12fe36535ca8ce3e386491f43fcfb74386 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 13:26:43 -0700 Subject: [PATCH 083/269] Fix bug where overlay would get stuck --- scripts/system/controllers/handControllerGrab.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index fde237e697..b191c6bf89 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3633,8 +3633,6 @@ function MyController(hand) { }; Overlays.sendMousePressOnOverlay(this.grabbedOverlay, pointerEvent); - entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; this.touchingEnterTimer = 0; this.touchingEnterPointerEvent = pointerEvent; From 7ddac54b37f773f9fc96f9da3bb651ad9f2da2c0 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Thu, 20 Jul 2017 13:55:04 -0700 Subject: [PATCH 084/269] copy from production for PR builds unchecked for custom and express also over rides deselection --- cmake/templates/NSIS.template.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 1f28d9c1aa..f653002fb6 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -600,7 +600,7 @@ Function PostInstallOptionsPage ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Copy settings and content from production install" Pop $CopyFromProductionCheckbox - ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED} ${EndIf} ; Check if Express is set, if so, abort the post install options page @@ -682,8 +682,8 @@ Function HandleInstallTypes ${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED} ${If} @PR_BUILD@ == 1 - ${NSD_SetState} $CopyFromProductionCheckbox ${BST_CHECKED} - ${EndIf} + ${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED} + ${EndIf} ; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected Call ReadPostInstallOptions From 10503c3a16c54b70d87ebf1ce61fdaa94ccd2606 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:18:07 -0700 Subject: [PATCH 085/269] registration point --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 80c5665127..7f20ced1c3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -41,7 +41,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 position = entityProperties.getPosition(); + glm::vec3 bbPosition = entityProperties.getPosition(); + glm::vec3 dimensions = entityProperties.getDimensions(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -58,7 +63,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(position); + _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { @@ -71,7 +76,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); _contextOverlay->setIsFacingAvatar(true); - _contextOverlay->setParentID(entityItemID); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); From c1de09c7f65635ec81802128a62567c9ca2405d2 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:24:16 -0700 Subject: [PATCH 086/269] whitespace issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7f20ced1c3..c15bfaf9a2 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x*dimensions.x, adjustPos.y*dimensions.y, adjustPos.z*dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; From c8015e4eb608d663ebadfb17bc8dc152badb32ff Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 14:52:58 -0700 Subject: [PATCH 087/269] cleanup, fixed build issue --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index c15bfaf9a2..50fcf50b90 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -45,7 +45,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * glm::vec3(adjustPos.x * dimensions.x, adjustPos.y * dimensions.y, adjustPos.z * dimensions.z)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -79,7 +79,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(position, cameraPosition); + float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > 1.5f) { From 5cd05da31e46ec60cfb19f1c87bff9fed460a9d3 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 20 Jul 2017 15:16:40 -0700 Subject: [PATCH 088/269] strict comparisons --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index b191c6bf89..f0f1267bcf 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2500,7 +2500,7 @@ function MyController(hand) { var pointerEvent; if (rayPickInfo.overlayID) { var overlay = rayPickInfo.overlayID; - if ((Overlays.getProperty(overlay, "type") == "web3d") && Overlays.keyboardFocusOverlay != overlay) { + if ((Overlays.getProperty(overlay, "type") === "web3d") && Overlays.keyboardFocusOverlay != overlay) { Entities.keyboardFocusEntity = null; Overlays.keyboardFocusOverlay = overlay; } From a8dac0cb79b49b2646d34780a43374b6322737e0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Jul 2017 15:45:50 -0700 Subject: [PATCH 089/269] 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 090/269] 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 091/269] 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 e68f9499fa018884784b6263a577602008036e01 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 20 Jul 2017 16:51:00 -0700 Subject: [PATCH 092/269] Add hover/unhover handlers to context overlays --- interface/src/Application.cpp | 16 ++++++++++--- .../ui/overlays/ContextOverlayInterface.cpp | 23 ++++++++++++++++++- .../src/ui/overlays/ContextOverlayInterface.h | 2 ++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cf5037f57..699ddfcb2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1348,9 +1348,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(overlays, - SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), - DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + + connect(overlays, + SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), + DependencyManager::get().data(), + SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 50fcf50b90..78ca0d591c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -69,7 +69,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.75f); + _contextOverlay->setPulseMin(0.6f); _contextOverlay->setPulseMax(1.0f); _contextOverlay->setColorPulse(1.0f); _contextOverlay->setIgnoreRayIntersection(false); @@ -130,6 +130,27 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } + +void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(0.0f); + _contextOverlay->setPulsePeriod(0.0f); + _contextOverlay->setAlpha(1.0f); + } +} + +void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { + if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { + qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; + _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); + _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setPulsePeriod(1.0f); + _contextOverlay->setAlpha(0.85f); + } +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 5ba70d7614..f06f85eab7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,6 +58,8 @@ public slots: bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); private: bool _verboseLogging { true }; From 649b974927e0deff5156d1333555b40206e4773d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 21 Jul 2017 01:01:23 +0100 Subject: [PATCH 093/269] 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 094/269] 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 095/269] 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 9c0026bf697c95f2bb210d900b1e739a9008859c Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 08:40:12 -0700 Subject: [PATCH 096/269] make sure context overlays are on after pal is dismissed --- scripts/system/pal.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 7f0629abaa..58a54ef859 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -718,10 +718,12 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; + ContextOverlay.enabled = true; } function tabletVisibilityChanged() { if (!tablet.tabletShown) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -865,6 +867,7 @@ function avatarDisconnected(nodeID) { function clearLocalQMLDataAndClosePAL() { sendToQml({ method: 'clearLocalQMLData' }); if (onPalScreen) { + ContextOverlay.enabled = true; tablet.gotoHomeScreen(); } } @@ -885,7 +888,6 @@ function shutdown() { if (onPalScreen) { tablet.gotoHomeScreen(); } - ContextOverlay.enabled = true; button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); From 33a8ab2d58eead3f286ccf3763fd242cb215a613 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 21 Jul 2017 18:18:23 +0200 Subject: [PATCH 097/269] 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 7d125f44e417d13a39e411767c2f520011820570 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 09:35:49 -0700 Subject: [PATCH 098/269] Set parentID even when overlay already exists --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 78ca0d591c..de1c4156a6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -58,9 +58,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlay->setColor(BB_OVERLAY_COLOR); _bbOverlay->setDrawInFront(true); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlay->setParentID(entityItemID); _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } + _bbOverlay->setParentID(entityItemID); _bbOverlay->setDimensions(entityProperties.getDimensions()); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); From d1c2218a31e52305dbabfa151e4499c90b3a631c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Jul 2017 17:06:36 -0700 Subject: [PATCH 099/269] 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 100/269] 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 101/269] 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 102/269] 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 103/269] 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 104/269] optimize cube update only for connected things --- libraries/shared/src/SpatiallyNestable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 38f18c5024..0a480d2d71 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -951,7 +951,7 @@ const float PARENTED_EXPANSION_FACTOR = 3.0f; bool SpatiallyNestable::checkAndMaybeUpdateQueryAACube() { bool success; AACube maxAACube = getMaximumAACube(success); - if (success && (!_queryAACubeSet || !_queryAACube.contains(maxAACube))) { + if (success && (!_queryAACubeSet || (_parentID.isNull() && _children.size() == 0) || !_queryAACube.contains(maxAACube))) { if (_parentJointIndex != INVALID_JOINT_INDEX || _children.size() > 0 ) { // make an expanded AACube centered on the object float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); From a800f6c05a6e57be7371e979c6817132e43d6d5f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:44:44 -0700 Subject: [PATCH 105/269] No magic numbers; Move (i) based on laser hand; Tablet to Tab key --- .../resources/controllers/keyboardMouse.json | 2 +- .../ui/overlays/ContextOverlayInterface.cpp | 50 ++++++++++++------- .../src/input-plugins/KeyboardMouseDevice.cpp | 1 + 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8baf56684a..a4f7cd8db1 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -116,6 +116,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } + { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } ] } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index de1c4156a6..7d1131218a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,10 +36,22 @@ ContextOverlayInterface::ContextOverlayInterface() { } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; +static const uint32_t LEFT_HAND_HW_ID = 1; +static const uint32_t RIGHT_HAND_HW_ID = 2; +static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters +static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; +static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; +static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); glm::vec3 bbPosition = entityProperties.getPosition(); glm::vec3 dimensions = entityProperties.getDimensions(); @@ -68,10 +80,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); - _contextOverlay->setAlpha(0.85f); - _contextOverlay->setPulseMin(0.6f); - _contextOverlay->setPulseMax(1.0f); - _contextOverlay->setColorPulse(1.0f); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); + _contextOverlay->setPulseMin(CONTEXT_OVERLAY_UNHOVERED_PULSEMIN); + _contextOverlay->setPulseMax(CONTEXT_OVERLAY_UNHOVERED_PULSEMAX); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); @@ -82,14 +94,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > 1.5f) { + if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.08f, 0.08f) * glm::distance(contextOverlayPosition, cameraPosition); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, -20.0f, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(0.12f, 0.12f) * glm::distance(contextOverlayPosition, cameraPosition); + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); @@ -134,20 +150,20 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(0.0f); - _contextOverlay->setPulsePeriod(0.0f); - _contextOverlay->setAlpha(1.0f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(0.0f); // pulse off + _contextOverlay->setPulsePeriod(0.0f); // pulse off + _contextOverlay->setAlpha(CONTEXT_OVERLAY_HOVERED_ALPHA); } } void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; - _contextOverlay->setColor({ 0xFF, 0xFF, 0xFF }); - _contextOverlay->setColorPulse(1.0f); - _contextOverlay->setPulsePeriod(1.0f); - _contextOverlay->setAlpha(0.85f); + _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); + _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); + _contextOverlay->setPulsePeriod(CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD); + _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index b5a2fc6b3c..e9ec6d8910 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -236,6 +236,7 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); From a346e5ccf6b42a6a43f45e5e29e125f91aa11b07 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 11:56:06 -0700 Subject: [PATCH 106/269] New inspect icon --- interface/resources/images/inspect-icon.png | Bin 0 -> 27911 bytes .../src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 interface/resources/images/inspect-icon.png diff --git a/interface/resources/images/inspect-icon.png b/interface/resources/images/inspect-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9259de23e9f5e966975da9652a5ecb1a15b8c248 GIT binary patch literal 27911 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 literal 0 HcmV?d00001 diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7d1131218a..53b767525f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -86,7 +86,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setColorPulse(CONTEXT_OVERLAY_UNHOVERED_COLORPULSE); _contextOverlay->setIgnoreRayIntersection(false); _contextOverlay->setDrawInFront(true); - _contextOverlay->setURL("http://i.imgur.com/gksZygp.png"); + _contextOverlay->setURL(PathUtils::resourcesPath() + "images/inspect-icon.png"); _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } From 615a3c52e4f74599912021bd6e067b8ad1a35f47 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 21 Jul 2017 21:26:33 +0200 Subject: [PATCH 107/269] 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 9e31024a0e3839756a9bd333cd1121dbbe441120 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 13:16:41 -0700 Subject: [PATCH 108/269] merged with new stuff too --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 53b767525f..6eec65a7d4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -49,6 +49,7 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; +static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { @@ -95,7 +96,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - contextOverlayPosition = (distanceToEntity - 1.0f) * glm::normalize(entityProperties.getPosition() - cameraPosition) + cameraPosition; + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 796292dd4c6efffedff555f5756d8aaaca705f0d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 13:21:13 -0700 Subject: [PATCH 109/269] Fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 3f175ffcc7..b0aa94b884 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -827,6 +827,9 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } glm::vec2 dimensions = overlay->getSize(); From 811be15bbcd03fe757f02a2003e9e3aeccca5b11 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 21 Jul 2017 14:02:19 -0700 Subject: [PATCH 110/269] Another attmept to fix the tablet --- interface/src/ui/overlays/Overlays.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b0aa94b884..40ab4309b5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -822,14 +822,14 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) { auto overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + if (getOverlayType(overlayID) == "web3d") { + overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); + } if (!overlay) { return PointerEvent(); } glm::vec3 position = overlay->getPosition(); glm::quat rotation = overlay->getRotation(); - if (getOverlayType(overlayID) == "web3d") { - overlay = std::dynamic_pointer_cast(getOverlay(overlayID)); - } glm::vec2 dimensions = overlay->getSize(); From e7c6ba9ae2a4183d32d9542d6236adb1977cf0f5 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 21 Jul 2017 14:44:45 -0700 Subject: [PATCH 111/269] unused var --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6eec65a7d4..83c86bfd82 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -37,7 +37,6 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; -static const uint32_t RIGHT_HAND_HW_ID = 2; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims From 06b4c53d28f0c843ab8b927bbe8ba94695dc15b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Sat, 22 Jul 2017 00:15:26 +0100 Subject: [PATCH 112/269] 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 113/269] 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 114/269] 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 115/269] 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 116/269] 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 117/269] 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 118/269] 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 119/269] 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 120/269] 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 121/269] 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 122/269] 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 123/269] 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 124/269] 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 125/269] 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 126/269] 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 127/269] add missing cursor-*.pngs --- interface/resources/images/cursor-arrow.png | Bin 0 -> 3562 bytes interface/resources/images/cursor-link.png | Bin 0 -> 44403 bytes interface/resources/images/cursor-none.png | Bin 0 -> 133 bytes interface/resources/images/cursor-reticle.png | Bin 0 -> 9234 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/images/cursor-arrow.png create mode 100644 interface/resources/images/cursor-link.png create mode 100644 interface/resources/images/cursor-none.png create mode 100644 interface/resources/images/cursor-reticle.png diff --git a/interface/resources/images/cursor-arrow.png b/interface/resources/images/cursor-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..408881b5856eb47a30875e3e56d5864721161587 GIT binary patch literal 3562 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 R^nCyTaI$x`E7(oB_+R=!c8UN1 literal 0 HcmV?d00001 diff --git a/interface/resources/images/cursor-link.png b/interface/resources/images/cursor-link.png new file mode 100644 index 0000000000000000000000000000000000000000..67ff4c7b6793a8f2f5107d9710bc553f7ff6d10a GIT binary patch literal 44403 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ó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 literal 0 HcmV?d00001 diff --git a/interface/resources/images/cursor-reticle.png b/interface/resources/images/cursor-reticle.png new file mode 100644 index 0000000000000000000000000000000000000000..2d9ba8fd659c9172c1333b96ee8e3a7deb273b9c GIT binary patch literal 9234 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 literal 0 HcmV?d00001 From 223ed5b59a11a41890b89e234bb6015775e96513 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 14:11:45 -0700 Subject: [PATCH 128/269] Hack to mod tablet orientation when clicking context overlay --- .../src/ui/overlays/ContextOverlayInterface.cpp | 13 +++++++++++++ interface/src/ui/overlays/ContextOverlayInterface.h | 2 ++ scripts/system/tablet-ui/tabletUI.js | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 83c86bfd82..6069ecf6e4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -33,6 +33,18 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { + if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { + QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); + QVariantMap props; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); + props.insert("position", vec3toVariant(position)); + props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + qApp->getOverlays().editOverlay(tabletFrameID, props); + _contextOverlayJustClicked = false; + } + }); } static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; @@ -149,6 +161,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); + _contextOverlayJustClicked = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index f06f85eab7..812914a82e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -19,6 +19,7 @@ #include #include #include +#include "avatar/AvatarManager.h" #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" @@ -66,6 +67,7 @@ private: bool _enabled { true }; QUuid _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; + bool _contextOverlayJustClicked { false }; void openMarketplace(); }; diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 9188f39a2e..257a56bf09 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -104,7 +104,6 @@ function showTabletUI() { checkTablet() - gTablet.tabletShown = true; if (!tabletRezzed || !tabletIsValid()) { closeTabletUI(); @@ -123,6 +122,7 @@ Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); } + gTablet.tabletShown = true; } function hideTabletUI() { From 9c152ddc6f7d0c86c88b8423a8ee64f295351a29 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 24 Jul 2017 22:30:43 +0100 Subject: [PATCH 129/269] 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 130/269] 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 131/269] 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 132/269] 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 133/269] 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 134/269] 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 135/269] use alt+f for 1st person camera and alt+o for 3rd --- interface/src/Application.cpp | 13 +------------ interface/src/Menu.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 03f62221ef..8113001cda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3020,20 +3020,9 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_F: { - if (isMeta) { - auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FirstPerson)) { - menu->setIsOptionChecked(MenuOption::ThirdPerson, true); - menu->setIsOptionChecked(MenuOption::FirstPerson, false); - } else { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - menu->setIsOptionChecked(MenuOption::ThirdPerson, false); - } - cameraMenuChanged(); - } else if (isOption) { + if (isOption) { _physicsEngine->dumpNextStats(); } - break; } case Qt::Key_H: { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c7223be208..f1ed0497c6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -222,14 +222,14 @@ Menu::Menu() { cameraModeGroup->setExclusive(true); // View > First Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::FirstPerson, 0, - true, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + viewMenu, MenuOption::FirstPerson, Qt::ALT | Qt::Key_F, + true, qApp, SLOT(cameraMenuChanged()))); // View > Third Person - cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::ThirdPerson, 0, - false, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( + viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_O, + false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, From f0871c6878ff614b5b2f3d775ea161e0edb8ae91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:06:15 -0700 Subject: [PATCH 136/269] avoid unnecessary and expensive gets --- interface/src/avatar/MyAvatar.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b32ef4024e..7910df90ec 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1643,10 +1643,13 @@ void MyAvatar::prepareForPhysicsSimulation() { } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { - glm::vec3 position = getPosition(); - glm::quat orientation = getOrientation(); + glm::vec3 position; + glm::quat orientation; if (_characterController.isEnabledAndReady()) { _characterController.getPositionAndOrientation(position, orientation); + } else { + position = getPosition(); + orientation = getOrientation(); } nextAttitude(position, orientation); _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); From 29be9aee65a14e314905dead6dc8229f6667b1f2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 21 Jul 2017 21:34:44 -0700 Subject: [PATCH 137/269] move nextAttitude() from AvatarData to MyAvatar --- interface/src/avatar/MyAvatar.cpp | 17 +++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + .../src/avatars-renderer/Avatar.h | 2 +- libraries/avatars/src/AvatarData.cpp | 17 ----------------- libraries/avatars/src/AvatarData.h | 3 --- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7910df90ec..2e3b9a584e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1642,6 +1642,23 @@ void MyAvatar::prepareForPhysicsSimulation() { _prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix); } +// There are a number of possible strategies for this set of tools through endRender, below. +void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { + bool success; + Transform trans = getTransform(success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + return; + } + trans.setTranslation(position); + trans.setRotation(orientation); + SpatiallyNestable::setTransform(trans, success); + if (!success) { + qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; + } + updateAttitude(); +} + void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { glm::vec3 position; glm::quat orientation; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 648a5b5f29..86321137d4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -432,6 +432,7 @@ public: void updateMotors(); void prepareForPhysicsSimulation(); + void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 89db519abc..db9d36be7a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -184,7 +184,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() override { _skeletonModel->updateAttitude(); } + virtual void updateAttitude() { _skeletonModel->updateAttitude(); } // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 6fdb4d1ef6..85228af941 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -110,23 +110,6 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() { return _defaultFullAvatarModelUrl; } -// There are a number of possible strategies for this set of tools through endRender, below. -void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) { - bool success; - Transform trans = getTransform(success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - return; - } - trans.setTranslation(position); - trans.setRotation(orientation); - SpatiallyNestable::setTransform(trans, success); - if (!success) { - qCWarning(avatars) << "Warning -- AvatarData::nextAttitude failed"; - } - updateAttitude(); -} - void AvatarData::setTargetScale(float targetScale) { auto newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); if (_targetScale != newValue) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 16768ec62a..1cf7d7dd91 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -445,9 +445,6 @@ public: using SpatiallyNestable::setOrientation; virtual void setOrientation(const glm::quat& orientation) override; - void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time. - virtual void updateAttitude() {} // Tell skeleton mesh about changes - glm::quat getHeadOrientation() const { lazyInitHeadData(); return _headData->getOrientation(); From f598b1f1f581761010f45ca0f7907a71943c484f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:56:05 -0700 Subject: [PATCH 138/269] fix spelling typo in comment --- interface/src/avatar/MyHead.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 9f2d080cd6..7fc6b9fa26 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -29,7 +29,7 @@ MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) { glm::quat MyHead::getHeadOrientation() const { // NOTE: Head::getHeadOrientation() is not used for orienting the camera "view" while in Oculus mode, so // you may wonder why this code is here. This method will be called while in Oculus mode to determine how - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // to change the driving direction while in Oculus mode. It is used to support driving toward where your // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not // always the same. @@ -39,7 +39,7 @@ glm::quat MyHead::getHeadOrientation() const { return headPose.rotation * Quaternions::Y_180; } - return myAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); + return myAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); } void MyHead::simulate(float deltaTime) { From 49942832474dd2638100d4114ac7d0ab68466f9e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:57:18 -0700 Subject: [PATCH 139/269] remove unnecessary state check --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index bd4d1201c7..9a7abc4e98 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -445,7 +445,7 @@ void CharacterController::handleChangedCollisionGroup() { void CharacterController::updateUpAxis(const glm::quat& rotation) { _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (_state != State::Hover && _rigidBody) { + if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); } } From 8c55476c65feb3762141403cc6225bd9f8b85699 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 24 Jul 2017 15:58:39 -0700 Subject: [PATCH 140/269] fix motor direction when walking upside down also maintain worldUp and remove unnecessary cruft --- interface/src/Application.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 6 +-- interface/src/avatar/MyAvatar.h | 1 - .../src/avatars-renderer/Avatar.cpp | 38 ++++++------------- .../src/avatars-renderer/Avatar.h | 13 ++----- .../src/avatars-renderer/SkeletonModel.cpp | 6 +-- .../src/avatars-renderer/SkeletonModel.h | 2 +- libraries/avatars/src/AvatarData.cpp | 14 +++---- libraries/avatars/src/AvatarData.h | 12 +++--- libraries/shared/src/shared/Camera.h | 2 +- 10 files changed, 38 insertions(+), 60 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dbb94cfdae..166b4fee72 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2314,7 +2314,7 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); + auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); // Mirror HMD yaw and roll @@ -2336,7 +2336,7 @@ void Application::paintGL() { + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + mirrorBodyOrientation * hmdOffset); } else { - _myCamera.setOrientation(myAvatar->getWorldAlignedOrientation() + _myCamera.setOrientation(myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(myAvatar->getDefaultEyePosition() + glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2e3b9a584e..b644defde2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1291,7 +1291,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() { } glm::vec3 MyAvatar::getDefaultEyePosition() const { - return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); + return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = 1.0f + 1.0f; @@ -1588,7 +1588,7 @@ void MyAvatar::updateMotors() { // non-hovering = walking: follow camera twist about vertical but not lift // so we decompose camera's rotation and store the twist part in motorRotation glm::quat liftRotation; - swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation); + motorRotation = getOrientation(); } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; @@ -1656,7 +1656,7 @@ void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) { if (!success) { qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed"; } - updateAttitude(); + updateAttitude(orientation); } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 86321137d4..2f8f838cf9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -552,7 +552,6 @@ public: Q_INVOKABLE bool isUp(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) > 0.0f; }; // true iff direction points up wrt avatar's definition of up. Q_INVOKABLE bool isDown(const glm::vec3& direction) { return glm::dot(direction, _worldUpDirection) < 0.0f; }; - public slots: void increaseSize(); void decreaseSize(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4016592d0a..06814c2707 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -151,11 +151,6 @@ glm::vec3 Avatar::getNeckPosition() const { return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition(); } - -glm::quat Avatar::getWorldAlignedOrientation () const { - return computeRotationFromBodyToWorldUp() * getOrientation(); -} - AABox Avatar::getBounds() const { if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) { // approximately 2m tall, scaled to user request. @@ -436,6 +431,11 @@ void Avatar::slamPosition(const glm::vec3& newPosition) { _lastVelocity = glm::vec3(0.0f); } +void Avatar::updateAttitude(const glm::quat& orientation) { + _skeletonModel->updateAttitude(orientation); + _worldUpDirection = orientation * Vectors::UNIT_Y; +} + void Avatar::applyPositionDelta(const glm::vec3& delta) { setPosition(getPosition() + delta); _positionDeltaAccumulator += delta; @@ -628,22 +628,6 @@ void Avatar::render(RenderArgs* renderArgs) { } } -glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { - glm::quat orientation = getOrientation(); - glm::vec3 currentUp = orientation * IDENTITY_UP; - float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)); - if (angle < EPSILON) { - return glm::quat(); - } - glm::vec3 axis; - if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis - axis = orientation * IDENTITY_RIGHT; - } else { - axis = glm::normalize(glm::cross(currentUp, _worldUpDirection)); - } - return glm::angleAxis(angle * proportion, axis); -} - void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { _attachmentsToDelete.clear(); @@ -1401,14 +1385,14 @@ glm::quat Avatar::getUncachedRightPalmRotation() const { return rightPalmRotation; } -void Avatar::setPosition(const glm::vec3& position) { - AvatarData::setPosition(position); - updateAttitude(); +void Avatar::setPositionViaScript(const glm::vec3& position) { + setPosition(position); + updateAttitude(getOrientation()); } -void Avatar::setOrientation(const glm::quat& orientation) { - AvatarData::setOrientation(orientation); - updateAttitude(); +void Avatar::setOrientationViaScript(const glm::quat& orientation) { + setOrientation(orientation); + updateAttitude(orientation); } void Avatar::updatePalms() { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index db9d36be7a..2c75012209 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -112,8 +112,6 @@ public: const Head* getHead() const { return static_cast(_headData); } Head* getHead() { return static_cast(_headData); } - glm::quat getWorldAlignedOrientation() const; - AABox getBounds() const; /// Returns the distance to use as a LOD parameter. @@ -184,7 +182,7 @@ public: void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const; void slamPosition(const glm::vec3& position); - virtual void updateAttitude() { _skeletonModel->updateAttitude(); } + virtual void updateAttitude(const glm::quat& orientation) override; // Call this when updating Avatar position with a delta. This will allow us to // _accurately_ measure position changes and compute the resulting velocity @@ -197,10 +195,8 @@ public: void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); float computeMass(); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + void setPositionViaScript(const glm::vec3& position) override; + void setOrientationViaScript(const glm::quat& orientation) override; // these call through to the SpatiallyNestable versions, but they are here to expose these to javascript. Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } @@ -240,7 +236,7 @@ public: bool hasNewJointData() const { return _hasNewJointData; } float getBoundingRadius() const; - + void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene); void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene); bool isInScene() const { return render::Item::isValidID(_renderItemID); } @@ -303,7 +299,6 @@ protected: glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } - glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void measureMotionDerivatives(float deltaTime); float getSkeletonHeight() const; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 9651951b46..c0d5fc07d7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -118,16 +118,16 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig.updateFromEyeParameters(eyeParams); } -void SkeletonModel::updateAttitude() { +void SkeletonModel::updateAttitude(const glm::quat& orientation) { setTranslation(_owningAvatar->getSkeletonPosition()); - setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180); + setRotation(orientation * Quaternions::Y_180); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); } // Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed), // but just before head has been simulated. void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { - updateAttitude(); + updateAttitude(_owningAvatar->getOrientation()); if (fullUpdate) { setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients()); diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index e48884c581..919e82825c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -35,7 +35,7 @@ public: void simulate(float deltaTime, bool fullUpdate = true) override; void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateAttitude(); + void updateAttitude(const glm::quat& orientation); /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? getFBXGeometry().leftHandJointIndex : -1; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 85228af941..9570056353 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -91,9 +91,6 @@ AvatarData::AvatarData() : _targetVelocity(0.0f), _density(DEFAULT_AVATAR_DENSITY) { - setBodyPitch(0.0f); - setBodyYaw(-90.0f); - setBodyRoll(0.0f); } AvatarData::~AvatarData() { @@ -2083,6 +2080,7 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { currentBasis = std::make_shared(Transform::fromJson(json[JSON_AVATAR_BASIS])); } + glm::quat orientation; if (json.contains(JSON_AVATAR_RELATIVE)) { // During playback you can either have the recording basis set to the avatar current state // meaning that all playback is relative to this avatars starting position, or @@ -2094,12 +2092,14 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { auto relativeTransform = Transform::fromJson(json[JSON_AVATAR_RELATIVE]); auto worldTransform = currentBasis->worldTransform(relativeTransform); setPosition(worldTransform.getTranslation()); - setOrientation(worldTransform.getRotation()); + orientation = worldTransform.getRotation(); } else { // We still set the position in the case that there is no movement. setPosition(currentBasis->getTranslation()); - setOrientation(currentBasis->getRotation()); + orientation = currentBasis->getRotation(); } + setOrientation(orientation); + updateAttitude(orientation); // Do after avatar orientation because head look-at needs avatar orientation. if (json.contains(JSON_AVATAR_HEAD)) { @@ -2217,11 +2217,11 @@ void AvatarData::setBodyRoll(float bodyRoll) { setOrientation(glm::quat(glm::radians(eulerAngles))); } -void AvatarData::setPosition(const glm::vec3& position) { +void AvatarData::setPositionViaScript(const glm::vec3& position) { SpatiallyNestable::setPosition(position); } -void AvatarData::setOrientation(const glm::quat& orientation) { +void AvatarData::setOrientationViaScript(const glm::quat& orientation) { SpatiallyNestable::setOrientation(orientation); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 1cf7d7dd91..0e936c49e0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -351,14 +351,14 @@ public: class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale) Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) - Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientationViaScript) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) @@ -440,10 +440,10 @@ public: float getBodyRoll() const; void setBodyRoll(float bodyRoll); - using SpatiallyNestable::setPosition; - virtual void setPosition(const glm::vec3& position) override; - using SpatiallyNestable::setOrientation; - virtual void setOrientation(const glm::quat& orientation) override; + virtual void setPositionViaScript(const glm::vec3& position); + virtual void setOrientationViaScript(const glm::quat& orientation); + + virtual void updateAttitude(const glm::quat& orientation) {} glm::quat getHeadOrientation() const { lazyInitHeadData(); diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 5f2162ff6e..c7b943f0dd 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -127,7 +127,7 @@ private: glm::mat4 _projection; // derived - glm::vec3 _position; + glm::vec3 _position { 0.0f, 0.0f, 0.0f }; glm::quat _orientation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; From 82a787a753ddefaa19c3531c37c719a9fcc07320 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 24 Jul 2017 16:04:11 -0700 Subject: [PATCH 141/269] 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 142/269] 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 143/269] 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 144/269] readded line that caused colflict. --- scripts/system/libraries/entitySelectionTool.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2d1853fae2..77b62913bf 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -4060,6 +4060,8 @@ SelectionDisplay = (function() { return false; } + entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true); + // ignore ray intersection for our selection box and yaw/pitch/roll result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] ); if (result.intersects) { From c22798a8e64adfbd0682b5be512b8c06a5b7dd25 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 24 Jul 2017 17:16:07 -0700 Subject: [PATCH 145/269] Offset tablet position to left of screen per feedback --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 6069ecf6e4..a9efc8e6f6 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -38,9 +38,8 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 position = myAvatar->getJointPosition("Head") + 0.6f * (myAvatar->getOrientation() * Vectors::FRONT); - props.insert("position", vec3toVariant(position)); - props.insert("orientation", quatToVariant(myAvatar->getOrientation() * glm::quat(0.0f, 0.0f, 1.0f, 0.0f))); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } From 5771ee27e1f510a563b4d25b39f8071d3e93d547 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Mon, 24 Jul 2017 17:33:01 -0700 Subject: [PATCH 146/269] 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 147/269] 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 148/269] 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 149/269] 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 150/269] 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 151/269] 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 152/269] 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 153/269] 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 154/269] 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 155/269] 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 156/269] 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 157/269] 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 158/269] Fix entity properties sometimes being resent When writing entity properties to a packet, we start off with a list of requested properties (`requestedProperties`) and keep track of which properties didn't fit (`propertiesDidntFit`) the packet, which is intialized to requestedProperties. As we pack the properties, we remove them from propertiesDidntFit if they didn't need to be written or were able to be packed. At the end we store propertiesDidntFit, and use it in the future as our requestedProperties when we try to pack more data into a packet. The bug: because of the order in which propertiesDidntFit is initialized, it ended up always being the list of all properties for that entity. This typically wasn't an issue because we usually go through and try to append all of the properties, and if we don't need to append them (because they aren't in requestedProperties) we remove them from our propertiesDidntFit list. When we don't have enough remaining space in the current packet for even the entity header, which is fairly small, we don't bother trying to append any of the properties. When this happens, propertiesDidntFit contains the full list of properties, which we save for the next pass through the entity, and use as our requestedProperties, causing us to resend entity data again. In the worst case we never end up sending all of the entity's data. --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..378f344d26 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -182,7 +182,6 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); - EntityPropertyFlags propertiesDidntFit = requestedProperties; // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. @@ -190,6 +189,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID()); } + EntityPropertyFlags propertiesDidntFit = requestedProperties; + LevelDetails entityLevel = packetData->startLevel(); quint64 lastEdited = getLastEdited(); From 256b7efdd2f9bdef50e1dad58550e64645f050b8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:06:54 -0700 Subject: [PATCH 159/269] Handle the case when the camera is inside the bounding box of an entity --- .../ui/overlays/ContextOverlayInterface.cpp | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index a9efc8e6f6..cc4f645c0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -14,6 +14,9 @@ #include +static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees +static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters ContextOverlayInterface::ContextOverlayInterface() { // "context_overlay" debug log category disabled by default. // Create your own "qtlogging.ini" file and set your "QT_LOGGING_CONF" environment variable @@ -38,8 +41,9 @@ ContextOverlayInterface::ContextOverlayInterface() { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); - props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, 30.0f, 0.0f))) * (0.65f * (myAvatar->getHeadOrientation() * Vectors::FRONT)))); - props.insert("orientation", quatToVariant(myAvatar->getHeadOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 210.0f, 0.0f))))); + glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); + props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); _contextOverlayJustClicked = false; } @@ -68,7 +72,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 dimensions = entityProperties.getDimensions(); if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos*dimensions)); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); } if (entityProperties.getMarketplaceID().length() != 0) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; @@ -84,7 +88,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); } _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setDimensions(dimensions); _bbOverlay->setRotation(entityProperties.getRotation()); _bbOverlay->setPosition(bbPosition); _bbOverlay->setVisible(true); @@ -105,15 +109,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distanceToEntity = glm::distance(bbPosition, cameraPosition); glm::vec3 contextOverlayPosition; glm::vec2 contextOverlayDimensions; - if (distanceToEntity > CONTEXT_OVERLAY_CLOSE_DISTANCE) { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { + if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box, position the context overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. // This makes it easy to inspect things you're holding. float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; @@ -122,6 +122,14 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + auto direction = glm::normalize(bbPosition - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); + _bbOverlay->setIgnoreRayIntersection(true); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); From c71dca5b4b44e2c6764094e727c0b362ba42155a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 11:15:44 -0700 Subject: [PATCH 160/269] Missed a magic number --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cc4f645c0a..95bdd7186c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -53,6 +53,7 @@ ContextOverlayInterface::ContextOverlayInterface() { static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims @@ -111,7 +112,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec2 contextOverlayDimensions; if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + 1.0f * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { // If the entity is too close to the camera, rotate the context overlay to the right of the entity. From 2d0c5ff37a8624bc75f7b94a5409ff841c8b1df7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 11:41:34 -0700 Subject: [PATCH 161/269] remove preloading of allowed user keys since it happens on connect --- domain-server/src/DomainGatekeeper.cpp | 15 ++------------- domain-server/src/DomainGatekeeper.h | 2 -- domain-server/src/DomainServer.cpp | 4 +--- libraries/networking/src/NodeList.cpp | 2 +- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index da31fed387..957830bc7b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -604,19 +604,6 @@ bool DomainGatekeeper::isWithinMaxCapacity() { return true; } - -void DomainGatekeeper::preloadAllowedUserPublicKeys() { - QStringList allowedUsers = _server->_settingsManager.getAllNames(); - - if (allowedUsers.size() > 0) { - // in the future we may need to limit how many requests here - for now assume that lists of allowed users are not - // going to create > 100 requests - foreach(const QString& username, allowedUsers) { - requestUserPublicKey(username); - } - } -} - void DomainGatekeeper::requestUserPublicKey(const QString& username) { // don't request public keys for the standard psuedo-account-names if (NodePermissions::standardNames.contains(username, Qt::CaseInsensitive)) { @@ -667,6 +654,8 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) { const QString JSON_DATA_KEY = "data"; const QString JSON_PUBLIC_KEY_KEY = "public_key"; + qDebug() << "Extracted public key for" << username.toLower(); + _userPublicKeys[username.toLower()] = QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()); } diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 163f255411..03a6292c55 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -39,8 +39,6 @@ public: const QUuid& walletUUID, const QString& nodeVersion); QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID); - void preloadAllowedUserPublicKeys(); - void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); } static void sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c5171620de..163bd48f1b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -160,9 +160,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : getTemporaryName(); } - _gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request - - //send signal to DomainMetadata when descriptors changed + // send signal to DomainMetadata when descriptors changed _metadata = new DomainMetadata(this); connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated, _metadata, &DomainMetadata::descriptorsChanged); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 75c97cc205..48e5c8a62c 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -327,7 +327,7 @@ void NodeList::sendDomainServerCheckIn() { << "but no keypair is present. Waiting for keypair generation to complete."; accountManager->generateNewUserKeypair(); - // don't send the check in packet - wait for the keypair first + // don't send the check in packet - wait for the new public key to be available to the domain-server first return; } From 5bc38bd7f07869f3392a8babbda49b75a63cc773 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Fri, 7 Jul 2017 11:15:42 -0400 Subject: [PATCH 162/269] [WL21389] Collision Shapes need to be updated (details below). Revised approach involves creating a helper function within ShapeFactory to aid in devising the ShapeType to be used by an ShapeEntityItem for collision. The ShapeFactory is currently doing this for creating the actual Bullet Library collision shapes. ShapeEntityItem overrides its virtually inherited computeShapeInfo which in turn calls the new ShapeFactory helper function. ShapeEntityItem has a new memvar _collisionShapeType to cache its actual ShapeType used by the physics system. This memvar is returned via the getShapeType accessor which is expected to return an object's ShapeType. Note(s): This is similar to the original approach save translation between entity::Shape and ShapeType isn't tied to the EntityItemProperties shapeTypeNames or shapeType. This approach more directly solves the issue of getting the actual ShapeType used by the time it's needed to determine the bullet collision object type created when initializing the physic information. Translation of the ShapeEntityItem's entity::Shape to its ShapeType is handled by ShapeFactory which handles creating the bullet collision objects when setting up physics on the ShapeEntityItems. Known Issue(s): This doesn't compile. It appears that the Entity Library needs to know about the Physics Library. The naive attempt at providing that link failed to resolve all compilation issues. Current Error: C1083: Cannot open include file: btBulletDynamicsCommon.h: No such file or directory (C:\projects\cusack\libraries\entities\src\ShapeEntityItem.cpp) C:\projects\cusack\libraries\physics\src\ShapeFactory.h 15 1 entities modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/physics/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js new file: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 10 +- libraries/entities/CMakeLists.txt | 3 +- libraries/entities/src/ShapeEntityItem.cpp | 54 ++++- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 203 ++++++++++++++++-- libraries/physics/src/ShapeFactory.h | 3 + libraries/shared/src/ShapeInfo.cpp | 5 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- .../tests/basicEntityTest/shapeSpawner.js | 33 +++ 9 files changed, 286 insertions(+), 33 deletions(-) create mode 100644 scripts/developer/tests/basicEntityTest/shapeSpawner.js diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 62ab3377a8..b197e0b9e6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -48,18 +48,22 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Cube); + result->setShape(entity::Shape::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Sphere); + result->setShape(entity::Shape::Sphere); return result; } diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 19341ec3e2..90de43b5f8 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars) +link_hifi_libraries(shared networking octree avatars physics) + diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 018d8c568a..d9c48f8593 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -58,7 +59,11 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - return baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); + + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -101,6 +106,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } +//TODO_CUSACK: Move back to header prior to PN +void ShapeEntityItem::setShape( const QString &shape ) { + setShape(entity::shapeFromString(shape)); +} + bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -160,10 +170,46 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } +void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} + + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + { + //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + return SHAPE_TYPE_ELLIPSOID; + } + + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { @@ -223,10 +269,12 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " shape:" << stringFromShape(_shape); + qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << "SHAPE EntityItem Ptr:" << this; } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 96f69deb0c..f021fcf957 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } + void setShape(const QString& shape); float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -84,6 +84,7 @@ public: QColor getQColor() const; void setColor(const QColor& value); + void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } @@ -100,6 +101,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index d209667966..a9bc8b2e05 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -17,7 +17,7 @@ #include "BulletUtil.h" // These are the same normalized directions used by the btShapeHull class. -// 12 points for the face centers of a duodecohedron plus another 30 points +// 12 points for the face centers of a dodecahedron plus another 30 points // for the midpoints the edges, for a total of 42. const uint32_t NUM_UNIT_SPHERE_DIRECTIONS = 42; static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = { @@ -247,6 +247,124 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +{ + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + outCollisionShape = new btSphereShape(radius); + } + + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + outCollisionShape = createConvexHull(points); + } + + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShape(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x > halfExtents.z) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeX(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + outCollisionShape = new btCylinderShapeZ(btHalfExtents); + } + + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } + + //Got here, then you are what you are along with outCollisionShape + return type; +} + +ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { + if ( shape == entity::Shape::NUM_SHAPES ) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } + + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } + + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } + + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { + + //TODO_CUSACK: Implement the hedrons + return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); + } + + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { + + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } + +} + const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -255,30 +373,33 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - case SHAPE_TYPE_SPHERE: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - shape = new btSphereShape(radius); - } - break; + //case SHAPE_TYPE_SPHERE: { + // glm::vec3 halfExtents = info.getHalfExtents(); + // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + // shape = new btSphereShape(radius); + //} + //break; + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - shape = new btSphereShape(radius); - } else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - shape = createConvexHull(points); - } + //float radius = halfExtents.x; + //const float MIN_RADIUS = 0.001f; + //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + //if (radius > MIN_RADIUS + // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // // close enough to true sphere + // shape = new btSphereShape(radius); + //} else { + // ShapeInfo::PointList points; + // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + // } + // shape = createConvexHull(points); + //} + + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); } break; case SHAPE_TYPE_CAPSULE_Y: { @@ -288,6 +409,42 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); + } + break; case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..52b448ee1d 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,11 +15,14 @@ #include #include +#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { + + ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 496e94f8bd..e40d91379c 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -136,7 +136,10 @@ float ShapeInfo::computeVolume() const { } case SHAPE_TYPE_CAPSULE_Y: { float radius = _halfExtents.x; - volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f); + // Need to offset halfExtents.y by x to account for the system treating + // the y extent of the capsule as the cylindrical height + spherical radius. + float cylinderHeight = 2.0f * (_halfExtents.y - _halfExtents.x); + volume = PI * radius * radius * (cylinderHeight + 4.0f * radius / 3.0f); break; } default: diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index 538e9145f5..fa5c9291cb 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,7 +8,9 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - type: "Sphere", + name: "Cusack_Testing", + type: "Shape", + shapeType: "Cylinder", color: { red: 200, green: 10, diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js new file mode 100644 index 0000000000..f93072a582 --- /dev/null +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -0,0 +1,33 @@ + var orientation = Camera.getOrientation(); + orientation = Quat.safeEulerAngles(orientation); + orientation.x = 0; + orientation = Quat.fromVec3Degrees(orientation); + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); + + // Math.random ensures no caching of script + var SCRIPT_URL = Script.resolvePath("myEntityScript.js") + + var myEntity = Entities.addEntity({ + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + color: { + red: 200, + green: 10, + blue: 200 + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + script: SCRIPT_URL + }) + + + function cleanup() { + // Entities.deleteEntity(myEntity); + } + + Script.scriptEnding.connect(cleanup); From 75403124b621835b1bb2c4b9a060980324ab7e1b Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 18 Jul 2017 17:15:02 -0400 Subject: [PATCH 163/269] [WL21389] Addresses physics library dependency and has some other fixes (details below). * Addresses physics library dependency by moving computeShapeInfo override from ShapeEntityItem (which is within Entities Library) to RenderableShapeEntityItem (which is in Entities-Renderer Library). ** Entities-Renderer library already links against the physic library. ** Per discussion with Andrew Meadows: In order to ShapeEntityItem to be utilized the library dependency between the Entity and Physics library would need to be resolved to avoid the cyclical reliance which isn't in the scope of this ticket. * Updates shapeSpawner test script from the default clone of basicEntityTest\entitySpawner.js ** Objects now have a finite lifetime ** Script now cleans up the objects created when the script ends ** Also moved some adjustable properties out into var aliases at the top of the file for easier/less error prone tweaking. Should probably add one for the shapeType. * Fixes some issues with validateShapeType helper function * Removed naive attempt at including physics library within entities library. * Transferred some todos from notes * Fixed some formatting NOTE(s): This compiles and runs. Cylinder is spawned and treated as CYLINDER_Y. TODO(s): * Add tweakable var for shapeType within shapeSpawner.js * Vet and verify other shapes. * Add in edge case handling. * Add in support for other shapes to ShapeInfo infrastructure. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/shapeSpawner.js --- .../src/RenderableShapeEntityItem.cpp | 25 +- .../src/RenderableShapeEntityItem.h | 2 + libraries/entities/CMakeLists.txt | 2 +- libraries/entities/src/ShapeEntityItem.cpp | 61 ++-- libraries/entities/src/ShapeEntityItem.h | 1 - libraries/physics/src/ShapeFactory.cpp | 277 +++++++++--------- libraries/shared/src/ShapeInfo.cpp | 14 +- .../tests/basicEntityTest/shapeSpawner.js | 53 ++-- 8 files changed, 226 insertions(+), 209 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b197e0b9e6..f071518b42 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -48,11 +49,12 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -86,6 +88,23 @@ bool RenderableShapeEntityItem::isTransparent() { } } +void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { + + if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { + if (_shape == entity::Shape::NUM_SHAPES) + { + EntityItem::computeShapeInfo(info); + + //--EARLY EXIT--( allow default handling to process ) + return; + } + + _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); + } + + return EntityItem::computeShapeInfo(info); +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 0cc6a54f81..96f450f523 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,6 +28,8 @@ public: bool isTransparent() override; + void computeShapeInfo(ShapeInfo& info); + private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 90de43b5f8..84906a48df 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(shared networking octree avatars physics) +link_hifi_libraries(shared networking octree avatars) diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index d9c48f8593..854be7df1e 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -12,7 +12,6 @@ #include #include -#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" @@ -59,11 +58,12 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); + auto result = baseFactory(entityID, properties); - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; + //TODO_CUSACK: Remove this before final PN + qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - return result; + return result; } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -96,9 +96,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; + _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -170,46 +172,29 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - - if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); - - //--EARLY EXIT--( allow default handling to process ) - return; - } - - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); - } - - return EntityItem::computeShapeInfo(info); -} - // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} + //TODO_CUSACK: This needs to be retrieved from properties if possible + // or stored within a new member and set during parsing of + // the properties like setShape via set/get/readEntityProperties. + // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use + // based on analysis of the shape's halfExtents when BulletLibrary collision shape was + // created as opposed to the desired ShapeType is it possible to retrieve that information)? + //if (_shape == entity::Shape::Cylinder) { + // return SHAPE_TYPE_CYLINDER_Y; + //} - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; + //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere + //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - { - //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - return SHAPE_TYPE_ELLIPSOID; - } + //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) + //{ + // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) + // return SHAPE_TYPE_ELLIPSOID; + //} - return _collisionShapeType; + return _collisionShapeType; } void ShapeEntityItem::setColor(const rgbColor& value) { diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index f021fcf957..435d22a6b2 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - void computeShapeInfo(ShapeInfo& info); ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index a9bc8b2e05..74aff45e55 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,121 +247,120 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr) +ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) { - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - outCollisionShape = new btSphereShape(radius); - } + if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) + { + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + if (outCollisionShape) { + (*outCollisionShape) = new btSphereShape(radius); + } - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - outCollisionShape = createConvexHull(points); - } + return SHAPE_TYPE_SPHERE; + } + else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + if (outCollisionShape) { + (*outCollisionShape) = createConvexHull(points); + } - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShape(btHalfExtents); - } + return SHAPE_TYPE_ELLIPSOID; + } + } + else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) + { + // TODO_CUSACK: Should allow for minor variance along axes? + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShape(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x > halfExtents.z) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeX(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_Y; + } + else if (halfExtents.x >= halfExtents.z) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - outCollisionShape = new btCylinderShapeZ(btHalfExtents); - } + return SHAPE_TYPE_CYLINDER_X; + } + else if (halfExtents.z > halfExtents.x) { + if (outCollisionShape) { + (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); + } - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } + return SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a sphere + { + ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); + return cylinderFallback; + } + } - //Got here, then you are what you are along with outCollisionShape - return type; + //Got here, then you are what you are along with outCollisionShape + return type; } ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if ( shape == entity::Shape::NUM_SHAPES ) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } + if (shape == entity::Shape::NUM_SHAPES) { + //--EARLY EXIT-- + return SHAPE_TYPE_NONE; + } - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } + const glm::vec3 halfExtents = entityDimensions * 0.5f; + switch (shape){ + case entity::Shape::Triangle: { + //TODO_CUSACK: Implement this + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } + //Note: Intentional Fallthrough from Quad to Cube + case entity::Shape::Quad: + case entity::Shape::Cube: { + return SHAPE_TYPE_BOX; + } + //Note: Intentional Fallthrough from Hexagon to Sphere + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Sphere: { + return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); + } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } + case entity::Shape::Cylinder: { + return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); + } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } + //Note: Intentional Fallthrough from Tetrahedron to Icosahedron + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: { - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } + //TODO_CUSACK: Implement the hedrons + return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); + } - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { + //Note: Intentional Fallthrough from Torus to default. + case entity::Shape::Torus: + case entity::Shape::Cone: { - //TODO_CUSACK: Implement the hedrons - return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents ); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } + // These types are currently unsupported + } + default: + return SHAPE_TYPE_NONE; + } } @@ -379,7 +378,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = new btSphereShape(radius); //} //break; - case SHAPE_TYPE_SPHERE: + case SHAPE_TYPE_SPHERE: case SHAPE_TYPE_ELLIPSOID: { glm::vec3 halfExtents = info.getHalfExtents(); //float radius = halfExtents.x; @@ -399,9 +398,11 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) // shape = createConvexHull(points); //} - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape); + validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); } break; + //TODO_CUSACK: Add Capsules to vetting/validation process for + // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -409,42 +410,44 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShape(radius, height); } break; - case SHAPE_TYPE_CAPSULE_X: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.y; - float height = 2.0f * halfExtents.x; - shape = new btCapsuleShapeX(radius, height); - } - break; - case SHAPE_TYPE_CAPSULE_Z: { - glm::vec3 halfExtents = info.getHalfExtents(); - float radius = halfExtents.x; - float height = 2.0f * halfExtents.z; - shape = new btCapsuleShapeZ(radius, height); - } - break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. - const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape); - } - break; + case SHAPE_TYPE_CAPSULE_X: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.y; + float height = 2.0f * halfExtents.x; + shape = new btCapsuleShapeX(radius, height); + } + break; + case SHAPE_TYPE_CAPSULE_Z: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + float height = 2.0f * halfExtents.z; + shape = new btCapsuleShapeZ(radius, height); + } + break; + case SHAPE_TYPE_CYLINDER_X: + case SHAPE_TYPE_CYLINDER_Z: + case SHAPE_TYPE_CYLINDER_Y: { + // TODO_CUSACK: Should allow for minor variance along axes. + const glm::vec3 halfExtents = info.getHalfExtents(); + //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { + // shape = new btCylinderShape(btHalfExtents); + //} + //else if (halfExtents.x > halfExtents.z) { + // shape = new btCylinderShapeX(btHalfExtents); + //} + //else if (halfExtents.z > halfExtents.x) { + // shape = new btCylinderShapeZ(btHalfExtents); + //} + //else //...there was no major axis, treat as a sphere + //{ + // //TODO_CUSACK: Shunt to ELLIPSOID handling + //} + validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + } + break; + //TODO_CUSACK: Add compound and simple hull to vetting/validation + // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index e40d91379c..57e927eaf3 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { @@ -55,6 +56,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -62,6 +65,8 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -70,12 +75,17 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { + //TODO_CUSACK: Should this have protection against inadvertant clearance and type + // resetting? If for some reason this was called and point list was and is emtpy + // would we still wish to clear out everything? _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { + //TODO_CUSACK: Should this pointlist clearance added in case + // this is a re-purposed instance? _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -117,6 +127,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { + //TODO_CUSACK: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -150,6 +161,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { + //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; diff --git a/scripts/developer/tests/basicEntityTest/shapeSpawner.js b/scripts/developer/tests/basicEntityTest/shapeSpawner.js index f93072a582..9fcaf3a3db 100644 --- a/scripts/developer/tests/basicEntityTest/shapeSpawner.js +++ b/scripts/developer/tests/basicEntityTest/shapeSpawner.js @@ -1,33 +1,30 @@ - var orientation = Camera.getOrientation(); - orientation = Quat.safeEulerAngles(orientation); - orientation.x = 0; - orientation = Quat.fromVec3Degrees(orientation); - var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation))); +// compute a position to create the object relative to avatar +var forwardOffset = Vec3.multiply(2.0, Quat.getFront(MyAvatar.orientation)); +var objectPosition = Vec3.sum(MyAvatar.position, forwardOffset); - // Math.random ensures no caching of script - var SCRIPT_URL = Script.resolvePath("myEntityScript.js") +var LIFETIME = 1800; //seconds +var DIM_HEIGHT = 1, DIM_WIDTH = 1, DIM_DEPTH = 1; +var COLOR_R = 100, COLOR_G = 10, COLOR_B = 200; - var myEntity = Entities.addEntity({ - name: "ShapeSpawnTest", - type: "Shape", - shape: "Cylinder", - color: { - red: 200, - green: 10, - blue: 200 - }, - position: center, - dimensions: { - x: 1, - y: 1, - z: 1 - }, - script: SCRIPT_URL - }) +var properties = { + name: "ShapeSpawnTest", + type: "Shape", + shape: "Cylinder", + dimensions: {x: DIM_WIDTH, y: DIM_HEIGHT, z: DIM_DEPTH}, + color: {red: COLOR_R, green: COLOR_G, blue: COLOR_B}, + position: objectPosition, + lifetime: LIFETIME, +}; + +// create the object +var entityId = Entities.addEntity(properties); + +function cleanup() { + Entities.deleteEntity(entityId); +} + +// delete the object when this script is stopped +Script.scriptEnding.connect(cleanup); - function cleanup() { - // Entities.deleteEntity(myEntity); - } - Script.scriptEnding.connect(cleanup); From 9e25c3b7d8d6446b7e678accf06d1da945868de4 Mon Sep 17 00:00:00 2001 From: 1P-Cusack <1p-cusack@1stplayable.com> Date: Tue, 25 Jul 2017 14:47:34 -0400 Subject: [PATCH 164/269] Renaming 'not' route to 'logicalNot'. Build was failing on non-windows platforms because 'not' is a reserved word in C++ (which Visual Studio happily ignores). --- interface/resources/controllers/vive.json | 4 ++-- libraries/controllers/src/controllers/impl/Filter.cpp | 2 +- .../controllers/src/controllers/impl/RouteBuilderProxy.cpp | 2 +- .../controllers/src/controllers/impl/RouteBuilderProxy.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index cc8e2a437a..73ab5cb2ae 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -24,7 +24,7 @@ { "from": "Vive.LS", "to": "Standard.LS" }, { "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp", "peek": true, - "filters": [ { "type": "not" } ] + "filters": [ { "type": "logicalNot" } ] }, { "from": "Vive.LSTouch", "to": "Standard.LSTouch" }, @@ -42,7 +42,7 @@ { "from": "Vive.RS", "to": "Standard.RS" }, { "from": "Vive.RSTouch", "to": "Standard.RightThumbUp", "peek": true, - "filters": [ { "type": "not" } ] + "filters": [ { "type": "logicalNot" } ] }, { "from": "Vive.RSTouch", "to": "Standard.RSTouch" }, diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index 08cba9c296..b6c3ed4d27 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -41,7 +41,7 @@ REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPos REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") -REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "not") +REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "logicalNot") REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate") diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index c6abc2d402..bc1ef55725 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -149,7 +149,7 @@ QObject* RouteBuilderProxy::pulse(float interval) { return this; } -QObject* RouteBuilderProxy::not() { +QObject* RouteBuilderProxy::logicalNot() { addFilter(std::make_shared()); return this; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index d70bdb649f..75f3747566 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -53,7 +53,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* postTransform(glm::mat4 transform); Q_INVOKABLE QObject* rotate(glm::quat rotation); Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant); - Q_INVOKABLE QObject* not(); + Q_INVOKABLE QObject* logicalNot(); private: void to(const Endpoint::Pointer& destination); From d155c0264030c3eec0c231d7f80072b6ab63662b Mon Sep 17 00:00:00 2001 From: Leander Hasty <1p-cusack@1stplayable.com> Date: Mon, 24 Jul 2017 17:02:40 -0400 Subject: [PATCH 165/269] [WL21389] wip and modifications based on comments https://github.com/highfidelity/hifi/pull/11024#pullrequestreview-51611518 Cleans up tabs, moves new functionality out of ShapeFactory directly to RenderableShapeEntityItem's computeShapeInfo override, begins to break down where we will need pointlists. Still need to determine how rotation is handled for pointlists, and check for axis alignment on cylinders before deciding on a shape. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/CMakeLists.txt modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/physics/src/ShapeFactory.h modified: libraries/shared/src/ShapeInfo.cpp modified: scripts/developer/tests/basicEntityTest/entitySpawner.js --- .../src/RenderableShapeEntityItem.cpp | 92 ++++++-- .../src/RenderableShapeEntityItem.h | 2 +- libraries/entities/CMakeLists.txt | 1 - libraries/entities/src/ShapeEntityItem.cpp | 37 +--- libraries/entities/src/ShapeEntityItem.h | 4 +- libraries/physics/src/ShapeFactory.cpp | 202 ++++-------------- libraries/physics/src/ShapeFactory.h | 3 - libraries/shared/src/ShapeInfo.cpp | 4 +- .../tests/basicEntityTest/entitySpawner.js | 4 +- 9 files changed, 122 insertions(+), 227 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index f071518b42..a305b201d6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -49,23 +49,18 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const } EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Cube); + result->setShape(entity::Cube); return result; } EntityItemPointer RenderableShapeEntityItem::sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { auto result = baseFactory(entityID, properties); - result->setShape(entity::Shape::Sphere); + result->setShape(entity::Sphere); return result; } @@ -90,19 +85,84 @@ bool RenderableShapeEntityItem::isTransparent() { void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { - if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) { - if (_shape == entity::Shape::NUM_SHAPES) - { - EntityItem::computeShapeInfo(info); + // This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc) + // is set. - //--EARLY EXIT--( allow default handling to process ) - return; + const glm::vec3 entityDimensions = getDimensions(); + + switch (_shape){ + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; + } + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO_CUSACK: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); - _collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions()); +#if 0 + // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y + // TODO_CUSACK: Should allow for minor variance along axes? + if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { + } + else if (entityDimensions.x >= entityDimensions.z) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_X; + } + else if (entityDimensions.z >= entityDimensions.x) { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; + } + else //...there was no major axis, treat as a hull + { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } +#endif + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + //TODO_CUSACK: pointCollection + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_NONE; + //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + } + break; + default:{ + //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. + } + break; } - return EntityItem::computeShapeInfo(info); + EntityItem::computeShapeInfo(info); } void RenderableShapeEntityItem::render(RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 96f450f523..2a841df57e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info); private: std::unique_ptr _procedural { nullptr }; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 84906a48df..19341ec3e2 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,3 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) link_hifi_libraries(shared networking octree avatars) - diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 854be7df1e..58799de807 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -58,12 +58,7 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity } EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - auto result = baseFactory(entityID, properties); - - //TODO_CUSACK: Remove this before final PN - qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id; - - return result; + return baseFactory(entityID, properties); } EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -93,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { + switch (_shape) { // TODO_CUSACK fill out? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? break; default: _type = EntityTypes::Shape; @@ -108,11 +103,6 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) { } } -//TODO_CUSACK: Move back to header prior to PN -void ShapeEntityItem::setShape( const QString &shape ) { - setShape(entity::shapeFromString(shape)); -} - bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -175,25 +165,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit // This value specifes how the shape should be treated by physics calculations. // For now, all polys will act as spheres ShapeType ShapeEntityItem::getShapeType() const { - //TODO_CUSACK: This needs to be retrieved from properties if possible - // or stored within a new member and set during parsing of - // the properties like setShape via set/get/readEntityProperties. - // Perhaps if the _actual_ collisionShapeType is needed (the version that's in use - // based on analysis of the shape's halfExtents when BulletLibrary collision shape was - // created as opposed to the desired ShapeType is it possible to retrieve that information)? - //if (_shape == entity::Shape::Cylinder) { - // return SHAPE_TYPE_CYLINDER_Y; - //} - - //// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere - //return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID; - - //if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) - //{ - // //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere ) - // return SHAPE_TYPE_ELLIPSOID; - //} - return _collisionShapeType; } @@ -254,7 +225,7 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const void ShapeEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << "SHAPE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " name:" << _name; + qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 435d22a6b2..42a92f7bd7 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -70,7 +70,7 @@ public: entity::Shape getShape() const { return _shape; } void setShape(const entity::Shape& shape); - void setShape(const QString& shape); + void setShape(const QString& shape) { setShape(entity::shapeFromString(shape)); } float getAlpha() const { return _alpha; }; void setAlpha(float alpha) { _alpha = alpha; } @@ -100,7 +100,7 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_NONE }; + ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 74aff45e55..87580c6726 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -247,123 +247,6 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { delete dataArray; } -ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL) -{ - if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID)) - { - float radius = halfExtents.x; - const float MIN_RADIUS = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (radius > MIN_RADIUS - && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // close enough to true sphere - if (outCollisionShape) { - (*outCollisionShape) = new btSphereShape(radius); - } - - return SHAPE_TYPE_SPHERE; - } - else { - ShapeInfo::PointList points; - points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - } - if (outCollisionShape) { - (*outCollisionShape) = createConvexHull(points); - } - - return SHAPE_TYPE_ELLIPSOID; - } - } - else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z)) - { - // TODO_CUSACK: Should allow for minor variance along axes? - const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShape(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Y; - } - else if (halfExtents.x >= halfExtents.z) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeX(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_X; - } - else if (halfExtents.z > halfExtents.x) { - if (outCollisionShape) { - (*outCollisionShape) = new btCylinderShapeZ(btHalfExtents); - } - - return SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a sphere - { - ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape); - return cylinderFallback; - } - } - - //Got here, then you are what you are along with outCollisionShape - return type; -} - -ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) { - if (shape == entity::Shape::NUM_SHAPES) { - //--EARLY EXIT-- - return SHAPE_TYPE_NONE; - } - - const glm::vec3 halfExtents = entityDimensions * 0.5f; - switch (shape){ - case entity::Shape::Triangle: { - //TODO_CUSACK: Implement this - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - //Note: Intentional Fallthrough from Quad to Cube - case entity::Shape::Quad: - case entity::Shape::Cube: { - return SHAPE_TYPE_BOX; - } - //Note: Intentional Fallthrough from Hexagon to Sphere - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Sphere: { - return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents); - } - - case entity::Shape::Cylinder: { - return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents); - } - - //Note: Intentional Fallthrough from Tetrahedron to Icosahedron - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: { - - //TODO_CUSACK: Implement the hedrons - return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents); - } - - //Note: Intentional Fallthrough from Torus to default. - case entity::Shape::Torus: - case entity::Shape::Cone: { - - // These types are currently unsupported - } - default: - return SHAPE_TYPE_NONE; - } - -} - const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); @@ -372,37 +255,32 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btBoxShape(glmToBullet(info.getHalfExtents())); } break; - //case SHAPE_TYPE_SPHERE: { - // glm::vec3 halfExtents = info.getHalfExtents(); - // float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); - // shape = new btSphereShape(radius); - //} - //break; - case SHAPE_TYPE_SPHERE: - case SHAPE_TYPE_ELLIPSOID: { + case SHAPE_TYPE_SPHERE: { glm::vec3 halfExtents = info.getHalfExtents(); - //float radius = halfExtents.x; - //const float MIN_RADIUS = 0.001f; - //const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - //if (radius > MIN_RADIUS - // && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR - // && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { - // // close enough to true sphere - // shape = new btSphereShape(radius); - //} else { - // ShapeInfo::PointList points; - // points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); - // for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { - // points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); - // } - // shape = createConvexHull(points); - //} - - validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape); + float radius = glm::max(halfExtents.x, glm::max(halfExtents.y, halfExtents.z)); + shape = new btSphereShape(radius); + } + break; + case SHAPE_TYPE_ELLIPSOID: { + glm::vec3 halfExtents = info.getHalfExtents(); + float radius = halfExtents.x; + const float MIN_RADIUS = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (radius > MIN_RADIUS + && fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) { + // close enough to true sphere + shape = new btSphereShape(radius); + } else { + ShapeInfo::PointList points; + points.reserve(NUM_UNIT_SPHERE_DIRECTIONS); + for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) { + points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents); + } + shape = createConvexHull(points); + } } break; - //TODO_CUSACK: Add Capsules to vetting/validation process for - // type checks. case SHAPE_TYPE_CAPSULE_Y: { glm::vec3 halfExtents = info.getHalfExtents(); float radius = halfExtents.x; @@ -424,30 +302,22 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) shape = new btCapsuleShapeZ(radius, height); } break; - case SHAPE_TYPE_CYLINDER_X: - case SHAPE_TYPE_CYLINDER_Z: - case SHAPE_TYPE_CYLINDER_Y: { - // TODO_CUSACK: Should allow for minor variance along axes. + case SHAPE_TYPE_CYLINDER_X: { const glm::vec3 halfExtents = info.getHalfExtents(); - //const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); - //if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) { - // shape = new btCylinderShape(btHalfExtents); - //} - //else if (halfExtents.x > halfExtents.z) { - // shape = new btCylinderShapeX(btHalfExtents); - //} - //else if (halfExtents.z > halfExtents.x) { - // shape = new btCylinderShapeZ(btHalfExtents); - //} - //else //...there was no major axis, treat as a sphere - //{ - // //TODO_CUSACK: Shunt to ELLIPSOID handling - //} - validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeX(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Z: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShapeZ(btHalfExtents); + } + case SHAPE_TYPE_CYLINDER_Y: { + const glm::vec3 halfExtents = info.getHalfExtents(); + const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); + shape = new btCylinderShape(btHalfExtents); } break; - //TODO_CUSACK: Add compound and simple hull to vetting/validation - // process for types. case SHAPE_TYPE_COMPOUND: case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 52b448ee1d..2bf79f390c 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -15,14 +15,11 @@ #include #include -#include //< Needed for entity::Shape #include // translates between ShapeInfo and btShape namespace ShapeFactory { - - ShapeType computeShapeType( entity::Shape shape, const glm::vec3 &entityDimensions); const btCollisionShape* createShapeFromInfo(const ShapeInfo& info); void deleteShape(const btCollisionShape* shape); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 57e927eaf3..5d6b4ece7a 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,8 +29,8 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? - _url = ""; + //TODO_CUSACK: Does this need additional cases and handling added? + _url = ""; _type = type; setHalfExtents(halfExtents); switch(type) { diff --git a/scripts/developer/tests/basicEntityTest/entitySpawner.js b/scripts/developer/tests/basicEntityTest/entitySpawner.js index fa5c9291cb..538e9145f5 100644 --- a/scripts/developer/tests/basicEntityTest/entitySpawner.js +++ b/scripts/developer/tests/basicEntityTest/entitySpawner.js @@ -8,9 +8,7 @@ var SCRIPT_URL = Script.resolvePath("myEntityScript.js") var myEntity = Entities.addEntity({ - name: "Cusack_Testing", - type: "Shape", - shapeType: "Cylinder", + type: "Sphere", color: { red: 200, green: 10, From ef1e426273b3412e63ee511495f8013375250245 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper Date: Tue, 25 Jul 2017 14:09:46 -0400 Subject: [PATCH 166/269] [WL21389] Some code and todo cleanup in prep for PR1. Changes to be committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/physics/src/ShapeFactory.cpp modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 29 +++++-------------- libraries/entities/src/ShapeEntityItem.cpp | 7 ++--- libraries/physics/src/ShapeFactory.cpp | 2 ++ libraries/shared/src/ShapeInfo.cpp | 20 ++++++------- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index a305b201d6..5fa2354bd5 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -114,26 +114,13 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { break; case entity::Shape::Cylinder: { _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO_CUSACK: determine if rotation is axis-aligned + // TODO WL21389: determine if rotation is axis-aligned //const Transform::Quat & rot = _transform.getRotation(); -#if 0 - // TODO: some way to tell apart SHAPE_TYPE_CYLINDER_Y - // TODO_CUSACK: Should allow for minor variance along axes? - if ((entityDimensions.y >= entityDimensions.x) && (entityDimensions.y >= entityDimensions.z)) { - } - else if (entityDimensions.x >= entityDimensions.z) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_X; - } - else if (entityDimensions.z >= entityDimensions.x) { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Z; - } - else //...there was no major axis, treat as a hull - { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection - } -#endif + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + } break; case entity::Shape::Triangle: @@ -145,15 +132,15 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Dodecahedron: case entity::Shape::Icosahedron: case entity::Shape::Cone: { - _collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - //TODO_CUSACK: pointCollection + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; } break; case entity::Shape::Torus: { // Not in GeometryCache::buildShapes, unsupported. _collisionShapeType = SHAPE_TYPE_NONE; - //TODO_CUSACK: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) } break; default:{ diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 58799de807..157e3afab3 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,14 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO_CUSACK fill out? + switch (_shape) { // TODO WL21389: fill out with other shapes? case entity::Shape::Cube: _type = EntityTypes::Box; _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; // TODO_CUSACK defer? Check to see if sphere is more appropriate? + _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,8 +162,7 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -// For now, all polys will act as spheres +// This value specifes how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 87580c6726..97174b2216 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -307,11 +307,13 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeX(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Z: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); shape = new btCylinderShapeZ(btHalfExtents); } + break; case SHAPE_TYPE_CYLINDER_Y: { const glm::vec3 halfExtents = info.getHalfExtents(); const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5d6b4ece7a..5930880859 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -29,7 +29,7 @@ void ShapeInfo::clear() { } void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) { - //TODO_CUSACK: Does this need additional cases and handling added? + //TODO WL21389: Does this need additional cases and handling added? _url = ""; _type = type; setHalfExtents(halfExtents); @@ -56,8 +56,9 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString } void ShapeInfo::setBox(const glm::vec3& halfExtents) { - //TODO_CUSACK: Should this pointlist clearance added in case + //TODO WL21389: Should this pointlist clearance added in case // this is a re-purposed instance? + // See https://github.com/highfidelity/hifi/pull/11024#discussion_r128885491 _url = ""; _type = SHAPE_TYPE_BOX; setHalfExtents(halfExtents); @@ -65,8 +66,7 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) { } void ShapeInfo::setSphere(float radius) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_SPHERE; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -75,17 +75,14 @@ void ShapeInfo::setSphere(float radius) { } void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) { - //TODO_CUSACK: Should this have protection against inadvertant clearance and type - // resetting? If for some reason this was called and point list was and is emtpy - // would we still wish to clear out everything? + //TODO WL21389: May need to skip resetting type here. _pointCollection = pointCollection; _type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; _doubleHashKey.clear(); } void ShapeInfo::setCapsuleY(float radius, float halfHeight) { - //TODO_CUSACK: Should this pointlist clearance added in case - // this is a re-purposed instance? + //TODO WL21389: See comment in setBox regarding clearance _url = ""; _type = SHAPE_TYPE_CAPSULE_Y; radius = glm::max(radius, MIN_HALF_EXTENT); @@ -127,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO_CUSACK: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes. const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { @@ -161,7 +158,7 @@ float ShapeInfo::computeVolume() const { } bool ShapeInfo::contains(const glm::vec3& point) const { - //TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound. + //TODO WL21389: Add support for other ShapeTypes like Ellipsoid/Compound. switch(_type) { case SHAPE_TYPE_SPHERE: return glm::length(point) <= _halfExtents.x; @@ -206,6 +203,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const { } const DoubleHashKey& ShapeInfo::getHash() const { + //TODO WL21389: Need to include the pointlist for SIMPLE_HULL in hash // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET; From b1aa6e19625f6e17c0f2c28cca494832cef2d96e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 13:42:29 -0700 Subject: [PATCH 167/269] Move hosting for quazip back to hifi S3 --- cmake/externals/quazip/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/quazip/CMakeLists.txt b/cmake/externals/quazip/CMakeLists.txt index d44c9ffde3..01650a432d 100644 --- a/cmake/externals/quazip/CMakeLists.txt +++ b/cmake/externals/quazip/CMakeLists.txt @@ -21,7 +21,7 @@ endif () ExternalProject_Add( ${EXTERNAL_NAME} - URL https://s3.amazonaws.com/Oculus/quazip-0.7.3.zip + URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip URL_MD5 ed03754d39b9da1775771819b8001d45 BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build CMAKE_ARGS ${QUAZIP_CMAKE_ARGS} From 6b5cabf00af08e4278828da0ac3fceec45a6f717 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Tue, 25 Jul 2017 18:12:07 -0400 Subject: [PATCH 168/269] [WL21389] Minor: Mark RenderableShapeEntityItem::computeShapeInfo as an override. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h --- libraries/entities-renderer/src/RenderableShapeEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 2a841df57e..f93e4b991e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -28,7 +28,7 @@ public: bool isTransparent() override; - virtual void computeShapeInfo(ShapeInfo& info); + virtual void computeShapeInfo(ShapeInfo& info) override; private: std::unique_ptr _procedural { nullptr }; From e9845784d5a5a6012b1b10c8729f5e5e6798ae94 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Jul 2017 13:43:30 -0700 Subject: [PATCH 169/269] 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 170/269] 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 7702bfceaa49cc555d39b1800ba7f9adea593a27 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 16:12:32 -0700 Subject: [PATCH 171/269] Big cleanup and preparation; Actually fix desktop tablet --- .../ui/overlays/ContextOverlayInterface.cpp | 138 +++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 3 + interface/src/ui/overlays/Planar3DOverlay.h | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.h | 2 +- 5 files changed, 92 insertions(+), 55 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 95bdd7186c..18cb41fda5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -39,9 +39,9 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); - QVariantMap props; auto myAvatar = DependencyManager::get()->getMyAvatar(); glm::quat cameraOrientation = qApp->getCamera().getOrientation(); + QVariantMap props; props.insert("position", vec3toVariant(myAvatar->getEyePosition() + glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_OFFSET, 0.0f))) * (CONTEXT_OVERLAY_TABLET_DISTANCE * (cameraOrientation * Vectors::FRONT)))); props.insert("orientation", quatToVariant(cameraOrientation * glm::quat(glm::radians(glm::vec3(0.0f, CONTEXT_OVERLAY_TABLET_ORIENTATION, 0.0f))))); qApp->getOverlays().editOverlay(tabletFrameID, props); @@ -68,32 +68,57 @@ static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - glm::vec3 dimensions = entityProperties.getDimensions(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * dimensions)); - } - if (entityProperties.getMarketplaceID().length() != 0) { + if (contextOverlayFilterPassed(entityItemID)) { qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; + + // Add all necessary variables to the stack + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 cameraPosition = qApp->getCamera().getPosition(); + float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); + glm::vec3 entityDimensions = entityProperties.getDimensions(); + glm::vec3 contextOverlayPosition; + glm::vec2 contextOverlayDimensions; + + // Draw the outline overlay + // This also gives us the centerpoint of the entity even if + // the the entity doesn't use the default registration point. + glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + + // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); + // Here, we determine the position and dimensions of the Context Overlay. + if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + // If the camera is inside the box... + // ...position the Context Overlay 1 meter in front of the camera. + contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { + // Else if the entity is too close to the camera... + // ...rotate the Context Overlay to the right of the entity. + // This makes it easy to inspect things you're holding. + float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + if (event.getID() == LEFT_HAND_HW_ID) { + offsetAngle *= -1; + } + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { + // Else, place the Context Overlay some offset away from the entity's bounding + // box in the direction of the camera. + auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); + PickRay pickRay(cameraPosition, direction); + _bbOverlay->setIgnoreRayIntersection(false); + auto result = qApp->getOverlays().findRayIntersection(pickRay); _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(dimensions); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); + // Finally, setup and draw the Context Overlay if (_contextOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_contextOverlayID)) { _contextOverlay = std::make_shared(); _contextOverlay->setAlpha(CONTEXT_OVERLAY_UNHOVERED_ALPHA); @@ -106,36 +131,11 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& _contextOverlay->setIsFacingAvatar(true); _contextOverlayID = qApp->getOverlays().addOverlay(_contextOverlay); } - glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceToEntity = glm::distance(bbPosition, cameraPosition); - glm::vec3 contextOverlayPosition; - glm::vec2 contextOverlayDimensions; - if (AABox(bbPosition - (dimensions / 2.0f), dimensions * 2.0f).contains(cameraPosition)) { - // If the camera is inside the box, position the context overlay 1 meter in front of the camera. - contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else if (distanceToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { - // If the entity is too close to the camera, rotate the context overlay to the right of the entity. - // This makes it easy to inspect things you're holding. - float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; - if (event.getID() == LEFT_HAND_HW_ID) { - offsetAngle *= -1; - } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { - auto direction = glm::normalize(bbPosition - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } _contextOverlay->setPosition(contextOverlayPosition); _contextOverlay->setDimensions(contextOverlayDimensions); _contextOverlay->setRotation(entityProperties.getRotation()); _contextOverlay->setVisible(true); + return true; } } else { @@ -144,17 +144,51 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return false; } +bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + return (entityProperties.getMarketplaceID().length() != 0); +} + +glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); + glm::vec3 bbPosition = entityProperties.getPosition(); + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + // Setup and draw the bounding box around the entity + if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { + _bbOverlay = std::make_shared(); + _bbOverlay->setIsSolid(false); + _bbOverlay->setColor(BB_OVERLAY_COLOR); + _bbOverlay->setDrawInFront(true); + _bbOverlay->setIgnoreRayIntersection(true); + _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); + } + _bbOverlay->setParentID(entityItemID); + _bbOverlay->setDimensions(entityProperties.getDimensions()); + _bbOverlay->setRotation(entityProperties.getRotation()); + _bbOverlay->setPosition(bbPosition); + _bbOverlay->setVisible(true); + // This returned position should always be the center of the entity + // even if the registration point isn't the default. + return bbPosition; +} + bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; setCurrentEntityWithContextOverlay(QUuid()); - qApp->getOverlays().deleteOverlay(_contextOverlayID); - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _contextOverlay = NULL; - _bbOverlay = NULL; - _contextOverlayID = UNKNOWN_OVERLAY_ID; - _bbOverlayID = UNKNOWN_OVERLAY_ID; _entityMarketplaceID.clear(); + // Destroy the Context Overlay + qApp->getOverlays().deleteOverlay(_contextOverlayID); + _contextOverlay = NULL; + _contextOverlayID = UNKNOWN_OVERLAY_ID; + // Destroy the outline overlay + qApp->getOverlays().deleteOverlay(_bbOverlayID); + _bbOverlay = NULL; + _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 812914a82e..623eb85401 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -70,6 +70,9 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 2ccf2c4513..8127d4ebb9 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -21,7 +21,7 @@ public: Planar3DOverlay(const Planar3DOverlay* planar3DOverlay); virtual AABox getBounds() const override; - glm::vec2 getSize() const { return _dimensions; }; + virtual glm::vec2 getSize() const { return _dimensions; }; glm::vec2 getDimensions() const { return _dimensions; } void setDimensions(float value) { _dimensions = glm::vec2(value); } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..1dd3389352 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -613,7 +613,7 @@ void Web3DOverlay::setScriptURL(const QString& scriptURL) { } } -glm::vec2 Web3DOverlay::getSize() { +glm::vec2 Web3DOverlay::getSize() const { return _resolution / _dpi * INCHES_TO_METERS * getDimensions(); }; diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 1e3706ed25..3d2f9dd514 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -50,7 +50,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - glm::vec2 getSize(); + glm::vec2 getSize() const override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; From e5ddded464ed54a4f942807e090fa5408eaf6129 Mon Sep 17 00:00:00 2001 From: utkarshgautamnyu Date: Tue, 25 Jul 2017 16:16:15 -0700 Subject: [PATCH 172/269] 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 173/269] 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 174/269] 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 175/269] 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 176/269] cleaning up for mac and linux --- libraries/midi/src/Midi.cpp | 112 +++++++++++++++++++++++++++++++----- libraries/midi/src/Midi.h | 11 ++-- 2 files changed, 103 insertions(+), 20 deletions(-) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 5b5376c6d3..ca6c67ad81 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -98,19 +98,6 @@ void Midi::sendNote(int status, int note, int vel) { } } -void Midi::noteReceived(int status, int note, int velocity) { - if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && - ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) { - return; // NOTE: only sending note-on and note-off to Javascript - } - - QVariantMap eventData; - eventData["status"] = status; - eventData["note"] = note; - eventData["velocity"] = velocity; - emit midiNote(eventData); -} - void Midi::MidiSetup() { midihin.clear(); @@ -187,14 +174,32 @@ void Midi::MidiCleanup() { } #endif +void Midi::noteReceived(int status, int note, int velocity) { + if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && + ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON)) { + return; // NOTE: only sending note-on and note-off to Javascript + } + + QVariantMap eventData; + eventData["status"] = status; + eventData["note"] = note; + eventData["velocity"] = velocity; +// emit midiNote(eventData); +} + // Midi::Midi() { instance = this; +#if defined Q_OS_WIN32 midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing +#endif MidiSetup(); } +Midi::~Midi() { +} + void Midi::playMidiNote(int status, int note, int velocity) { sendNote(status, note, velocity); } @@ -237,14 +242,14 @@ QStringList Midi::listMidiDevices(bool output) { void Midi::unblockMidiDevice(QString name, bool output) { if (output) { - for (int i = 0; i < midioutexclude.size(); i++) { + for (unsigned long i = 0; i < midioutexclude.size(); i++) { if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { midioutexclude.erase(midioutexclude.begin() + i); break; } } } else { - for (int i = 0; i < midiinexclude.size(); i++) { + for (unsigned long i = 0; i < midiinexclude.size(); i++) { if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { midiinexclude.erase(midiinexclude.begin() + i); break; @@ -266,3 +271,80 @@ void Midi::thruModeEnable(bool enable) { thruModeEnabled = enable; } +/* +/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -arch x86_64 -isysroot +/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -L/Users/burt/hifi/assignment-client/Debug -F/Users/burt/hifi/assignment-client/Debug -F/Users/burt/Qt5.6.2/5.6/clang_64/lib -filelist +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client.LinkFileList -Xlinker -rpath -Xlinker +/Users/burt/Qt5.6.2/5.6/clang_64/lib -mmacosx-version-min=10.8 -Xlinker -object_path_lto -Xlinker +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_lto.o -Xlinker -no_deduplicate -stdlib=libc++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names +/Users/burt/hifi/libraries/audio/Debug/libaudio.a +/Users/burt/hifi/libraries/avatars/Debug/libavatars.a +/Users/burt/hifi/libraries/octree/Debug/liboctree.a +/Users/burt/hifi/libraries/gpu/Debug/libgpu.a +/Users/burt/hifi/libraries/model/Debug/libmodel.a +/Users/burt/hifi/libraries/fbx/Debug/libfbx.a +/Users/burt/hifi/libraries/entities/Debug/libentities.a +/Users/burt/hifi/libraries/networking/Debug/libnetworking.a +/Users/burt/hifi/libraries/animation/Debug/libanimation.a +/Users/burt/hifi/libraries/recording/Debug/librecording.a +/Users/burt/hifi/libraries/shared/Debug/libshared.a +/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a +/Users/burt/hifi/libraries/embedded-webserver/Debug/libembedded-webserver.a +/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a +/Users/burt/hifi/libraries/physics/Debug/libphysics.a +/Users/burt/hifi/libraries/plugins/Debug/libplugins.a +/Users/burt/hifi/libraries/ui/Debug/libui.a +/Users/burt/hifi/libraries/script-engine/Debug/libscript-engine.a +/Users/burt/hifi/libraries/ui/Debug/libui.a +/Users/burt/hifi/libraries/recording/Debug/librecording.a +/Users/burt/hifi/libraries/controllers/Debug/libcontrollers.a +/Users/burt/hifi/libraries/physics/Debug/libphysics.a +/Users/burt/hifi/libraries/entities/Debug/libentities.a +/Users/burt/hifi/libraries/audio/Debug/libaudio.a +/Users/burt/hifi/libraries/plugins/Debug/libplugins.a +/Users/burt/hifi/libraries/avatars/Debug/libavatars.a +/Users/burt/hifi/libraries/octree/Debug/liboctree.a +/Users/burt/hifi/libraries/animation/Debug/libanimation.a +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletDynamics.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletCollision.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libLinearMath.dylib +/Users/burt/hifi/ext/Xcode/bullet/project/lib/libBulletSoftBody.dylib +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScriptTools.framework/QtScriptTools +/Users/burt/hifi/ext/Xcode/quazip/project/lib/libquazip5d.1.0.0.dylib +/Users/burt/hifi/libraries/procedural/Debug/libprocedural.a +/Users/burt/hifi/libraries/model-networking/Debug/libmodel-networking.a +/Users/burt/hifi/libraries/fbx/Debug/libfbx.a +/Users/burt/hifi/libraries/model/Debug/libmodel.a +/Users/burt/hifi/libraries/image/Debug/libimage.a +/Users/burt/hifi/ext/Xcode/nvtt/project/lib/libnvtt.dylib +/Users/burt/hifi/libraries/gpu-gl/Debug/libgpu-gl.a +/Users/burt/hifi/libraries/gpu/Debug/libgpu.a +/Users/burt/hifi/libraries/ktx/Debug/libktx.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtConcurrent.framework/QtConcurrent -lpthread +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebSockets.framework/QtWebSockets +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtXmlPatterns.framework/QtXmlPatterns +/Users/burt/hifi/libraries/gl/Debug/libgl.a +/Users/burt/hifi/libraries/networking/Debug/libnetworking.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngine.framework/QtWebEngine +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebEngineCore.framework/QtWebEngineCore +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQuick.framework/QtQuick +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWebChannel.framework/QtWebChannel +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtPositioning.framework/QtPositioning +/usr/local/Cellar/openssl/1.0.2k/lib/libssl.dylib +/usr/local/Cellar/openssl/1.0.2k/lib/libcrypto.dylib +/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbb_debug.dylib +/Users/burt/hifi/ext/Xcode/tbb/project/src/tbb/lib/libtbbmalloc_debug.dylib -framework IOKit -framework CoreFoundation +/Users/burt/hifi/libraries/shared/Debug/libshared.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtScript.framework/QtScript +/usr/local/lib/libz.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtQml.framework/QtQml +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtOpenGL.framework/QtOpenGL +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtWidgets.framework/QtWidgets +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtGui.framework/QtGui -framework OpenGL +/Users/burt/hifi/ext/Xcode/glew/project/lib/libglew_d.a +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtNetwork.framework/QtNetwork +/Users/burt/Qt5.6.2/5.6/clang_64/lib/QtCore.framework/QtCore -Xlinker -dependency_info -Xlinker +/Users/burt/hifi/assignment-client/hifi.build/Debug/assignment-client.build/Objects-normal/x86_64/assignment-client_dependency_info.dat -o +/Users/burt/hifi/assignment-client/Debug/assignment-client +*/ + diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index e8ba803ad4..54ffdb58e8 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -22,9 +22,6 @@ class Midi : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - -public: - Midi(); public: void noteReceived(int status, int note, int velocity); // relay a note to Javascript @@ -39,8 +36,8 @@ private: void MidiSetup(); void MidiCleanup(); -signals: - void midiNote(QVariantMap eventData); +//signals: +// void midiNote(QVariantMap eventData); public slots: /// play a note on all connected devices @@ -66,6 +63,10 @@ Q_INVOKABLE void unblockMidiDevice(QString name, bool output); /// repeat all incoming notes to all outputs (default disabled) Q_INVOKABLE void thruModeEnable(bool enable); + +public: + Midi(); + virtual ~Midi(); }; #endif // hifi_Midi_h From 9020607ccfa75fd5ded41d1a08079c299966b20f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 25 Jul 2017 17:54:46 -0700 Subject: [PATCH 177/269] First pass --- interface/src/Application.cpp | 6 + .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 12 + libraries/render/src/render/Args.h | 6 + scripts/system/marketplaces/marketplaces.js | 206 +++++++++--------- 5 files changed, 133 insertions(+), 103 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 699ddfcb2d..978f20d743 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5393,6 +5393,12 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se } renderArgs->_debugFlags = renderDebugFlags; //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); + + RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = static_cast(renderOutlineFlags | + static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..7c768dbfa0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -37,7 +37,8 @@ class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) - Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled); + Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) + Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -53,6 +54,8 @@ public: void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setEnabled(bool enabled) { _enabled = enabled; } bool getEnabled() { return _enabled; } + bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } + void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); @@ -70,6 +73,7 @@ private: bool _contextOverlayJustClicked { false }; void openMarketplace(); + bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9884debcce..52308499d7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,6 +372,18 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } + bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); + if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); + gpu::Batch& batch = *args->_batch; + bool success; + auto shapeTransform = getTransformToCenter(success); + if (success) { + batch.setModelTransform(shapeTransform); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(args, batch, yellowColor); + } + } + if (!hasModel() || (_model && _model->didVisualGeometryRequestFail())) { static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index 449a3ac22b..f53454cf75 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -63,6 +63,11 @@ namespace render { public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; + enum OutlineFlags { + RENDER_OUTLINE_NONE = 0, + RENDER_OUTLINE_WIREFRAMES = 1, + RENDER_OUTLINE_SHADER = 2 + }; enum DebugFlags { RENDER_DEBUG_NONE = 0, RENDER_DEBUG_HULLS = 1 @@ -112,6 +117,7 @@ namespace render { int _boundaryLevelAdjust { 0 }; RenderMode _renderMode { DEFAULT_RENDER_MODE }; DisplayMode _displayMode { MONO }; + OutlineFlags _outlineFlags{ RENDER_OUTLINE_NONE }; DebugFlags _debugFlags { RENDER_DEBUG_NONE }; gpu::Batch* _batch = nullptr; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3be8143830..462beac538 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -11,135 +11,137 @@ /* global Tablet, Script, HMD, UserActivityLogger, Entities */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ -(function() { // BEGIN LOCAL_SCOPE +(function () { // BEGIN LOCAL_SCOPE -Script.include("../libraries/WebTablet.js"); + Script.include("../libraries/WebTablet.js"); -var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. -var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); -var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); + var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. + var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); + var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); -var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; + // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -// Event bridge messages. -var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; -var CLARA_IO_STATUS = "CLARA.IO STATUS"; -var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; -var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; -var GOTO_DIRECTORY = "GOTO_DIRECTORY"; -var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; -var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; -var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + // Event bridge messages. + var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; + var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; + var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; + var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; -var CLARA_DOWNLOAD_TITLE = "Preparing Download"; -var messageBox = null; -var isDownloadBeingCancelled = false; + var CLARA_DOWNLOAD_TITLE = "Preparing Download"; + var messageBox = null; + var isDownloadBeingCancelled = false; -var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel -var NO_BUTTON = 0; // QMessageBox::NoButton + var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel + var NO_BUTTON = 0; // QMessageBox::NoButton -var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; + var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; -function onMessageBoxClosed(id, button) { - if (id === messageBox && button === CANCEL_BUTTON) { - isDownloadBeingCancelled = true; - messageBox = null; - tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; + tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); + } } -} -Window.messageBoxClosed.connect(onMessageBoxClosed); + Window.messageBoxClosed.connect(onMessageBoxClosed); -var onMarketplaceScreen = false; + var onMarketplaceScreen = false; -function showMarketplace() { - UserActivityLogger.openedMarketplace(); - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - tablet.webEventReceived.connect(function (message) { + function showMarketplace() { + UserActivityLogger.openedMarketplace(); + tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.webEventReceived.connect(function (message) { - if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } + if (message === GOTO_DIRECTORY) { + tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + } - if (message === QUERY_CAN_WRITE_ASSETS) { - tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } + if (message === QUERY_CAN_WRITE_ASSETS) { + tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + } - if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } + if (message === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + } - if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { + if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.slice(CLARA_IO_STATUS.length); + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } return; } - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; } - return; - } - if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; + if (message === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; } - return; - } + }); + } - if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var marketplaceButton = tablet.addButton({ + icon: "icons/tablet-icons/market-i.svg", + activeIcon: "icons/tablet-icons/market-a.svg", + text: "MARKET", + sortOrder: 9 }); -} -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var marketplaceButton = tablet.addButton({ - icon: "icons/tablet-icons/market-i.svg", - activeIcon: "icons/tablet-icons/market-a.svg", - text: "MARKET", - sortOrder: 9 -}); - -function onCanWriteAssetsChanged() { - var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - tablet.emitScriptEvent(message); -} - -function onClick() { - if (onMarketplaceScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); - showMarketplace(); + function onCanWriteAssetsChanged() { + var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); + tablet.emitScriptEvent(message); } -} -function onScreenChanged(type, url) { - onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL - // for toolbar mode: change button to active when window is first openend, false otherwise. - marketplaceButton.editProperties({isActive: onMarketplaceScreen}); -} - -marketplaceButton.clicked.connect(onClick); -tablet.screenChanged.connect(onScreenChanged); -Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); - -Script.scriptEnding.connect(function () { - if (onMarketplaceScreen) { - tablet.gotoHomeScreen(); + function onClick() { + if (onMarketplaceScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + ContextOverlay.isInMarketplaceInspectionMode = false; + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + showMarketplace(); + ContextOverlay.isInMarketplaceInspectionMode = true; + } } - tablet.removeButton(marketplaceButton); - tablet.screenChanged.disconnect(onScreenChanged); - Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); -}); + + function onScreenChanged(type, url) { + onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL + // for toolbar mode: change button to active when window is first openend, false otherwise. + marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + } + + marketplaceButton.clicked.connect(onClick); + tablet.screenChanged.connect(onScreenChanged); + Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); + + Script.scriptEnding.connect(function () { + if (onMarketplaceScreen) { + tablet.gotoHomeScreen(); + } + tablet.removeButton(marketplaceButton); + tablet.screenChanged.disconnect(onScreenChanged); + Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); + }); }()); // END LOCAL_SCOPE From d2975c79c9b5f495e365a03fcfedd6f1ed37c308 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 08:41:26 -0700 Subject: [PATCH 178/269] 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 179/269] 1 more --- tests/render-perf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 553e7c06e7..a1ecb7df2a 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural) +link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural midi) package_libraries_for_deployment() From 33ff5917be70f26b4b5472c52661fcfd8ddfc9c5 Mon Sep 17 00:00:00 2001 From: "scromie@turnmeupgames.com" Date: Wed, 26 Jul 2017 19:34:20 +0300 Subject: [PATCH 180/269] fix --- scripts/system/html/js/entityProperties.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 777ef54085..7ef1a6974b 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -475,6 +475,15 @@ function unbindAllInputs() { } } +function clearSelection() { + if(document.selection && document.selection.empty) { + document.selection.empty(); + } else if(window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + } +} + function loaded() { openEventBridge(function() { @@ -1051,6 +1060,7 @@ function loaded() { activeElement.select(); } } + clearSelection(); } }); } From 215fa7b816c943b656f7033eaf7183feb8f64fcb Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 09:34:46 -0700 Subject: [PATCH 181/269] 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 182/269] 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 183/269] Removed Compound Collision type --- interface/resources/qml/AssetServer.qml | 11 ++--------- .../resources/qml/hifi/dialogs/TabletAssetServer.qml | 8 +------- .../resources/qml/hifi/tablet/NewModelDialog.qml | 3 +-- scripts/system/edit.js | 5 ----- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 11cab8b8de..a400897276 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -153,9 +153,7 @@ ScrollingWindow { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; - - + var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -163,9 +161,7 @@ ScrollingWindow { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; - - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; var prompt = desktop.customInputDialog({ @@ -210,9 +206,6 @@ ScrollingWindow { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 15f72cfbcb..55c2c71c61 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -154,9 +154,7 @@ Rectangle { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; - var SHAPE_TYPES = []; SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision"; SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model"; @@ -164,8 +162,7 @@ Rectangle { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; - + var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; var prompt = tabletRoot.customInputDialog({ @@ -210,9 +207,6 @@ Rectangle { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } diff --git a/interface/resources/qml/hifi/tablet/NewModelDialog.qml b/interface/resources/qml/hifi/tablet/NewModelDialog.qml index f0c9c3c14a..47d28486a9 100644 --- a/interface/resources/qml/hifi/tablet/NewModelDialog.qml +++ b/interface/resources/qml/hifi/tablet/NewModelDialog.qml @@ -147,8 +147,7 @@ Rectangle { "Good - Sub-meshes", "Exact - All polygons", "Box", - "Sphere", - "Compound"] + "Sphere"] } Row { diff --git a/scripts/system/edit.js b/scripts/system/edit.js index fbd1732b0b..390bd5beb3 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -339,7 +339,6 @@ var toolBar = (function () { var SHAPE_TYPE_STATIC_MESH = 3; var SHAPE_TYPE_BOX = 4; var SHAPE_TYPE_SPHERE = 5; - var SHAPE_TYPE_COMPOUND = 6; var DYNAMIC_DEFAULT = false; function handleNewModelDialogResult(result) { @@ -362,9 +361,6 @@ var toolBar = (function () { case SHAPE_TYPE_SPHERE: shapeType = "sphere"; break; - case SHAPE_TYPE_COMPOUND: - shapeType = "compound"; - break; default: shapeType = "none"; } @@ -464,7 +460,6 @@ var toolBar = (function () { SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons"; SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box"; SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere"; - SHAPE_TYPES[SHAPE_TYPE_COMPOUND] = "Compound"; var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; // tablet version of new-model dialog From 9147bc21d0a3dadebf61fcf4220899fe45511c49 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 11:41:27 -0700 Subject: [PATCH 184/269] Getting there. --- .../ui/overlays/ContextOverlayInterface.cpp | 76 ++++++++----------- .../src/ui/overlays/ContextOverlayInterface.h | 6 +- .../src/RenderableModelEntityItem.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 20 +++++ libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityItemProperties.cpp | 6 ++ libraries/entities/src/EntityItemProperties.h | 1 + .../src/EntityItemPropertiesDefaults.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + scripts/system/marketplaces/marketplaces.js | 7 +- 12 files changed, 76 insertions(+), 53 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..2562d7aa76 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,6 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -76,13 +78,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 entityDimensions = entityProperties.getDimensions(); - glm::vec3 contextOverlayPosition; + glm::vec3 entityPosition = entityProperties.getPosition(); + glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec2 contextOverlayDimensions; - // Draw the outline overlay - // This also gives us the centerpoint of the entity even if - // the the entity doesn't use the default registration point. - glm::vec3 entityCenterPoint = drawOutlineOverlay(entityItemID); + // Update the position of the overlay if the registration point of the entity + // isn't default + if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { + glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); + entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + } + + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); @@ -91,7 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& setCurrentEntityWithContextOverlay(entityItemID); // Here, we determine the position and dimensions of the Context Overlay. - if (AABox(entityCenterPoint - (entityDimensions / 2.0f), entityDimensions * 2.0f).contains(cameraPosition)) { + if (boundingBox.contains(cameraPosition)) { // If the camera is inside the box... // ...position the Context Overlay 1 meter in front of the camera. contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); @@ -104,17 +111,17 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityProperties.getPosition() - cameraPosition)) + cameraPosition; + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { // Else, place the Context Overlay some offset away from the entity's bounding // box in the direction of the camera. - auto direction = glm::normalize(entityProperties.getPosition() - cameraPosition); - PickRay pickRay(cameraPosition, direction); - _bbOverlay->setIgnoreRayIntersection(false); - auto result = qApp->getOverlays().findRayIntersection(pickRay); - _bbOverlay->setIgnoreRayIntersection(true); - contextOverlayPosition = result.intersection - direction * CONTEXT_OVERLAY_FAR_OFFSET; + glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); + float distance; + BoxFace face; + glm::vec3 normal; + bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } @@ -149,33 +156,6 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent return (entityProperties.getMarketplaceID().length() != 0); } -glm::vec3 ContextOverlayInterface::drawOutlineOverlay(const EntityItemID& entityItemID) { - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - glm::vec3 bbPosition = entityProperties.getPosition(); - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - bbPosition = bbPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); - } - - // Setup and draw the bounding box around the entity - if (_bbOverlayID == UNKNOWN_OVERLAY_ID || !qApp->getOverlays().isAddedOverlay(_bbOverlayID)) { - _bbOverlay = std::make_shared(); - _bbOverlay->setIsSolid(false); - _bbOverlay->setColor(BB_OVERLAY_COLOR); - _bbOverlay->setDrawInFront(true); - _bbOverlay->setIgnoreRayIntersection(true); - _bbOverlayID = qApp->getOverlays().addOverlay(_bbOverlay); - } - _bbOverlay->setParentID(entityItemID); - _bbOverlay->setDimensions(entityProperties.getDimensions()); - _bbOverlay->setRotation(entityProperties.getRotation()); - _bbOverlay->setPosition(bbPosition); - _bbOverlay->setVisible(true); - // This returned position should always be the center of the entity - // even if the registration point isn't the default. - return bbPosition; -} - bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; @@ -185,10 +165,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt qApp->getOverlays().deleteOverlay(_contextOverlayID); _contextOverlay = NULL; _contextOverlayID = UNKNOWN_OVERLAY_ID; - // Destroy the outline overlay - qApp->getOverlays().deleteOverlay(_bbOverlayID); - _bbOverlay = NULL; - _bbOverlayID = UNKNOWN_OVERLAY_ID; return true; } return false; @@ -227,6 +203,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } +void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + //if (contextOverlayFilterPassed(entityID)) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + //} +} + +void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +} + static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 7c768dbfa0..c15e4854bd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -23,7 +23,6 @@ #include "EntityScriptingInterface.h" #include "ui/overlays/Image3DOverlay.h" -#include "ui/overlays/Cube3DOverlay.h" #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" @@ -44,9 +43,7 @@ class ContextOverlayInterface : public QObject, public Dependency { QSharedPointer _hmdScriptingInterface; QSharedPointer _tabletScriptingInterface; OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; - OverlayID _bbOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; - std::shared_ptr _bbOverlay { nullptr }; public: ContextOverlayInterface(); @@ -64,6 +61,8 @@ public slots: void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); + void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; @@ -76,7 +75,6 @@ private: bool _isInMarketplaceInspectionMode { false }; bool contextOverlayFilterPassed(const EntityItemID& entityItemID); - glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 52308499d7..a03360a0e5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,8 +372,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = (bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES); - if (getMarketplaceID().length() != 0 && showingEntityHighlight) { + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; bool success; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5996327e87..82ddb9c90d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LOCKED; requestedProperties += PROP_USER_DATA; requestedProperties += PROP_MARKETPLACE_ID; + requestedProperties += PROP_SHOULD_HIGHLIGHT; requestedProperties += PROP_NAME; requestedProperties += PROP_HREF; requestedProperties += PROP_DESCRIPTION; @@ -277,6 +278,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_LOCKED, getLocked()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, getUserData()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); @@ -828,6 +830,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID); } + if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT) { + READ_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); + } + READ_ENTITY_PROPERTY(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); @@ -2807,6 +2813,20 @@ void EntityItem::setMarketplaceID(const QString& value) { }); } +bool EntityItem::getShouldHighlight() const { + bool result; + withReadLock([&] { + result = _shouldHighlight; + }); + return result; +} + +void EntityItem::setShouldHighlight(const bool value) { + withWriteLock([&] { + _shouldHighlight = value; + }); +} + uint32_t EntityItem::getDirtyFlags() const { uint32_t result; withReadLock([&] { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 92c83651aa..36ac6ba1cc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -316,6 +316,9 @@ public: QString getMarketplaceID() const; void setMarketplaceID(const QString& value); + bool getShouldHighlight() const; + void setShouldHighlight(const bool value); + // TODO: get rid of users of getRadius()... float getRadius() const; @@ -532,6 +535,7 @@ protected: QString _userData; SimulationOwner _simulationOwner; QString _marketplaceID; + bool _shouldHighlight { false }; QString _name; QString _href; //Hyperlink href QString _description; //Hyperlink description diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index a207902789..aa7075f5e0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -289,6 +289,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_START, radiusStart); CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); + CHECK_PROPERTY_CHANGE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); CHECK_PROPERTY_CHANGE(PROP_NAME, name); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); @@ -406,6 +407,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MARKETPLACE_ID, marketplaceID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOULD_HIGHLIGHT, shouldHighlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); @@ -982,6 +984,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float); ADD_PROPERTY_TO_MAP(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString); + ADD_PROPERTY_TO_MAP(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); @@ -1334,6 +1337,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, properties.getMarketplaceID()); + APPEND_ENTITY_PROPERTY(PROP_SHOULD_HIGHLIGHT, properties.getShouldHighlight()); APPEND_ENTITY_PROPERTY(PROP_NAME, properties.getName()); APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, properties.getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, properties.getActionData()); @@ -1632,6 +1636,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOULD_HIGHLIGHT, bool, setShouldHighlight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACTION_DATA, QByteArray, setActionData); @@ -1746,6 +1751,7 @@ void EntityItemProperties::markAllChanged() { //_alphaFinishChanged = true; _marketplaceIDChanged = true; + _shouldHighlightChanged = true; _keyLight.markAllChanged(); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b526ac663c..916532b47c 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -170,6 +170,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, ParticleEffectEntityItem::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); + DEFINE_PROPERTY_REF(PROP_SHOULD_HIGHLIGHT, ShouldHighlight, shouldHighlight, bool, ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index d52c5d9aab..43d0e33ba6 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -27,6 +27,7 @@ const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f); const bool ENTITY_ITEM_DEFAULT_LOCKED = false; const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString(""); +const bool ENTITY_ITEM_DEFAULT_SHOULD_HIGHLIGHT = false; const QUuid ENTITY_ITEM_DEFAULT_SIMULATOR_ID = QUuid(); const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f; diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b3cfc143c2..9600d0d4fe 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -78,6 +78,7 @@ enum EntityPropertyList { PROP_COMPOUND_SHAPE_URL, // used by Model + zones entities PROP_MARKETPLACE_ID, // all entities + PROP_SHOULD_HIGHLIGHT, // all entities PROP_ACCELERATION, // all entities PROP_SIMULATION_OWNER, // formerly known as PROP_SIMULATOR_ID PROP_NAME, // all entities diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 240697d890..d2500196d9 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -62,7 +62,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_BULLET_DYNAMICS; + return VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 848bfd97cf..cb3db791b4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -218,6 +218,7 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; +const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 462beac538..418cf61834 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -116,12 +116,10 @@ if (onMarketplaceScreen) { // for toolbar-mode: go back to home screen, this will close the window. tablet.gotoHomeScreen(); - ContextOverlay.isInMarketplaceInspectionMode = false; } else { var entity = HMD.tabletID; Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); showMarketplace(); - ContextOverlay.isInMarketplaceInspectionMode = true; } } @@ -129,6 +127,11 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); + if (onMarketplaceScreen) { + ContextOverlay.isInMarketplaceInspectionMode = true; + } else { + ContextOverlay.isInMarketplaceInspectionMode = false; + } } marketplaceButton.clicked.connect(onClick); From 24ddbb4b22353606fb48be2bcae53a9aeed93fe9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:08:55 -0700 Subject: [PATCH 185/269] Silly bug preventing the whole thing from working --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 978f20d743..27848a1193 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5396,9 +5396,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = static_cast(renderOutlineFlags | - static_cast(RenderArgs::RENDER_OUTLINE_WIREFRAMES)); + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; } + renderArgs->_outlineFlags = renderOutlineFlags; } } From 62f6c10b2b04306744b0bb87697997d4e8f77258 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 12:45:10 -0700 Subject: [PATCH 186/269] Renaming things; bugfixes; improvements --- interface/src/Application.cpp | 6 ++-- .../ui/overlays/ContextOverlayInterface.cpp | 30 ++++++++++++------- .../src/ui/overlays/ContextOverlayInterface.h | 10 +++---- .../src/EntityTreeRenderer.cpp | 2 +- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27848a1193..d9b014d48d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1350,17 +1350,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(overlays, SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(clickContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverEnterContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&))); connect(overlays, SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)), DependencyManager::get().data(), - SLOT(hoverLeaveContextOverlay(const OverlayID&, const PointerEvent&))); + SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&))); // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 2562d7aa76..3fdd005672 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -36,8 +36,8 @@ ContextOverlayInterface::ContextOverlayInterface() { auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(highlightEntity(const EntityItemID&, const PointerEvent&))); - connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(unHighlightEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); + connect(entityTreeRenderer, SIGNAL(hoverLeaveEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverLeaveEntity(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { if (_contextOverlayJustClicked && _hmdScriptingInterface->isMounted()) { QUuid tabletFrameID = _hmdScriptingInterface->getCurrentTabletFrameID(); @@ -89,6 +89,9 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); // Update the cached Entity Marketplace ID @@ -159,6 +162,8 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -174,7 +179,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt return ContextOverlayInterface::destroyContextOverlay(entityItemID, PointerEvent()); } -void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; openMarketplace(); @@ -183,7 +188,7 @@ void ContextOverlayInterface::clickContextOverlay(const OverlayID& overlayID, co } } -void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Started hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -193,7 +198,7 @@ void ContextOverlayInterface::hoverEnterContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event) { +void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID && _contextOverlay) { qCDebug(context_overlay) << "Stopped hovering over Context Overlay. Overlay ID:" << overlayID; _contextOverlay->setColor(CONTEXT_OVERLAY_COLOR); @@ -203,16 +208,18 @@ void ContextOverlayInterface::hoverLeaveContextOverlay(const OverlayID& overlayI } } -void ContextOverlayInterface::highlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - //if (contextOverlayFilterPassed(entityID)) { +void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (contextOverlayFilterPassed(entityID)) { qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); - //} + } } -void ContextOverlayInterface::unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); +void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { + if (_currentEntityWithContextOverlay != entityID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + } } static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; @@ -227,5 +234,6 @@ void ContextOverlayInterface::openMarketplace() { QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; tablet->gotoWebScreen(url); _hmdScriptingInterface->openTablet(); + _isInMarketplaceInspectionMode = true; } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c15e4854bd..fc539661c4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -58,11 +58,11 @@ public slots: bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event); bool destroyContextOverlay(const EntityItemID& entityItemID); - void clickContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverEnterContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void hoverLeaveContextOverlay(const OverlayID& overlayID, const PointerEvent& event); - void highlightEntity(const EntityItemID& entityID, const PointerEvent& event); - void unHighlightEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); + void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); + void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); private: bool _verboseLogging { true }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a8eca41077..d55352d31e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -675,7 +675,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); - bool precisionPicking = false; // for mouse moves we do not do precision picking + bool precisionPicking = true; // for mouse moves we do precision picking RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { From 65c28ca82123234ab22057668925b45e9b581236 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 13:17:51 -0700 Subject: [PATCH 187/269] Bugfixes --- .../src/ui/overlays/ContextOverlayInterface.cpp | 14 +++++++++++--- scripts/system/marketplaces/marketplaces.js | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3fdd005672..4ecf919cfe 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -97,6 +97,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // Update the cached Entity Marketplace ID _entityMarketplaceID = entityProperties.getMarketplaceID(); + + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } + // Update the cached "Current Entity with Context Overlay" variable setCurrentEntityWithContextOverlay(entityItemID); @@ -162,8 +168,10 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; + qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay @@ -222,7 +230,7 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } -static const QString MARKETPLACE_BASE_URL = "http://metaverse.highfidelity.com/marketplace/items/"; +static const QString MARKETPLACE_BASE_URL = "https://metaverse.highfidelity.com/marketplace/items/"; void ContextOverlayInterface::openMarketplace() { // lets open the tablet and go to the current item in diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 418cf61834..7b25589e92 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -127,7 +127,7 @@ onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL // for toolbar mode: change button to active when window is first openend, false otherwise. marketplaceButton.editProperties({ isActive: onMarketplaceScreen }); - if (onMarketplaceScreen) { + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { ContextOverlay.isInMarketplaceInspectionMode = false; From 5bd6dac66c87a3369e2e2360d689ec5ce46aa6ff Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:24:56 -0700 Subject: [PATCH 188/269] wrap text if longer than 1m text entity --- scripts/system/chat.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 58a1849f1f..65c7567b30 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -43,6 +43,7 @@ var speechBubbleOffset = {x: 0, y: 0.3, z: 0.0}; // The offset from the joint to whic the speech bubble is attached. var speechBubbleJointName = 'Head'; // The name of the joint to which the speech bubble is attached. var speechBubbleLineHeight = 0.05; // The height of a line of text in the speech bubble. + var SPEECH_BUBBLE_MAX_WIDTH = 1; // meters // Load the persistent variables from the Settings, with defaults. function loadSettings() { @@ -645,8 +646,16 @@ //print("updateSpeechBubble:", "speechBubbleMessage", speechBubbleMessage, "textSize", textSize.width, textSize.height); var fudge = 0.02; + var width = textSize.width + fudge; - var height = textSize.height + fudge; + var height = speechBubbleLineHeight + fudge; + + if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + var numLines = Math.ceil(width); + height = speechBubbleLineHeight * numLines + fudge; + width = SPEECH_BUBBLE_MAX_WIDTH; + }; + dimensions = { x: width, y: height, @@ -672,6 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); + position.y += height / 2; // offset based on wrapped height of bubble speechBubbleParams.position = position; if (!speechBubbleTextID) { From 256853f79b4d15370540950c3e2041c961ab5d1e Mon Sep 17 00:00:00 2001 From: Liv Date: Wed, 26 Jul 2017 13:28:52 -0700 Subject: [PATCH 189/269] syntax and clarification on magic number 2 as half height --- scripts/system/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 65c7567b30..85de9fd0ac 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -650,7 +650,7 @@ var width = textSize.width + fudge; var height = speechBubbleLineHeight + fudge; - if(textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { + if (textSize.width >= SPEECH_BUBBLE_MAX_WIDTH) { var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; @@ -681,7 +681,7 @@ Vec3.sum( headPosition, rotatedOffset); - position.y += height / 2; // offset based on wrapped height of bubble + position.y += height / 2; // offset based on half of bubble height speechBubbleParams.position = position; if (!speechBubbleTextID) { From a960ea5ccbba13fbeba68c55fcbd3de43bcb46c0 Mon Sep 17 00:00:00 2001 From: Burt Sloane Date: Wed, 26 Jul 2017 13:32:23 -0700 Subject: [PATCH 190/269] Linux build --- libraries/script-engine/CMakeLists.txt | 2 +- tests/controllers/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 39338fd767..42b2d51417 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image) +link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 7be71928be..5757a3ad4b 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -6,7 +6,7 @@ setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers midi) +link_hifi_libraries(shared gl script-engine plugins render-utils ui-plugins input-plugins display-plugins controllers) if (WIN32) From 3a05219c59ae3de69f7fe81e5883d2073f63ff97 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 14:10:41 -0700 Subject: [PATCH 191/269] Closer still --- .../ui/overlays/ContextOverlayInterface.cpp | 25 ++++--- .../src/ui/overlays/ContextOverlayInterface.h | 4 +- .../system/controllers/handControllerGrab.js | 73 +++++++++++++------ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 4ecf919cfe..aa162e6101 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -89,8 +89,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); } - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + enableEntityHighlight(entityItemID); AABox boundingBox = AABox(entityPosition - (entityDimensions / 2.0f), entityDimensions * 2.0f); @@ -99,8 +98,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } // Update the cached "Current Entity with Context Overlay" variable @@ -169,8 +167,7 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << _currentEntityWithContextOverlay; - qApp->getEntities()->getTree()->findEntityByEntityItemID(_currentEntityWithContextOverlay)->setShouldHighlight(false); + disableEntityHighlight(_currentEntityWithContextOverlay); } setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); @@ -218,15 +215,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { if (contextOverlayFilterPassed(entityID)) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(true); + enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { if (_currentEntityWithContextOverlay != entityID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityID)->setShouldHighlight(false); + disableEntityHighlight(entityID); } } @@ -245,3 +240,13 @@ void ContextOverlayInterface::openMarketplace() { _isInMarketplaceInspectionMode = true; } } + +void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); +} + +void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index fc539661c4..4b9c67c5d1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -63,6 +63,7 @@ public slots: void contextOverlays_hoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event); void contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event); void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); + bool contextOverlayFilterPassed(const EntityItemID& entityItemID); private: bool _verboseLogging { true }; @@ -74,7 +75,8 @@ private: void openMarketplace(); bool _isInMarketplaceInspectionMode { false }; - bool contextOverlayFilterPassed(const EntityItemID& entityItemID); + void enableEntityHighlight(const EntityItemID& entityItemID); + void disableEntityHighlight(const EntityItemID& entityItemID); }; #endif // hifi_ContextOverlayInterface_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3268031f8b..247246b11d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -187,7 +187,8 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -var potentialEntityWithContextOverlay = false; +var hoveredEntityID = false; +var contextOverlayTimer = false; var entityWithContextOverlay = false; var contextualHand = -1; @@ -2231,28 +2232,54 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - if (rayPickInfo.entityID && !entityWithContextOverlay) { - Script.setTimeout(function () { - if (rayPickInfo.entityID === potentialEntityWithContextOverlay && - !entityWithContextOverlay - && contextualHand !== -1) { - var pointerEvent = { - type: "Move", - id: contextualHand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { - entityWithContextOverlay = rayPickInfo.entityID; - potentialEntityWithContextOverlay = false; - } + if (rayPickInfo.entityID) { + if (hoveredEntityID !== rayPickInfo.entityID) { + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; + + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } - }, 500); - contextualHand = this.hand; - potentialEntityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = rayPickInfo.entityID; + Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); + } + + if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { + Script.clearTimeout(contextOverlayTimer); + } + + // If we already have a context overlay, we don't want to move it to + // another entity while we're searching. + if (!entityWithContextOverlay) { + contextOverlayTimer = Script.setTimeout(function () { + if (rayPickInfo.entityID === hoveredEntityID && + !entityWithContextOverlay + && contextualHand !== -1) { + var pointerEvent = { + type: "Move", + id: contextualHand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + entityWithContextOverlay = rayPickInfo.entityID; + hoveredEntityID = false; + } + } + contextOverlayTimer = false; + }, 500); + contextualHand = this.hand; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -3503,7 +3530,7 @@ function MyController(hand) { if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - potentialEntityWithContextOverlay = false; + hoveredEntityID = false; } if (isInEditMode()) { From 17c7e38fcecaf6b6c463d4a4da59724c007d1927 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:02:52 -0700 Subject: [PATCH 192/269] Bugfixes? --- .../system/controllers/handControllerGrab.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 247246b11d..37712ff90c 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2201,6 +2201,10 @@ function MyController(hand) { this.searchExit = function () { contextualHand = -1; + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + } + hoveredEntityID = false; }; this.search = function(deltaTime, timestamp) { @@ -2486,8 +2490,10 @@ function MyController(hand) { button: "None" }; + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); this.hoverEntity = entity; - Entities.sendHoverEnterEntity(entity, pointerEvent); + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); } // send mouse events for button highlights and tooltips. @@ -2551,8 +2557,11 @@ function MyController(hand) { button: "None" }; - this.hoverOverlay = overlay; - Overlays.sendHoverEnterOverlay(overlay, pointerEvent); + if (this.hoverOverlay !== overlay) { + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = overlay; + Overlays.sendHoverEnterOverlay(this.hoverOverlay, pointerEvent); + } // Send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || @@ -3527,10 +3536,13 @@ function MyController(hand) { var existingSearchDistance = this.searchSphereDistance; this.release(); + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } if (entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(entityWithContextOverlay); entityWithContextOverlay = false; - hoveredEntityID = false; } if (isInEditMode()) { From 1336a59b6c35aaf9cbe21f259460b4e6d123e007 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 15:14:58 -0700 Subject: [PATCH 193/269] Stupid bracket --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 37712ff90c..a3bf6d4bd8 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2490,10 +2490,11 @@ function MyController(hand) { button: "None" }; - if (this.hoverEntity !== entity) { - Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); - this.hoverEntity = entity; - Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + if (this.hoverEntity !== entity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = entity; + Entities.sendHoverEnterEntity(this.hoverEntity, pointerEvent); + } } // send mouse events for button highlights and tooltips. From 9c9cfad4c483ad8d626c2721ad8fb245ec76cfff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Jul 2017 15:56:08 -0700 Subject: [PATCH 194/269] use ctrl rather than alt to activate camera-mode shortcuts --- interface/src/Menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 72a3eb6bfc..56bd3eb749 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -223,17 +223,17 @@ Menu::Menu() { // View > First Person cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, Qt::ALT | Qt::Key_F, + viewMenu, MenuOption::FirstPerson, Qt::CTRL | Qt::Key_F, true, qApp, SLOT(cameraMenuChanged()))); // View > Third Person cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::ThirdPerson, Qt::ALT | Qt::Key_G, + viewMenu, MenuOption::ThirdPerson, Qt::CTRL | Qt::Key_G, false, qApp, SLOT(cameraMenuChanged()))); // View > Mirror cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FullscreenMirror, Qt::ALT | Qt::Key_H, + viewMenu, MenuOption::FullscreenMirror, Qt::CTRL | Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); // View > Independent [advanced] From 39cb0bc357e881f2cb7cdb6fff998205dd28a791 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:12:54 -0700 Subject: [PATCH 195/269] WHYYYY DOESN'T THIS WORK --- .../ui/overlays/ContextOverlayInterface.cpp | 12 +++-- .../system/controllers/handControllerGrab.js | 44 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index aa162e6101..e6d1af0af5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -242,11 +242,15 @@ void ContextOverlayInterface::openMarketplace() { } void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + if (!qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'true' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(true); + } } void ContextOverlayInterface::disableEntityHighlight(const EntityItemID& entityItemID) { - qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; - qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + if (qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->getShouldHighlight()) { + qCDebug(context_overlay) << "Setting 'shouldHighlight' to 'false' for Entity ID:" << entityItemID; + qApp->getEntities()->getTree()->findEntityByEntityItemID(entityItemID)->setShouldHighlight(false); + } } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index a3bf6d4bd8..82691003e2 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2236,18 +2236,22 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None" + }; if (rayPickInfo.entityID) { + print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { - pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "None" - }; - + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } if (hoveredEntityID) { Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); } @@ -2255,17 +2259,14 @@ function MyController(hand) { Entities.sendHoverEnterEntity(hoveredEntityID, pointerEvent); } - if (contextOverlayTimer && rayPickInfo.entityID != hoveredEntityID) { - Script.clearTimeout(contextOverlayTimer); - } - // If we already have a context overlay, we don't want to move it to // another entity while we're searching. - if (!entityWithContextOverlay) { + if (!entityWithContextOverlay && !contextOverlayTimer) { contextOverlayTimer = Script.setTimeout(function () { if (rayPickInfo.entityID === hoveredEntityID && - !entityWithContextOverlay - && contextualHand !== -1) { + !entityWithContextOverlay && + contextualHand !== -1 && + contextOverlayTimer) { var pointerEvent = { type: "Move", id: contextualHand + 1, // 0 is reserved for hardware mouse @@ -2284,6 +2285,15 @@ function MyController(hand) { }, 500); contextualHand = this.hand; } + } else { + if (hoveredEntityID) { + Entities.sendHoverLeaveEntity(hoveredEntityID, pointerEvent); + hoveredEntityID = false; + } + if (contextOverlayTimer) { + Script.clearTimeout(contextOverlayTimer); + contextOverlayTimer = false; + } } var candidateHotSpotEntities = Entities.findEntities(handPosition, MAX_EQUIP_HOTSPOT_RADIUS); From 8a7561d61ba30391147691349c3ecab9416cf46a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 26 Jul 2017 16:35:55 -0700 Subject: [PATCH 196/269] IT'S WORKING SOB SOB SOB --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++---- scripts/system/controllers/handControllerGrab.js | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e6d1af0af5..668766abbf 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -153,7 +153,10 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& return true; } } else { - return destroyContextOverlay(entityItemID, event); + if (!_currentEntityWithContextOverlay.isNull()) { + return destroyContextOverlay(_currentEntityWithContextOverlay, event); + } + return false; } return false; } @@ -166,9 +169,7 @@ bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& ent bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_contextOverlayID != UNKNOWN_OVERLAY_ID) { qCDebug(context_overlay) << "Destroying Context Overlay on top of entity with ID: " << entityItemID; - if (!_currentEntityWithContextOverlay.isNull() && _currentEntityWithContextOverlay != entityItemID) { - disableEntityHighlight(_currentEntityWithContextOverlay); - } + disableEntityHighlight(entityItemID); setCurrentEntityWithContextOverlay(QUuid()); _entityMarketplaceID.clear(); // Destroy the Context Overlay diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 82691003e2..bd8ab26506 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2246,7 +2246,6 @@ function MyController(hand) { button: "None" }; if (rayPickInfo.entityID) { - print("ZRF: " + hoveredEntityID); if (hoveredEntityID !== rayPickInfo.entityID) { if (contextOverlayTimer) { Script.clearTimeout(contextOverlayTimer); From c57660c82d3f17b8759f6dd3271d584c361f72f4 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Wed, 26 Jul 2017 16:52:00 -0700 Subject: [PATCH 197/269] 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 198/269] 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 199/269] Remove nmake troubleshooting step --- BUILD_WIN.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index e9a4a5fd3b..3e93656d45 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -75,14 +75,6 @@ For any problems after Step #6, first try this: Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. -#### nmake cannot be found - -Make sure nmake.exe is located at the following path: - - C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin - -If not, add the directory where nmake is located to the PATH environment variable. - #### Qt is throwing an error Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly. From dd76c30de5b703a5250fddd4978e869b517922ce Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Thu, 27 Jul 2017 17:31:30 +0700 Subject: [PATCH 200/269] Make Xylophone mallets equipable, make the mallets provide haptic feedback, fix a closing bracket bug stopping the xylophone from working and clean up code according to coding standards --- .../marketplace/xylophone/pUtils.js | 34 ----- .../marketplace/xylophone/xylophoneKey.js | 48 +++++-- .../marketplace/xylophone/xylophoneRezzer.js | 117 +++++++++++------- 3 files changed, 111 insertions(+), 88 deletions(-) delete mode 100644 unpublishedScripts/marketplace/xylophone/pUtils.js diff --git a/unpublishedScripts/marketplace/xylophone/pUtils.js b/unpublishedScripts/marketplace/xylophone/pUtils.js deleted file mode 100644 index 2cafbc1f50..0000000000 --- a/unpublishedScripts/marketplace/xylophone/pUtils.js +++ /dev/null @@ -1,34 +0,0 @@ -// -// pUtils.js -// -// Created by Patrick Gosch on 03/28/2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -getEntityTextures = function(id) { - var results = null; - var properties = Entities.getEntityProperties(id, "textures"); - if (properties.textures) { - try { - results = JSON.parse(properties.textures); - } catch (err) { - logDebug(err); - logDebug(properties.textures); - } - } - return results ? results : {}; -}; - -setEntityTextures = function(id, textureList) { - var json = JSON.stringify(textureList); - Entities.editEntity(id, {textures: json}); -}; - -editEntityTextures = function(id, textureName, textureURL) { - var textureList = getEntityTextures(id); - textureList[textureName] = textureURL; - setEntityTextures(id, textureList); -}; diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index afba7e8075..38b8552b6e 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -11,8 +11,9 @@ (function() { Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; - var TEXGRAY = Script.resolvePath("xylotex_bar_gray.png"); - var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); + var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); + var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); + var IS_DEBUG = false; var _this; function XylophoneKey() { @@ -22,7 +23,7 @@ XylophoneKey.prototype = { sound: null, isWaiting: false, - homePos: null, + homePosition: null, injector: null, preload: function(entityID) { @@ -45,20 +46,51 @@ hit: function() { if (!_this.isWaiting) { _this.isWaiting = true; - _this.homePos = Entities.getEntityProperties(_this.entityID, ["position"]).position; - _this.injector = Audio.playSound(_this.sound, {position: _this.homePos, volume: 1}); - editEntityTextures(_this.entityID, "file5", TEXGRAY); + _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; + _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; + var HAPTIC_DURATION = 20; + var HAPTIC_HAND = 2; // Both hands + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); _this.timeout(); } }, timeout: function() { Script.setTimeout(function() { - editEntityTextures(_this.entityID, "file5", TEXBLACK); + _this.editEntityTextures(_this.entityID, "file5", TEXTURE_BLACK); _this.isWaiting = false; }, TIMEOUT); + }, + + getEntityTextures: function(id) { + var results = null; + var properties = Entities.getEntityProperties(id, "textures"); + if (properties.textures) { + try { + results = JSON.parse(properties.textures); + } catch (err) { + if (IS_DEBUG) { + print(err); + print(properties.textures); + } + } + } + return results ? results : {}; + }, + + setEntityTextures: function(id, textureList) { + var json = JSON.stringify(textureList); + Entities.editEntity(id, {textures: json}); + }, + + editEntityTextures: function(id, textureName, textureURL) { + var textureList = _this.getEntityTextures(id); + textureList[textureName] = textureURL; + _this.setEntityTextures(id, textureList); + } }; return new XylophoneKey(); - }); diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index 6416f81037..e99341bb19 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -8,65 +8,69 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var soundFiles = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; -var keyModelURL = Script.resolvePath("xyloKey_2_a_e.fbx"); -var keyScriptURL = Script.resolvePath("xylophoneKey.js"); -var TEXBLACK = Script.resolvePath("xylotex_bar_black.png"); -var malletModelURL = Script.resolvePath("Mallet3-2pc.fbx"); -var malletModelColliderURL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; +var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); +var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); +var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); +var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); +var FORWARD = { x: 0, y: 0, z: -1 }; var center = MyAvatar.position; -var fwd = {x:0, y:0, z:-1}; -var xyloFramePos = Vec3.sum(center, Vec3.multiply(fwd, 0.8)); -var xyloFrameID = Entities.addEntity( { +var XYLOPHONE_FORWARD_OFFSET = 0.8; +var xylophoneFramePosition = Vec3.sum(center, Vec3.multiply(FORWARD, XYLOPHONE_FORWARD_OFFSET)); +var xylophoneFrameID = Entities.addEntity({ name: "Xylophone", type: "Model", modelURL: Script.resolvePath("xylophoneFrameWithWave.fbx"), - position: xyloFramePos, - rotation: Quat.fromVec3Radians({x:0, y:Math.PI, z:0}), + position: xylophoneFramePosition, + rotation: Quat.fromVec3Radians({ x: 0, y: Math.PI, z: 0 }), shapeType: "static-mesh" }); -center.y += (0.45); // key Y offset from frame -var keyPos, keyRot, ud, td, keyID; -for (var i = 1; i <= soundFiles.length; i++) { +var KEY_Y_OFFSET = 0.45; +center.y += KEY_Y_OFFSET; +var keyPosition, keyRotation, userData, textureData, keyID; +var ROTATION_START = 0.9; +var ROTATION_DELTA = 0.2; +for (var i = 1; i <= SOUND_FILES.length; i++) { + + keyRotation = Quat.fromVec3Radians({ x: 0, y: ROTATION_START - (i*ROTATION_DELTA), z: 0 }); + keyPosition = Vec3.sum(center, Vec3.multiplyQbyV(keyRotation, FORWARD)); - keyRot = Quat.fromVec3Radians({x:0, y:(0.9 - (i*0.2)), z:0}); - keyPos = Vec3.sum(center, Vec3.multiplyQbyV(keyRot, fwd)); - - ud = { - soundFile: soundFiles[i-1] + userData = { + soundFile: SOUND_FILES[i-1] }; - td = { + textureData = { "file4": Script.resolvePath("xylotex_bar" + i + ".png"), - "file5": TEXBLACK + "file5": TEXTURE_BLACK }; - keyID = Entities.addEntity( { + keyID = Entities.addEntity({ name: ("XyloKey" + i), type: "Model", - modelURL: keyModelURL, - position: keyPos, - rotation: keyRot, + modelURL: KEY_MODEL_URL, + position: keyPosition, + rotation: keyRotation, shapeType: "static-mesh", - script: keyScriptURL, - textures: JSON.stringify(td), - userData: JSON.stringify(ud), - parentID: xyloFrameID - } ); + script: KEY_SCRIPT_URL, + textures: JSON.stringify(textureData), + userData: JSON.stringify(userData), + parentID: xylophoneFrameID + }); } // if rezzed on/above something, wait until after model has loaded so you can read its dimensions then move object on to that surface. -var pickRay = {origin: center, direction: {x:0, y:-1, z:0}}; +var pickRay = {origin: center, direction: {x: 0, y: -1, z: 0}}; var intersection = Entities.findRayIntersection(pickRay, true); if (intersection.intersects && (intersection.distance < 10)) { var surfaceY = intersection.intersection.y; Script.setTimeout( function() { // should add loop to check for fbx loaded instead of delay - var xyloDimensions = Entities.getEntityProperties(xyloFrameID, ["dimensions"]).dimensions; - xyloFramePos.y = surfaceY + (xyloDimensions.y/2); - Entities.editEntity(xyloFrameID, {position: xyloFramePos}); + var xylophoneDimensions = Entities.getEntityProperties(xylophoneFrameID, ["dimensions"]).dimensions; + xylophoneFramePosition.y = surfaceY + (xylophoneDimensions.y/2); + Entities.editEntity(xylophoneFrameID, {position: xylophoneFramePosition}); rezMallets(); }, 2000); } else { @@ -75,28 +79,49 @@ if (intersection.intersects && (intersection.distance < 10)) { } function rezMallets() { - var malletProps = { + var malletProperties = { name: "Xylophone Mallet", type: "Model", - modelURL: malletModelURL, - compoundShapeURL: malletModelColliderURL, - collidesWith: "static,dynamic,kinematic,", + modelURL: MALLET_MODEL_URL, + compoundShapeURL: MALLET_MODEL_COLLIDER_URL, + collidesWith: "static,dynamic,kinematic", collisionMask: 7, collisionsWillMove: 1, dynamic: 1, damping: 1, angularDamping: 1, shapeType: "compound", - userData: "{\"grabbableKey\":{\"grabbable\":true}}", - dimensions: {"x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911} // not being set from fbx for some reason. + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + }, + wearable: { + joints: { + LeftHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ], + RightHand: [ + { x: 0, y: 0.2, z: 0.04 }, + Quat.fromVec3Degrees({ x: 0, y: 90, z: 90 }) + ] + } + } + }), + dimensions: { "x": 0.057845603674650192, "y": 0.057845607399940491, "z": 0.30429631471633911 } // not being set from fbx for some reason. }; - malletProps.position = Vec3.sum(xyloFramePos, {x: 0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI - 0.1, z:0}); - Entities.addEntity(malletProps); + var LEFT_MALLET_POSITION = { x: 0.1, y: 0.55, z: 0 }; + var LEFT_MALLET_ROTATION = { x: 0, y: Math.PI - 0.1, z: 0 }; + var RIGHT_MALLET_POSITION = { x: -0.1, y: 0.55, z: 0 }; + var RIGHT_MALLET_ROTATION = { x: 0, y: Math.PI + 0.1, z: 0 }; - malletProps.position = Vec3.sum(xyloFramePos, {x: -0.1, y: 0.55, z: 0}); - malletProps.rotation = Quat.fromVec3Radians({x:0, y:Math.PI + 0.1, z:0}); - Entities.addEntity(malletProps); + malletProperties.position = Vec3.sum(xylophoneFramePosition, LEFT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(LEFT_MALLET_ROTATION); + Entities.addEntity(malletProperties); + + malletProperties.position = Vec3.sum(xylophoneFramePosition, RIGHT_MALLET_POSITION); + malletProperties.rotation = Quat.fromVec3Radians(RIGHT_MALLET_ROTATION); + Entities.addEntity(malletProperties); Script.stop(); } \ No newline at end of file From f7add1f6c6cd6a443577cf8e7fc93464376a48b1 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 13:17:42 -0700 Subject: [PATCH 201/269] 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 202/269] 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 203/269] 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 204/269] 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 205/269] 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 206/269] Update Midi.cpp --- libraries/midi/src/Midi.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 7716602144..ad650cf067 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -20,9 +20,11 @@ #endif +#if defined Q_OS_WIN32 const int MIDI_BYTE_MASK = 0x0FF; const int MIDI_SHIFT_NOTE = 8; const int MIDI_SHIFT_VELOCITY = 16; +#endif const int MIDI_STATUS_MASK = 0x0F0; const int MIDI_NOTE_OFF = 0x080; const int MIDI_NOTE_ON = 0x090; From 8b7a29c3f3250ebfd777bf93f8d0153981feb3b2 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Fri, 28 Jul 2017 06:22:29 +0700 Subject: [PATCH 207/269] remove putils reference --- unpublishedScripts/marketplace/xylophone/xylophoneKey.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 38b8552b6e..5cff66e557 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,7 +9,6 @@ // (function() { - Script.include(Script.resolvePath("pUtils.js")); var TIMEOUT = 150; var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); From 55e23a31069087ca6d4db8445c0fc72cff3bc050 Mon Sep 17 00:00:00 2001 From: nimisha20 Date: Thu, 27 Jul 2017 16:57:08 -0700 Subject: [PATCH 208/269] 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 209/269] 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 210/269] 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 211/269] 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 212/269] Merge pull request #10987 from SamGondelman/hmd59Crash2 Fix crash when putting on HMD for Qt 5.9 --- plugins/oculus/src/OculusBaseDisplayPlugin.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 93f4787f0f..0df504dfa2 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -126,15 +126,17 @@ void OculusBaseDisplayPlugin::internalDeactivate() { } bool OculusBaseDisplayPlugin::activateStandBySession() { - _session = acquireOculusSession(); if (!_session) { - return false; + _session = acquireOculusSession(); } - return true; + return _session; } void OculusBaseDisplayPlugin::deactivateSession() { - releaseOculusSession(); - _session = nullptr; + // FIXME + // Switching to Qt 5.9 exposed a race condition or similar issue that caused a crash when putting on an Rift + // while already in VR mode. Commenting these out is a workaround. + //releaseOculusSession(); + //_session = nullptr; } void OculusBaseDisplayPlugin::updatePresentPose() { //mat4 sensorResetMat; From 45934cb53e2b1622ef3a0366f6703b68f563ca33 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 28 Jul 2017 18:04:29 +0200 Subject: [PATCH 213/269] Fix Landscape mode, when menu pushed in Create mode. Fix pages gets white in Create mode --- interface/src/ui/overlays/Web3DOverlay.cpp | 2 ++ libraries/ui/src/ui/TabletScriptingInterface.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index acba15d2ec..080c72f3bb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -205,6 +205,8 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); + _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); + tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); // mark the TabletProxy object as cpp ownership. diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6ff5e46cea..b74d9fcb5c 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -483,6 +483,11 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { return result; } + //set landscape off when pushing menu items while in Create mode + if (_landscape) { + setLandscape(false); + } + QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; From 3ec9640ea1e8bdf201dd56e8043432bf9b4262e2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 28 Jul 2017 09:22:47 -0700 Subject: [PATCH 214/269] fix calculation of walk motor for HMD --- interface/src/avatar/MyAvatar.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b644defde2..83d170300b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1586,9 +1586,14 @@ void MyAvatar::updateMotors() { motorRotation = getMyHead()->getHeadOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift - // so we decompose camera's rotation and store the twist part in motorRotation + // we decompose camera's rotation and store the twist part in motorRotation + // however, we need to perform the decomposition in the avatar-frame + // using the local UP axis and then transform back into world-frame + glm::quat orientation = getOrientation(); + glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame glm::quat liftRotation; - motorRotation = getOrientation(); + swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation); + motorRotation = orientation * motorRotation; } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; From 3d0c13915a247040acbf631b9e91c68e9ba4152b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 09:33:02 -0700 Subject: [PATCH 215/269] Fix tablet button crash --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 6ff5e46cea..37a2bae0bf 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -825,7 +825,13 @@ TabletButtonProxy::~TabletButtonProxy() { void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) { Q_ASSERT(QThread::currentThread() == qApp->thread()); + if (_qmlButton) { + QObject::disconnect(_qmlButton, &QQuickItem::destroyed, this, nullptr); + } _qmlButton = qmlButton; + if (_qmlButton) { + QObject::connect(_qmlButton, &QQuickItem::destroyed, this, [this] { _qmlButton = nullptr; }); + } } void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) { From cc4fbc97cdab663701315f823e928e9fd2429646 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Fri, 28 Jul 2017 15:59:17 -0400 Subject: [PATCH 216/269] [WL21389] PR1 Update based on code review discussion & feedback (details below). * Removed some left overs from prior approach. * Moved _collisionShapeType & getShapeType override from ShapeEntityItem to RenderableShapeEntityItem (see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r130154903) * Switched _collisionShapeType default from SHAPE_TYPE_NONE to SHAPE_TYPE_ELLIPSOID ** see thread: https://github.com/highfidelity/hifi/pull/11048#discussion_r129982909 Note(s): * Retested and the cylinder behaves as expected along with the Box & Sphere shapes save from the previously mentioned caveats in the PR notes (https://github.com/highfidelity/hifi/pull/11048) * Confirmed that currently unsupported shapes (hedrons, polygons, & cone) fallback to ellipsoid behavior given default change. Changes Committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp modified: libraries/entities-renderer/src/RenderableShapeEntityItem.h modified: libraries/entities/src/ShapeEntityItem.cpp modified: libraries/entities/src/ShapeEntityItem.h modified: libraries/shared/src/ShapeInfo.cpp --- .../src/RenderableShapeEntityItem.cpp | 112 +++++++++--------- .../src/RenderableShapeEntityItem.h | 7 ++ libraries/entities/src/ShapeEntityItem.cpp | 9 +- libraries/entities/src/ShapeEntityItem.h | 2 - libraries/shared/src/ShapeInfo.cpp | 2 +- 5 files changed, 67 insertions(+), 65 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5fa2354bd5..c853335915 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -91,67 +90,72 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { const glm::vec3 entityDimensions = getDimensions(); switch (_shape){ - case entity::Shape::Quad: - case entity::Shape::Cube: { - _collisionShapeType = SHAPE_TYPE_BOX; - } - break; - case entity::Shape::Sphere: { - - float diameter = entityDimensions.x; - const float MIN_DIAMETER = 0.001f; - const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; - if (diameter > MIN_DIAMETER - && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR - && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { - - _collisionShapeType = SHAPE_TYPE_SPHERE; + case entity::Shape::Quad: + case entity::Shape::Cube: { + _collisionShapeType = SHAPE_TYPE_BOX; } - else { + break; + case entity::Shape::Sphere: { + + float diameter = entityDimensions.x; + const float MIN_DIAMETER = 0.001f; + const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f; + if (diameter > MIN_DIAMETER + && fabsf(diameter - entityDimensions.y) / diameter < MIN_RELATIVE_SPHERICAL_ERROR + && fabsf(diameter - entityDimensions.z) / diameter < MIN_RELATIVE_SPHERICAL_ERROR) { + + _collisionShapeType = SHAPE_TYPE_SPHERE; + } + else { + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + } + } + break; + case entity::Shape::Cylinder: { + _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; + // TODO WL21389: determine if rotation is axis-aligned + //const Transform::Quat & rot = _transform.getRotation(); + + // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and + // hull ( or dimensions, need circular cross section) + // Should allow for minor variance along axes? + + } + break; + case entity::Shape::Triangle: + case entity::Shape::Hexagon: + case entity::Shape::Octagon: + case entity::Shape::Circle: + case entity::Shape::Tetrahedron: + case entity::Shape::Octahedron: + case entity::Shape::Dodecahedron: + case entity::Shape::Icosahedron: + case entity::Shape::Cone: { + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) + //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + } + break; + case entity::Shape::Torus: + { + // Not in GeometryCache::buildShapes, unsupported. + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; + //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) + } + break; + default:{ _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } - } - break; - case entity::Shape::Cylinder: { - _collisionShapeType = SHAPE_TYPE_CYLINDER_Y; - // TODO WL21389: determine if rotation is axis-aligned - //const Transform::Quat & rot = _transform.getRotation(); - - // TODO WL21389: some way to tell apart SHAPE_TYPE_CYLINDER_Y, _X, _Z based on rotation and - // hull ( or dimensions, need circular cross section) - // Should allow for minor variance along axes? - - } - break; - case entity::Shape::Triangle: - case entity::Shape::Hexagon: - case entity::Shape::Octagon: - case entity::Shape::Circle: - case entity::Shape::Tetrahedron: - case entity::Shape::Octahedron: - case entity::Shape::Dodecahedron: - case entity::Shape::Icosahedron: - case entity::Shape::Cone: { - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; - } - break; - case entity::Shape::Torus: - { - // Not in GeometryCache::buildShapes, unsupported. - _collisionShapeType = SHAPE_TYPE_NONE; - //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later if desired support) - } - break; - default:{ - //_collisionShapeType = SHAPE_TYPE_NONE; // Remains SHAPE_TYPE_NONE. - } - break; + break; } EntityItem::computeShapeInfo(info); } +// This value specifes how the shape should be treated by physics calculations. +ShapeType RenderableShapeEntityItem::getShapeType() const { + return _collisionShapeType; +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index f93e4b991e..d603cdedef 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -29,10 +29,17 @@ public: bool isTransparent() override; virtual void computeShapeInfo(ShapeInfo& info) override; + ShapeType getShapeType() const override; + private: std::unique_ptr _procedural { nullptr }; + //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain + //! prior functionality where new or unsupported shapes are treated as + //! ellipsoids. + ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID }; + SIMPLE_RENDERABLE(); }; diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 157e3afab3..eb1bed503c 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -88,14 +88,12 @@ EntityItemProperties ShapeEntityItem::getProperties(EntityPropertyFlags desiredP void ShapeEntityItem::setShape(const entity::Shape& shape) { _shape = shape; - switch (_shape) { // TODO WL21389: fill out with other shapes? + switch (_shape) { case entity::Shape::Cube: _type = EntityTypes::Box; - _collisionShapeType = ShapeType::SHAPE_TYPE_BOX; break; case entity::Shape::Sphere: _type = EntityTypes::Sphere; - _collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID; break; default: _type = EntityTypes::Shape; @@ -162,11 +160,6 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -// This value specifes how the shape should be treated by physics calculations. -ShapeType ShapeEntityItem::getShapeType() const { - return _collisionShapeType; -} - void ShapeEntityItem::setColor(const rgbColor& value) { memcpy(_color, value, sizeof(rgbColor)); } diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 42a92f7bd7..60fcfd628a 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -84,7 +84,6 @@ public: QColor getQColor() const; void setColor(const QColor& value); - ShapeType getShapeType() const override; bool shouldBePhysical() const override { return !isDead(); } bool supportsDetailedRayIntersection() const override; @@ -100,7 +99,6 @@ protected: float _alpha { 1 }; rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; - ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_NONE }; }; #endif // hifi_ShapeEntityItem_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 5930880859..a556548b25 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -124,7 +124,7 @@ int ShapeInfo::getLargestSubshapePointCount() const { } float ShapeInfo::computeVolume() const { - //TODO WL21389: Add support for other ShapeTypes. + //TODO WL21389: Add support for other ShapeTypes( CYLINDER_X, CYLINDER_Y, etc). const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; switch(_type) { From 17dd028ec7ffc761920dbcf4226bf691ef66acba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 28 Jul 2017 14:23:18 -0700 Subject: [PATCH 217/269] 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 218/269] 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 219/269] Disable touch event emulation for tablet until we fix crash --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a069b67d2b..32b16ff39b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -337,11 +337,19 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { + // FIXME touch event emulation is broken in some way. Do NOT enable this code + // unless you have done a debug build of the application and verified that + // you are not getting assertion errors on handling the touch events inside + // Qt. +#if 0 if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { handlePointerEventAsMouse(event); } +#else + handlePointerEventAsMouse(event); +#endif } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { From 3c9ab36416a187251a22947358a522dfdc4e6ace Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 29 Jul 2017 12:00:41 +0200 Subject: [PATCH 220/269] Remove already already implemented fix --- interface/src/ui/overlays/Web3DOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ffb4faf83e..2bcb247df8 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -192,7 +192,6 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - _webSurface->getSurfaceContext()->setContextProperty("Paths", DependencyManager::get().data()); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); From 734330dc063311990e8308bed080582b8655fbc7 Mon Sep 17 00:00:00 2001 From: Menithal Date: Sat, 29 Jul 2017 15:48:31 +0300 Subject: [PATCH 221/269] Fixed Particle Editor Update event Particle Editor was agressively refreshing when ever a particle was be ing up dated Added a flag check to make sure the particle was always using the latest version --- scripts/system/edit.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..02cc35e645 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -14,7 +14,7 @@ Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool */ (function() { // BEGIN LOCAL_SCOPE - + "use strict"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function () { +selectionManager.addEventListener(function (test) { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); @@ -275,7 +275,8 @@ var toolBar = (function () { properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); } entityID = Entities.addEntity(properties); - if (properties.type == "ParticleEffect") { + + if (properties.type === "ParticleEffect") { selectParticleEntity(entityID); } @@ -2229,10 +2230,9 @@ var particleExplorerTool = new ParticleExplorerTool(); var selectedParticleEntity = 0; var selectedParticleEntityID = null; - function selectParticleEntity(entityID) { var properties = Entities.getEntityProperties(entityID); - + selectedParticleEntityID = entityID; if (properties.emitOrientation) { properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); } @@ -2274,7 +2274,6 @@ entityListTool.webView.webEventReceived.connect(function (data) { return; } // Destroy the old particles web view first - selectParticleEntity(ids[0]); } else { selectedParticleEntity = 0; particleExplorerTool.destroyWebView(); From 7d18d2e01788e8c7d07cd204edad031f37493c55 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 28 Jul 2017 22:14:18 -0700 Subject: [PATCH 222/269] 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 223/269] 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 224/269] Revert "Disable touch event emulation for tablet until we fix crash" --- interface/src/ui/overlays/Web3DOverlay.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 32b16ff39b..a069b67d2b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -337,19 +337,11 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { } void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { - // FIXME touch event emulation is broken in some way. Do NOT enable this code - // unless you have done a debug build of the application and verified that - // you are not getting assertion errors on handling the touch events inside - // Qt. -#if 0 if (_inputMode == Touch) { handlePointerEventAsTouch(event); } else { handlePointerEventAsMouse(event); } -#else - handlePointerEventAsMouse(event); -#endif } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { From 861b33a84563185027a559473909b36b613a470f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 17:22:25 -0700 Subject: [PATCH 225/269] Fix potential nullptr access in EntityTreeElement --- libraries/entities/src/EntityTreeElement.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 108cb39222..487bf60f61 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -1001,7 +1001,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int if (!bestFitBefore && bestFitAfter) { // This is the case where the entity existed, and is in some element in our tree... if (currentContainingElement.get() != this) { - currentContainingElement->removeEntityItem(entityItem); + // if the currentContainingElement is non-null, remove the entity from it + if (currentContainingElement) { + currentContainingElement->removeEntityItem(entityItem); + } addEntityItem(entityItem); } } From bb7110d81b47957a6757b801ccf3af39c6f2fc2a Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 30 Jul 2017 19:22:54 +0200 Subject: [PATCH 226/269] Desktop Running scripts window now closed and as soon as Tablet opened, it will show Running Script. TODO: make Tablet opens automatically --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++++++++++++- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..9ed87ea337 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -208,12 +208,13 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _toolbarMode = toolbarMode; + auto offscreenUi = DependencyManager::get(); + if (toolbarMode) { removeButtonsFromHomeScreen(); addButtonsToToolbar(); // create new desktop window - auto offscreenUi = DependencyManager::get(); auto tabletRootWindow = new TabletRootWindow(); tabletRootWindow->initQml(QVariantMap()); auto quickItem = tabletRootWindow->asQuickItem(); @@ -230,16 +231,37 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { removeButtonsFromToolbar(); addButtonsToHomeScreen(); + //check if running scripts opened and save it for reopen in Tablet + if (offscreenUi->isVisible("RunningScripts")) { + offscreenUi->hide("RunningScripts"); + _showRunningScripts = true; + } // destroy desktop window if (_desktopWindow) { _desktopWindow->deleteLater(); _desktopWindow = nullptr; } } + loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); + + //connect to Tablet shown signal to open running scripts + if (_showRunningScripts) { + QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); + //qApp->toggleTabletUI(true); + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + } } + static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (buttonProxy == NULL){ diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index e61398585e..82b7ad52b6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -252,6 +252,7 @@ protected: enum class State { Uninitialized, Home, Web, Menu, QML }; State _state { State::Uninitialized }; bool _landscape { false }; + bool _showRunningScripts { false }; }; Q_DECLARE_METATYPE(TabletProxy*); From b5f5a037f007e91b22d27cb39507e347637e7bed Mon Sep 17 00:00:00 2001 From: humbletim Date: Sun, 30 Jul 2017 16:25:56 -0400 Subject: [PATCH 227/269] remove "grabbable: true" property from attachment Overlays --- .../dist/app-doppleganger-marketplace.js | 3 +-- .../doppleganger-attachments/doppleganger-attachments.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js index 6abd3a0fc5..c2cf2a2353 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -1075,7 +1075,7 @@ dopplegangerAttachments.attachmentsUpdated.connect(function(attachments) { // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = __webpack_require__(1), @@ -1196,7 +1196,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js index 5dee264fae..7526f56511 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger-attachments.js @@ -6,7 +6,7 @@ // var require = function(id) { return Script.require(id + '?'+new Date().getTime().toString(36)); } module.exports = DopplegangerAttachments; -DopplegangerAttachments.version = '0.0.1a'; +DopplegangerAttachments.version = '0.0.1b'; DopplegangerAttachments.WANT_DEBUG = false; var _modelHelper = require('./model-helper.js'), @@ -127,7 +127,6 @@ DopplegangerAttachments.prototype = { dynamic: false, shapeType: 'none', lifetime: 60, - grabbable: true, }, !this.manualJointSync && { parentID: parentID, parentJointIndex: jointIndex, From 5fab6c37a3daec1beb0302af43fff7ce2f46f05a Mon Sep 17 00:00:00 2001 From: Matti Lahtinen Date: Mon, 31 Jul 2017 08:56:04 +0300 Subject: [PATCH 228/269] Removed stray variable test --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 02cc35e645..300db5d50a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -67,7 +67,7 @@ gridTool.setVisible(false); var entityListTool = new EntityListTool(); -selectionManager.addEventListener(function (test) { +selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); From 1f6b92a5190966886712a5356ecef593215eb75b Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Mon, 31 Jul 2017 17:27:49 +0700 Subject: [PATCH 229/269] Vibrate only the controller that hits the key and decrease timeout so that the xylophone can be played faster --- .../marketplace/xylophone/xylophoneKey.js | 22 +++++++++++-------- .../marketplace/xylophone/xylophoneRezzer.js | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js index 5cff66e557..3d131252c8 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneKey.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneKey.js @@ -9,12 +9,12 @@ // (function() { - var TIMEOUT = 150; + var TIMEOUT = 50; // at 30 ms, the key's color sometimes fails to switch when hit var TEXTURE_GRAY = Script.resolvePath("xylotex_bar_gray.png"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var IS_DEBUG = false; var _this; - + function XylophoneKey() { _this = this; } @@ -34,24 +34,28 @@ collisionWithEntity: function(thisEntity, otherEntity, collision) { if (collision.type === 0) { - _this.hit(); + _this.hit(otherEntity); } }, - clickDownOnEntity: function() { - _this.hit(); + clickDownOnEntity: function(otherEntity) { + _this.hit(otherEntity); }, - hit: function() { + hit: function(otherEntity) { if (!_this.isWaiting) { _this.isWaiting = true; _this.homePosition = Entities.getEntityProperties(_this.entityID, ["position"]).position; _this.injector = Audio.playSound(_this.sound, {position: _this.homePosition, volume: 1}); _this.editEntityTextures(_this.entityID, "file5", TEXTURE_GRAY); + var HAPTIC_STRENGTH = 1; - var HAPTIC_DURATION = 20; - var HAPTIC_HAND = 2; // Both hands - Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, HAPTIC_HAND); + var HAPTIC_DURATION = 20; + var userData = JSON.parse(Entities.getEntityProperties(otherEntity, 'userData').userData); + if (userData.hasOwnProperty('hand')){ + Controller.triggerHapticPulse(HAPTIC_STRENGTH, HAPTIC_DURATION, userData.hand); + } + _this.timeout(); } }, diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js index e99341bb19..6adf8710a7 100644 --- a/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js +++ b/unpublishedScripts/marketplace/xylophone/xylophoneRezzer.js @@ -11,6 +11,7 @@ var SOUND_FILES = ["C4.wav", "D4.wav", "E4.wav", "F4.wav", "G4.wav", "A4.wav", "B4.wav", "C5.wav"]; var KEY_MODEL_URL = Script.resolvePath("xyloKey_2_a_e.fbx"); var KEY_SCRIPT_URL = Script.resolvePath("xylophoneKey.js"); +var MALLET_SCRIPT_URL = Script.resolvePath("xylophoneMallet.js"); var TEXTURE_BLACK = Script.resolvePath("xylotex_bar_black.png"); var MALLET_MODEL_URL = Script.resolvePath("Mallet3-2pc.fbx"); var MALLET_MODEL_COLLIDER_URL = Script.resolvePath("Mallet3-2bpc_phys.obj"); @@ -91,6 +92,7 @@ function rezMallets() { damping: 1, angularDamping: 1, shapeType: "compound", + script: MALLET_SCRIPT_URL, userData: JSON.stringify({ grabbableKey: { invertSolidWhileHeld: true From e30e0b1a5f43f92c6ef0cc7e6117da209a6f3812 Mon Sep 17 00:00:00 2001 From: vladest Date: Mon, 31 Jul 2017 16:45:21 +0200 Subject: [PATCH 230/269] Make sure Tablet will be shown with opened running scripts dialog. Fixed potential stack corruption --- .../resources/qml/hifi/tablet/TabletRoot.qml | 8 ++++- .../ui/src/ui/TabletScriptingInterface.cpp | 35 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index faa4013bce..c7df6ac64f 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -202,5 +202,11 @@ Item { width: 480 height: 706 - function setShown(value) {} + function setShown(value) { + if (value === true) { + HMD.openTablet() + } else { + HMD.closeTablet() + } + } } diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 9ed87ea337..74150a5a4b 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -245,20 +245,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); - - //connect to Tablet shown signal to open running scripts - if (_showRunningScripts) { - QMetaObject::invokeMethod(qApp, "toggleTabletUI", Q_ARG(bool, true)); - //qApp->toggleTabletUI(true); - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); - if (_tabletShown && _showRunningScripts) { - _showRunningScripts = false; - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); - } } @@ -384,9 +370,26 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { }); if (_initialScreen) { - pushOntoStack(_initialPath); + if (!_showRunningScripts) { + pushOntoStack(_initialPath); + } _initialScreen = false; } + + if (_showRunningScripts) { + _showRunningScripts = false; + //connect to Tablet shown signal to open running scripts + //we cant just call pushOnStack here since RunningScripts draws incorrectly + auto conn = std::make_shared(); + *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { + QObject::disconnect(*conn); //not needed anymore, disconnect + if (_tabletShown) { + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } + }); + //show Tablet. Make sure, setShown implemented in TabletRoot.qml + QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); + } } else { removeButtonsFromHomeScreen(); _state = State::Uninitialized; @@ -523,7 +526,7 @@ bool TabletProxy::pushOntoStack(const QVariant& path) { qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } - return root; + return (root != nullptr); } void TabletProxy::popFromStack() { From ed9ea17917d2f2d49630d8a8329dab97ccbe0730 Mon Sep 17 00:00:00 2001 From: Liv Erickson Date: Mon, 31 Jul 2017 08:02:18 -0700 Subject: [PATCH 231/269] remove semi-colon --- scripts/system/chat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/chat.js b/scripts/system/chat.js index 85de9fd0ac..fa997e20cc 100644 --- a/scripts/system/chat.js +++ b/scripts/system/chat.js @@ -654,7 +654,7 @@ var numLines = Math.ceil(width); height = speechBubbleLineHeight * numLines + fudge; width = SPEECH_BUBBLE_MAX_WIDTH; - }; + } dimensions = { x: width, From 60201548670ca5549691dbab2091b93e14e4042d Mon Sep 17 00:00:00 2001 From: Menithal Date: Mon, 31 Jul 2017 22:49:11 +0300 Subject: [PATCH 232/269] Removed some whitespaces to trigger new build --- scripts/system/edit.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 300db5d50a..b5e54e7a57 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -26,11 +26,8 @@ Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", "libraries/progressDialog.js", - "libraries/entitySelectionTool.js", - "libraries/ToolTip.js", - "libraries/entityCameraTool.js", "libraries/gridTool.js", "libraries/entityList.js", From 43c8079f0383c8f92ca44d1d21773d87a6c14221 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 13:55:43 -0700 Subject: [PATCH 233/269] temporary inspectionMode setting to turn ContextOverlays completely off/on --- .../src/ui/overlays/ContextOverlayInterface.cpp | 15 +++++++++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 18cb41fda5..0235a8d4f4 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -34,6 +34,11 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; + // initially, set _enabled to match the switch. Later we enable/disable via the getter/setters + // if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all + // the time after getting edge highlighting, etc... + _enabled = _settingSwitch.get(); + auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"), &TabletProxy::tabletShownChanged, this, [&]() { @@ -66,6 +71,16 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; +void ContextOverlayInterface::setEnabled(bool enabled) { + // only enable/disable if the setting in 'on'. If it is 'off', + // make sure _enabled is always false. + if (_settingSwitch.get()) { + _enabled = enabled; + } else { + _enabled = false; + } +} + bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 623eb85401..1ec3d3aefd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,7 +51,7 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } - void setEnabled(bool enabled) { _enabled = enabled; } + void setEnabled(bool enabled); bool getEnabled() { return _enabled; } public slots: @@ -73,6 +73,8 @@ private: bool contextOverlayFilterPassed(const EntityItemID& entityItemID); glm::vec3 drawOutlineOverlay(const EntityItemID& entityItemID); + + Setting::Handle _settingSwitch { "inspectionMode", false }; }; #endif // hifi_ContextOverlayInterface_h From 0b79809f547c9ec90c3b611a02972feabdaa3584 Mon Sep 17 00:00:00 2001 From: LaShonda Hopper <1p-cusack@1stplayable.com> Date: Mon, 31 Jul 2017 17:08:30 -0400 Subject: [PATCH 234/269] [WL21389] PR1 Feedback: Small change missed last commit (details below). Until they're properly implemented, default the hedrons and polygons to SHAPE_TYPE_ELLIPSOID within RenderableShapeEntity::computeShapeInfo. Changes committed: modified: libraries/entities-renderer/src/RenderableShapeEntityItem.cpp --- libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index c853335915..3f9497741f 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -132,7 +132,7 @@ void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) { case entity::Shape::Icosahedron: case entity::Shape::Cone: { //TODO WL21389: SHAPE_TYPE_SIMPLE_HULL and pointCollection (later) - //_collisionShapeType = SHAPE_TYPE_SIMPLE_HULL; + _collisionShapeType = SHAPE_TYPE_ELLIPSOID; } break; case entity::Shape::Torus: From b371c875c47bf53a24857064c2dc5c3907fdbbc6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 31 Jul 2017 14:46:26 -0700 Subject: [PATCH 235/269] undo tab mapping to bring up tablet, for now --- interface/resources/controllers/keyboardMouse.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index e3d2132e01..f377e02e5f 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -15,7 +15,7 @@ { "comment" : "Mouse turn need to be small continuous increments", "from": { "makeAxis" : [ [ "Keyboard.MouseMoveLeft" ], - [ "Keyboard.MouseMoveRight" ] + [ "Keyboard.MouseMoveRight" ] ] }, "when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ], @@ -31,8 +31,8 @@ { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", "from": { "makeAxis" : [ [ "Keyboard.TouchpadLeft" ], - [ "Keyboard.TouchpadRight" ] - ] + [ "Keyboard.TouchpadRight" ] + ] }, "when": [ "Application.InHMD", "Application.SnapTurn" ], "to": "Actions.StepYaw", @@ -131,6 +131,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.Tab", "to": "Actions.ContextMenu" } + { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } ] } From de97dfc9d74309f1ea34c5f7eb0e1e721dbcd26c Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 15:25:02 -0700 Subject: [PATCH 236/269] install the bundled vcredist_x64 instead of relying on DLLs being setup by cmake directly in the install folder --- cmake/templates/NSIS.template.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 5417220ef1..de63e164e6 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,9 +853,11 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" + ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - Delete "$INSTDIR\vcredist_x64.exe" + ;Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From 46966b11321254fd96e5b14d0a052f85b68e0857 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:19:34 -0700 Subject: [PATCH 237/269] switching from LZMA (default) to BZIP2 for NSIS compression --- cmake/macros/GenerateInstallers.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..0296ad9901 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,6 +22,7 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From ae31df426c32094f780abecbed1c09ec70e9f648 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 16:52:38 -0700 Subject: [PATCH 238/269] remove vcredist_x64 after installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index de63e164e6..2445933b52 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -857,7 +857,7 @@ Section "-Core installation" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" - ;Delete "$INSTDIR\vcredist_x64.exe" + Delete "$INSTDIR\vcredist_x64.exe" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. From 6855dcbacf253d630e15c1d82340434da1a4c9e0 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Mon, 31 Jul 2017 17:17:11 -0700 Subject: [PATCH 239/269] use bzip2 compression only for PR builds (so that we internally benefit from faster builds) --- cmake/macros/GenerateInstallers.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0296ad9901..aa681e27b4 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -22,7 +22,9 @@ macro(GENERATE_INSTALLERS) set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}") set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) - set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + if (PR_BUILD) + set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2") + endif () set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) if (WIN32) From bea91be521c8668333cf3778f04599c0c55af18a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 29 Jul 2017 12:27:12 -0700 Subject: [PATCH 240/269] Fix for potential crash in JS console --- interface/src/ui/JSConsole.cpp | 69 ++++++++++++++++-------------- interface/src/ui/JSConsole.h | 10 ++--- interface/src/ui/TestingDialog.cpp | 8 ++-- interface/src/ui/TestingDialog.h | 2 +- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index a62fb2270b..8c1ff87d13 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "JSConsole.h" + #include #include #include @@ -20,7 +22,6 @@ #include #include "Application.h" -#include "JSConsole.h" #include "ScriptHighlighting.h" const int NO_CURRENT_HISTORY_COMMAND = -1; @@ -60,14 +61,12 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } -JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : +JSConsole::JSConsole(QWidget* parent, const QSharedPointer& scriptEngine) : QWidget(parent), _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME), - _commandHistory(_readLines(_savedHistoryFilename)), - _ownScriptEngine(scriptEngine == NULL), - _scriptEngine(NULL) { + _commandHistory(_readLines(_savedHistoryFilename)) { _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); _ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap); @@ -90,36 +89,37 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : } JSConsole::~JSConsole() { - disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); + if (_scriptEngine) { + disconnect(_scriptEngine.data(), SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine.data(), SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + _scriptEngine.reset(); } delete _ui; } -void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { +void JSConsole::setScriptEngine(const QSharedPointer& scriptEngine) { if (_scriptEngine == scriptEngine && scriptEngine != NULL) { return; } if (_scriptEngine != NULL) { - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); - if (_ownScriptEngine) { - _scriptEngine->deleteLater(); - } + disconnect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + disconnect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + disconnect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + disconnect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); + _scriptEngine.reset(); } // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine - _ownScriptEngine = (scriptEngine == NULL); - _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(_consoleFileName, false) : scriptEngine; + if (scriptEngine.isNull()) { + _scriptEngine = QSharedPointer(DependencyManager::get()->loadScript(_consoleFileName, false), &QObject::deleteLater); + } else { + _scriptEngine = scriptEngine; + } - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); - connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); - connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError); + connect(_scriptEngine.data(), &ScriptEngine::printedMessage, this, &JSConsole::handlePrint); + connect(_scriptEngine.data(), &ScriptEngine::infoMessage, this, &JSConsole::handleInfo); + connect(_scriptEngine.data(), &ScriptEngine::warningMessage, this, &JSConsole::handleWarning); + connect(_scriptEngine.data(), &ScriptEngine::errorMessage, this, &JSConsole::handleError); } void JSConsole::executeCommand(const QString& command) { @@ -135,19 +135,22 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QFuture future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command); + QWeakPointer weakScriptEngine = _scriptEngine; + auto consoleFileName = _consoleFileName; + QFuture future = QtConcurrent::run([weakScriptEngine, consoleFileName, command]()->QScriptValue{ + QScriptValue result; + auto scriptEngine = weakScriptEngine.lock(); + if (scriptEngine) { + BLOCKING_INVOKE_METHOD(scriptEngine.data(), "evaluate", + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, command), + Q_ARG(const QString&, consoleFileName)); + } + return result; + }); _executeWatcher.setFuture(future); } -QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { - QScriptValue result; - BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate", - Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, command), - Q_ARG(const QString&, _consoleFileName)); - return result; -} - void JSConsole::commandFinished() { QScriptValue result = _executeWatcher.result(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 59280f65aa..5010b5b9ca 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "ui_console.h" #include "ScriptEngine.h" @@ -29,10 +30,10 @@ const int CONSOLE_HEIGHT = 200; class JSConsole : public QWidget { Q_OBJECT public: - JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL); + JSConsole(QWidget* parent, const QSharedPointer& scriptEngine = QSharedPointer()); ~JSConsole(); - void setScriptEngine(ScriptEngine* scriptEngine = NULL); + void setScriptEngine(const QSharedPointer& scriptEngine = QSharedPointer()); void clear(); public slots: @@ -58,17 +59,14 @@ private: void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); - QScriptValue executeCommandInWatcher(const QString& command); QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QString _savedHistoryFilename; QList _commandHistory; - // Keeps track if the script engine is created inside the JSConsole - bool _ownScriptEngine; QString _rootCommand; - ScriptEngine* _scriptEngine; + QSharedPointer _scriptEngine; static const QString _consoleFileName; }; diff --git a/interface/src/ui/TestingDialog.cpp b/interface/src/ui/TestingDialog.cpp index f55eb63a5b..bba14cd5d7 100644 --- a/interface/src/ui/TestingDialog.cpp +++ b/interface/src/ui/TestingDialog.cpp @@ -24,12 +24,12 @@ TestingDialog::TestingDialog(QWidget* parent) : _console->setFixedHeight(TESTING_CONSOLE_HEIGHT); auto _engines = DependencyManager::get(); - _engine = _engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath); + _engine.reset(_engines->loadScript(qApp->applicationDirPath() + testRunnerRelativePath)); _console->setScriptEngine(_engine); - connect(_engine, &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); + connect(_engine.data(), &ScriptEngine::finished, this, &TestingDialog::onTestingFinished); } void TestingDialog::onTestingFinished(const QString& scriptPath) { - _engine = nullptr; - _console->setScriptEngine(nullptr); + _engine.reset(); + _console->setScriptEngine(); } diff --git a/interface/src/ui/TestingDialog.h b/interface/src/ui/TestingDialog.h index b90b8f2e99..055e43eaf7 100644 --- a/interface/src/ui/TestingDialog.h +++ b/interface/src/ui/TestingDialog.h @@ -29,7 +29,7 @@ public: private: std::unique_ptr _console; - ScriptEngine* _engine; + QSharedPointer _engine; }; #endif From 9b28d546e037f5dc1a9b1a4cd729ea26f4deb73d Mon Sep 17 00:00:00 2001 From: "Scromie@turnmeupgames.com" Date: Tue, 1 Aug 2017 12:32:27 +0300 Subject: [PATCH 241/269] Test plan --- Test Plan 2.docx | Bin 0 -> 7305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Test Plan 2.docx diff --git a/Test Plan 2.docx b/Test Plan 2.docx new file mode 100644 index 0000000000000000000000000000000000000000..da60821b53e86ea9383e166da7f1d63dba588740 GIT binary patch literal 7305 zcmaKRWmp_ruhxxxyQt50eftRLb;_hGa=-*>-7q`i6obI~u09 zS^X?VV`Ygmjg1v#rM3{s0L((Ne{TIbVH89$@6 zt%i_E@gJCFYZ+(==A^6h_ℜHr^16?r6-Yt^lIBVxkg z?6mrQLCS5A9E?!v^!?sAG)eZA0aJ}};hV<-A)MLNe$2Nj@`yTYh|nwR8j^G<*MyNs+Qo`Ph}BrQwSCLIgr#MAiX8C*23}v&RqEK}auuF>G4lPv%LENVZ%q3bKtLMmx5wyru z)l1niSjXhnXVgPnca2@k9);t@EMb^rq63D+$JYj>?qbkh4nCu{yHZh*-JgfmHDvlG zoynJmAAF8v)z$XG=4bj`3$1lfUUV?G7uJ442=&&~?bW#-l2V^ylTm<>ak=(XuhwWS z4(zZ}`U(kso~$=%W4y$z=qN#k#rk=YRM~3=Jrq*_1DI>X8#jbF{O`BZ0jbaI$0`Y#)`_+ zj6mUzfKP!{Af>Oh_O7f=o>^)LZpZz2@DS zN+G({7EwIb*%C}A!6iS(HLktgp}0AJDrf2NvY|J`IIyrM z+z@Pn-cWTEQqZ)vEd}0lKebC*uEhu@su(IHHO{A{chSD;py|&~(06%jAZFqdpM>{A z7j+Xejtv>lOLP-#^-B56+?06jKIN!=xMjosWuse+p3+2E+r+-*G}U%$0_wNk{e5kc zhyTwnSpw%wX$2O^wUeLoI!6V1VJ$_#yJLd=(HuoV{ImiDR}@9ggV z2PgHYaU`*A8)6tw^dK$KPQ+3hB$R8-Kj_w*omQ~nBQ;E$0(XO}c3F~@Lu=vaS=}9l zQ*}pvlu^@UJJ}(unngT8>4ixnR(oaa`@GR%vcj@}XWHmOVB>gQ*9Z6%IAsyW_ntri z;O0N+i1{}iT|8~g{vhL#)&g{i7xzBs8d2gT(stpLP%4MYb;2z`L3cXCvnz=q8eI}Z zV>WYrla$0TqAOUALqU{x5!~07El)-U@w$AF7MBvBp)q&aR)BmOLz)Z-O^0fYiC~hm z;E&0G^C+>TCWjZO_u9K2HeWTRSSu%x?D>GxWNVy^i>{_z&9DmKwrDpatS$Opz{v6R z&nk)v7T-}BAtQ$RD%32tt#}B!%Mms?sfnf0_DKf8_RvJ9$m-NF^a8=;+!APIdWFk% z6pC0Ho|0M=`M2GrF>Go0&-n-x1x!%u1h^tziQDQAgsY>%n|iT>)NqH>ky?y^Nosob zs1~8TM3@v4qUuBvtq%HME@*%XOoY_22r3%4YI6|Tk#`udd=8XD330gZ1rAZMZrxbc z(1Z`JNifwMVZ|)U)YXFo5vd}?7*IvW#3-0OUaS-EVu0Dgm&BZ)6rqGoFiF8O+bpAp zrppN=6mP_B>`GT0Y#~N4s#POH&k<{`Ft)i;)ZBwi6P7_sWr58Y^0jI+)*n|yR zb!HSc=x}OZWze`@`be1Joe(n^>bd7>@Iw}xCsUEJ>JT7VZnWR2tqEbJ(o1cxt7H7? zG{@^x8*LZWZ1TM1AqrlIT0C&}Wv5wa-n|H*U ztUCg)3i)=?!kP7Kj^U|ixfu2DB+{wD1C7#7NC~nWC(7IMY@GTaWtGA($r}a6RJ3S3 zk7t;nl3Y9BjHp)G}iYOHr%{Cny)$7(D`2=nLds>JEN8B92h6jmPYz zfe$HppxOyaE>|>JGb!P^A40w2LKv;1;OSig-#C*h1DAxY^4FE`p{ST@~pjh6nLiG^Ore)5VWwJ(5{ z%{|JcJq@wi98=`AQXzNf`MVydcNL{xM>Y{d7Ie9BJz3a~`z*`J*em|V9`tNK;%S8n zzW_7etN>t2fH9Bi!jfSPe(r8l&y_7kz_)4eTle@A_CQyZ!Oo$BKg#|j;>|V_8~`9o z_+JI$@3L=dZ{lia26cI=4@Wt#oR)Y&0m?P*MtfEtz}%AipQ3EC%VIc)?B68IEv#%g zqELxpv&pam7<4={gAa2}RxcTxli+0%ZrHZpomA%Y7>d!wv%j&x4oI+{r#8}?8;FQb>TZJ#k_v(9PU)ABe*Wmx7ZH7C zY`T1@DBuHO4cBMy%p^ylrgW%c2b?q_u8LtKr+b^4d0bn3Z$&5@8$8P3-pCiXs}H(H zeHZUDQw6*#fg5i3J~Yhth>HEYFEHewe65@fP#(6Q=r zOr@*hK@pYMQ9idYRvg3Yy32~zT)Jyim=aJRr}y*9b~(d9WGJfJ_)IUHmacIbd4L(; z4-y%~om_(REPoCDNVvDJ+ax@GHWDUTmrNghA}Q{!Qm7x*&$+bKG%|ek^75tQYP?>D zn6tXMEWz;@WMpL5ACdx>a!pTy`uS7y`3?rEm2W=7Y}&A(gr7+4NlnI!Wt7O~w&Ohh znd(W(S)gm5R<+EUDeiEc`(X~!U8!V?dl_kviuuiU*)IB7X8Z9^@+Fhp3#xX5ui0EK zHwf{OWI%1L7cv7SZ@`f7X`l4Eu%DVrjF9qH35h60fteN-@GJPV;GTmOE4Kot%u=~~ z#D-;bT*SK17tud1ta+12*{Vdz3My0LAfFn9A0NK@4536JkB&^zzRz$SEHs_W%-}Gp z%1G;z!~Q-a)gn^;X33A{in2rMW$JL<9L$e^`?~n|L~Sg1m8;Nk6h^L19wnc9*Fs?y zqEXi#SN;J6f+W(*O0C4@J#~X4{(H9t(5q3ih>gTq@r{G5B|X@~7O%siS>{{~L+2J; z&B5xL$rOdK4!>FYbw#;1m5J>GGK2Y1RPkw#EOEK^^cH4a2fUdZ-!kM}k8ftxa=My& z)XhGmZLj%?`p{v0tvzz1_5Q3V;wb59qOAXt({G#BZGp~v))nyt_j30*_h@j1GLk7f zO{o5DT4R1J2}U|r`oyu~uCp)^nkJkkoMuBOb#Lz`O`?(P)5y}ET2c>-sph8g=5F=6 zW}8KD#%As2x3;kzgKHAtbzLkOTY%nA$&U^P61A5SEt_QfsJqn^-t%8)_GvR0DMyUc zr476A7RZ+*8Q$?x&S_PDv;4wQ>~eq`y$!e7Jl214`0!%4A{qE@pIMHUiRGL@YQQQQ z#K@qMyNt`*2i%n8O4+4nU9R_YG$P4?On$~X@M=gPTNY?!D9dEIohI&3ijGz$y=C3*Q8z z_iSV~tS??;yheNt$pN+*xD0>-cmkevqpu`TM3M90RA3k=QQ-d9#dreroY&2FPJT*vq6t>ko%`qnO@AiB8h%$kC5YK}Z-(M_ z%f;8capXTwx>t0=r87Ugi9+20o5z7Fk&B)4c^SHH-4;Bx0J)tT#Vax#es!%Z#PI#4eShzl*xu+iN&^M;E{SFrFeiiZ5{7t$g9#cDhXdVNQRl z`e{pQp>VRp1)g`EBe6)ymhRC(IbVz=`4EnH^6*5+OSTNqoMSLHqd1PchUEsCwdIDm z<&U(c@&`BCBBAp$mccf1$J7D8;AYr{2i1;MJfhH9Z@fZPs*>4N2~dK+_KV#?bZVs% zdhvX0&DRkG12J`z)A^Q`>)~+D`P7|zeHJG)Y;Y(2;bKeSkvnFnLGwu1~`2SnC#Nh?%8t#g{EXgKq|7k%stLekNTFvx(WXf3ai>K17DWBGg3Qs64I~Gj|IhIJEWb+gGKj zXJ|pH>#|-Ig0d&9Lb8a0L$!jRl7y(;nfQ`CIPI_uF+KsccgB&bRU+QZbu|t|;4qhX zRVEY|;3|p5DhhUNlUx|;Bqw;h7B(GnGVk=+qyG|>xRV&ejtRlAoZ1qSK^M<_9>)0* zf{B31i(PO!M^(62jbe2ZPBi60S;&`~u!De_>Vz4MP`f>tO{!2z=F%0kMUilg4NEj0 zpKL!(8sWD~5cqkaTy*D<6*hwmj$Nde`LX2A)9L{B-SFGG=Gw`zg*BN4=*qYjIo(+T z#NiSHosoOosLa{0(l#=tXU(mGhCSBKQYL5}w@9F6H)2x8+DW zh#YLlK6Ao~+ku~ec$ntvQspCErh4z@ttnGR)aAHwP)&XAnem*CeNtJ?CFVT~O`DQg z-C66`3OO=dtt@MZ1a5qdMms~>2ZVX&^LhCy9YKNX=zc53h}zA5tBSOZz;i4Ah|qxj zYVyt3uC)GC0lp-e$IPi7P2a!;um>2Cwt~irS+6paY<{E0 zMQ1w|Um9LOQ3K`zINjc0R;Ya}pZ+`V{++d#xYsnDK@KAE`nIC7>99{}7?_e3_`~VE zkD)I-K&YF{=!CEqG1&08{7xO9H=5DG;n9vMgIoxwM#jg;VpG}uey)@;tzZ(lY~on} zbLBWB3hzpt&qF~QyxSm)DIe4l*uvGl40Xt_q^W-vpRdl)@2uQvME^9-+^D`2I=^_H z@!THv0(b$uV6!KGygeB+DCR5PE+;-4n550;JiEauDz4OjUu#?sYeZUup^aE6U^E-5 zU_TYa9EW>A#UVCPI22HFF~!$xcF`?}@kP+Mm*@dNcprt@ zaA-b5XM`ihvY0efDt+h^n5*>kLcVxJvd(JikoE(#OD3aZE42i#gZ*UyrO74s#^tr< z1d-pWjUtC`2}ZT5CvJgE<4^ao;ChF{R;4vp7xq&Y-QZs6*@oY!y617<*u{R8^O8-e zh5zYjlb!A`TgPK_Jpsk#^k{j`i)v(!!e;9)Ko204G=>Ld{y40g``9PE`B;74G`ERV zzl4z*?B(Hbr&Qg2exT9p*TJJ--FejLa0IBf5Eqy5u2xvFHe6X_>q$m%gtzqBQp@UR zi*AC2fop^<>xh`f52dQ1SB&9loiU(SS1RS%HQzpv<(WIePgX0yl=%R8D>k*l-X&LO z>XtBGiz{uOS;UK`v6eo*mJTCQJo%M#GO%dkb=s-_MWs&D`?0s?hCVw**m2ugkFZa= zPLA}{3L82AU@i*$51EzZFI~sb$;{UIU%M{I)0wIyd23iUv@qof0m37JT$-X3kTN%I zf?Wn#cgCb(#PetJj5$klxIcohTGNaC3Y6a_%w>_EO!Lr@!4hfX(lQj%HBg@;bnYLW zJ!8O^uW}8X=ng^4+PAuo9Fsdkb^WN`HY>W=h^lgqdn$4`;c600{*KR!riDYCBWkOkm&0yf6)ii7s$O(e zOM9$r0zPT+nACt|Y?3;1CL84P(_6vwB)X5!i{_O5fX&p&!#AqyhSOlxr)Hnv$yFwy zIi%|V0H74+zqY2R007{By!3}>V#ftAdRfsB)^Hrc2L%*I0<%B(4N3$+HU5WcN9uK;CrcxlZ~5_*D=`8%zZ zd875+rcA}D;2k4-Qk>^sW^RRO77io2n6ckjNkHjd`o(o(O+ie8xQ=l33oB7cZ|&n?HPY5@QM z82DdriUR-u^d#(|E@n^{Lv>FFGv`-N+wjI@#V46I^hu_*SKa7^p`30&^-Jx}Co$Sr z&_#V5;$S>F%4ndBE|;kp9NXkMTltnKXn5SgOMiy`PYPLWH4S@!Z*KBJ(r8kP03Sg6d+rP)&+w0ZzK|d|qeH=y5}2 zAe+NbN4W0iw>6<-j;9{2Csj7^jC?i$q4Log;M_~9T-ICN$JbDTOpbIXKpA_h38tN0 z{=peQwM^L~dDL`it&7TdNjF%LGbb%^pJ|6~+cH5Wb^n#*Aae=jIVpQr5P{xT_kHq4 zVlqB-7;=&a1czz5So?k(c$@GvWGPRzh>2@1b3Vf`!fjbfss3Q}G_iryKne1$m5pal zUlQQqc#Iqf0JOqB`4`yYuKb8*svxoI-ujF_5 zuR8C)Zx4n26aFtz{&)PZ`puIX{EyMY|8@Jnwc+2vzb2!9KYKj#pWt5wpWorX67(r! z{bP^#f5QKd{Pp`vzY^ey(Ek{i@L%-)9r$ZxpZM{Q;S&9?Y*CSiM|hfq1V92j0s#OQ I;wQ-e1EA-uc>n+a literal 0 HcmV?d00001 From f1d7ef76e7f28cce1bee01f47af6229903327bd9 Mon Sep 17 00:00:00 2001 From: rabelaiis Date: Tue, 1 Aug 2017 23:41:16 +0700 Subject: [PATCH 242/269] Add the mallet js file --- .../marketplace/xylophone/xylophoneMallet.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 unpublishedScripts/marketplace/xylophone/xylophoneMallet.js diff --git a/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js new file mode 100644 index 0000000000..061d1303bb --- /dev/null +++ b/unpublishedScripts/marketplace/xylophone/xylophoneMallet.js @@ -0,0 +1,25 @@ +// +// xylophoneMallet.js +// +// Created by Johnathan Franck on 07/30/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +(function() { + function XylophoneMallet() { + } + + XylophoneMallet.prototype = { + startEquip: function(entityID, args) { + var LEFT_HAND = 0; + var RIGHT_HAND = 1; + var userData = JSON.parse(Entities.getEntityProperties(entityID, 'userData').userData); + userData.hand = args[0] === "left" ? LEFT_HAND : RIGHT_HAND; + Entities.editEntity(entityID, {userData: JSON.stringify(userData)}); + } + }; + + return new XylophoneMallet(); +}); From 3a9b8c02a6532f4c414897bd8d28ee5d271db482 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 1 Aug 2017 13:41:28 -0400 Subject: [PATCH 243/269] * disable Avatar "follow" / recentering behavior when using HMD + "View > Mirror" * restore access to "View > Mirror" from HMD mode --- interface/src/Application.cpp | 6 ++---- interface/src/avatar/MyAvatar.cpp | 3 ++- scripts/system/hmd.js | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 94d9d6e885..69359660af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4483,11 +4483,9 @@ void Application::cameraModeChanged() { void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { - if (isHMDMode()) { - menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - } else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { + if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); + getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers } } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 490f22ed8b..160235d77e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2849,7 +2849,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - if (myAvatar.getHMDLeanRecenterEnabled()) { + if (myAvatar.getHMDLeanRecenterEnabled() && + qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); } diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c545e6bcee..3598b461a4 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -40,9 +40,8 @@ function updateControllerDisplay() { var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -// Independent and Entity mode make people sick. Third Person and Mirror have traps that we need to work through. -// Disable them in hmd. -var desktopOnlyViews = ['Mirror', 'Independent Mode', 'Entity Mode']; +// Independent and Entity mode make people sick; disable them in hmd. +var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; function onHmdChanged(isHmd) { HMD.closeTablet(); From c22e08f3e8ac3e4a550f0d7f7e4316bd090109dc Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 19:58:23 +0100 Subject: [PATCH 244/269] loacked script engine in EntityEditPacketSender --- libraries/entities/src/EntityEditPacketSender.cpp | 1 + libraries/entities/src/EntityEditPacketSender.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 7845b0d5e3..ee0fcf8218 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -54,6 +54,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, EntityItemProperties entityProperties = entity->getProperties(); entityProperties.merge(properties); + std::lock_guard lock(_mutex); QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); QVariant variantProperties = scriptProperties.toVariant(); QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 9190a8296a..81efaa865c 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -14,6 +14,8 @@ #include +#include + #include "EntityItem.h" #include "AvatarData.h" @@ -49,6 +51,7 @@ private: EntityItemID entityItemID, const EntityItemProperties& properties); private: + std::mutex _mutex; AvatarData* _myAvatar { nullptr }; QScriptEngine _scriptEngine; }; From cca29e849b50a8b982144439b3ca46d2cb2f73d9 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 1 Aug 2017 23:18:42 +0100 Subject: [PATCH 245/269] fix issue of tablet begin blank when switching between HMD and Desktop mode --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 43c460fd23..e7b736d232 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -236,7 +236,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { _desktopWindow = nullptr; } } - loadHomeScreen(true); emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } From 762b4098143b4fa68cf10e16e232ca5229fe6a7f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:20:33 -0700 Subject: [PATCH 246/269] unused variable --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cbe184f31e..b0fc826f0a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -142,7 +142,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - bool intersectionExists = boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } From 588afaaa85861ea2f0ca70446cbc0d510d70c6a6 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 1 Aug 2017 16:59:42 -0700 Subject: [PATCH 247/269] unused variable... --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index b0fc826f0a..e406d139d0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -57,7 +57,6 @@ ContextOverlayInterface::ContextOverlayInterface() { }); } -static const xColor BB_OVERLAY_COLOR = {255, 255, 0}; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters From 1b6feb63c8d901d484dfac389fbf7bb4d1ff46b5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 1 Aug 2017 18:40:43 -0700 Subject: [PATCH 248/269] Fix for crash when attempting to teleport while avatar was loading The fix had two parts. * Make Avatar::getAbsoluteDefaultJointXXXInObjectFrame thread safe * Make teleport.js handle a zero foot offset more gracefully, to prevent the avatar from teleporting into the floor. --- .../src/avatars-renderer/Avatar.cpp | 33 ++++++++++++++----- scripts/system/controllers/teleport.js | 8 ++++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 06814c2707..d775fe05f0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -899,17 +899,34 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const { } glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const { - glm::quat rotation; - glm::quat rot = _skeletonModel->getRig().getAnimSkeleton()->getAbsoluteDefaultPose(index).rot(); - return Quaternions::Y_180 * rot; + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + auto skeleton = model->getRig().getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // The rotation part of the geometry-to-rig transform is always identity so we can skip it. + // Y_180 is to convert from rig-frame into avatar-frame + return Quaternions::Y_180 * skeleton->getAbsoluteDefaultPose(index).rot(); + } + } + return Quaternions::Y_180; } glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const { - glm::vec3 translation; - const Rig& rig = _skeletonModel->getRig(); - glm::vec3 trans = rig.getAnimSkeleton()->getAbsoluteDefaultPose(index).trans(); - glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - return transformPoint(y180Mat * rig.getGeometryToRigTransform(), trans); + // To make this thread safe, we hold onto the model by smart ptr, which prevents it from being deleted while we are accessing it. + auto model = getSkeletonModel(); + if (model) { + const Rig& rig = model->getRig(); + auto skeleton = rig.getAnimSkeleton(); + if (skeleton && index >= 0 && index < skeleton->getNumJoints()) { + // trans is in geometry frame. + glm::vec3 trans = skeleton->getAbsoluteDefaultPose(index).trans(); + // Y_180 is to convert from rig-frame into avatar-frame + glm::mat4 geomToAvatarMat = Matrices::Y_180 * rig.getGeometryToRigTransform(); + return transformPoint(geomToAvatarMat, trans); + } + } + return Vectors::ZERO; } glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index b058ec670f..15ba314a1d 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -381,7 +381,13 @@ function getAvatarFootOffset() { } if (footJointIndex != -1) { // default vertical offset from foot to avatar root. - return -MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex).y; + var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); + if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { + // if footPos is exactly zero, it's probably wrong because avatar is currently loading, fall back to default. + return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; + } else { + return -footPos.y; + } } else { return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; } From 4330e312122448354b9f892ec5e306812d54fbf4 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 16:32:09 +0200 Subject: [PATCH 249/269] Toolbar mode: 1. Fixed switching between different apps. 2. Fixed switching from Create --- interface/resources/qml/hifi/audio/Audio.qml | 2 +- libraries/ui/src/ui/TabletScriptingInterface.cpp | 13 ++++++------- scripts/system/edit.js | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 03d27e3831..93742e39a5 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -33,7 +33,7 @@ Rectangle { // only show the title if loaded through a "loader" function showTitle() { - return root.parent.objectName == "loader"; + return (root.parent !== null) && root.parent.objectName == "loader"; } Column { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 37a2bae0bf..2bdad096f4 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -237,7 +237,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } } loadHomeScreen(true); - emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL)); } static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) { @@ -463,14 +462,14 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } if (root) { - if (_state != State::QML) { - removeButtonsFromHomeScreen(); - QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); - _state = State::QML; + removeButtonsFromHomeScreen(); //works only in Tablet + QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); + _state = State::QML; + if (path != _currentPathLoaded) { emit screenChanged(QVariant("QML"), path); - _currentPathLoaded = path; - QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } + _currentPathLoaded = path; + QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); } else { qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null"; } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 06260277fd..046cd2e66a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -419,7 +419,7 @@ var toolBar = (function () { createButton = activeButton; tablet.screenChanged.connect(function (type, url) { if (isActive && (type !== "QML" || url !== "Edit.qml")) { - that.toggle(); + that.setActive(false) } }); tablet.fromQml.connect(fromQml); From 05f1caab01b8fa1a0e872c92898559a81c0148b4 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 2 Aug 2017 16:35:58 +0100 Subject: [PATCH 250/269] better solution to help and blank tablet issues --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index e7b736d232..603b85c160 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -228,7 +228,12 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); } else { removeButtonsFromToolbar(); - addButtonsToHomeScreen(); + + if (_currentPathLoaded == TABLET_SOURCE_URL) { + addButtonsToHomeScreen(); + } else { + loadHomeScreen(true); + } // destroy desktop window if (_desktopWindow) { From 7d361c69c2493e6198c19a5fe750be87f60d3e4d Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 2 Aug 2017 19:52:47 +0200 Subject: [PATCH 251/269] Audio mic bar show only rectangle on mouse over --- interface/resources/qml/hifi/audio/MicBar.qml | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 10e12551b7..e757d7cadf 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -27,7 +27,7 @@ Rectangle { color: "#00000000"; border { - width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0; + width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0; color: colors.border; } @@ -61,7 +61,7 @@ Rectangle { drag.target: dragTarget; } - Item { + QtObject { id: colors; readonly property string unmuted: "#FFF"; @@ -72,7 +72,7 @@ Rectangle { readonly property string red: colors.muted; readonly property string fill: "#55000000"; readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF"; - readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted; + readonly property string icon: Audio.muted ? muted : unmuted; } Item { @@ -92,10 +92,8 @@ Rectangle { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; - function exclusiveOr(a, b) { return (a || b) && !(a && b); } - id: image; - source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon; + source: Audio.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -118,9 +116,9 @@ Rectangle { Item { id: status; - readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted; + readonly property string color: Audio.muted ? colors.muted : colors.unmuted; - visible: Audio.muted || mouseArea.containsMouse; + visible: Audio.muted; anchors { left: parent.left; @@ -133,14 +131,14 @@ Rectangle { Text { anchors { - horizontalCenter: parent.horizontalCenter; + horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter; } color: parent.color; - text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE"; - font.pointSize: 12; + text: Audio.muted ? "MUTED" : "MUTE"; + font.pointSize: 12; } Rectangle { @@ -150,7 +148,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } @@ -161,7 +159,7 @@ Rectangle { } width: 50; - height: 4; + height: 4; color: parent.color; } } From 106b0ad8bb7e1118d6abd2f0f7aa747588fedc3e Mon Sep 17 00:00:00 2001 From: seefo Date: Mon, 31 Jul 2017 14:37:40 -0700 Subject: [PATCH 252/269] Added Vive pucks as an input channel --- interface/resources/controllers/standard.json | 19 ++++++++++++++++++- interface/resources/controllers/vive.json | 19 ++++++++++++++++++- .../controllers/src/controllers/Actions.cpp | 17 +++++++++++++++++ .../controllers/src/controllers/Actions.h | 17 +++++++++++++++++ .../src/controllers/StandardController.cpp | 17 +++++++++++++++++ scripts/developer/tests/puck-attach.js | 6 +++--- scripts/developer/tests/viveTrackedObjects.js | 2 +- 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 166f1a6869..0d5c095585 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -109,6 +109,23 @@ { "from": "Standard.Head", "to": "Actions.Head" }, { "from": "Standard.LeftArm", "to": "Actions.LeftArm" }, - { "from": "Standard.RightArm", "to": "Actions.RightArm" } + { "from": "Standard.RightArm", "to": "Actions.RightArm" }, + + { "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" }, + { "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" }, + { "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" }, + { "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" }, + { "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" }, + { "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" }, + { "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" }, + { "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" }, + { "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" }, + { "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" }, + { "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" }, + { "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" }, + { "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" }, + { "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" }, + { "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" }, + { "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" } ] } diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 73ab5cb2ae..02fc09c815 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -77,6 +77,23 @@ { "from": "Vive.Head", "to" : "Standard.Head"}, { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" } + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, + + { "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" }, + { "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" }, + { "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" }, + { "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" }, + { "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" }, + { "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" }, + { "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" }, + { "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" }, + { "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" }, + { "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" }, + { "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" }, + { "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" }, + { "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" }, + { "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" }, + { "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" }, + { "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" } ] } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index b3c0ed3f05..d8dd7f5e35 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -101,6 +101,23 @@ namespace controller { makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"), makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"), + makePosePair(Action::TRACKED_OBJECT_00, "TrackedObject00"), + makePosePair(Action::TRACKED_OBJECT_01, "TrackedObject01"), + makePosePair(Action::TRACKED_OBJECT_02, "TrackedObject02"), + makePosePair(Action::TRACKED_OBJECT_03, "TrackedObject03"), + makePosePair(Action::TRACKED_OBJECT_04, "TrackedObject04"), + makePosePair(Action::TRACKED_OBJECT_05, "TrackedObject05"), + makePosePair(Action::TRACKED_OBJECT_06, "TrackedObject06"), + makePosePair(Action::TRACKED_OBJECT_07, "TrackedObject07"), + makePosePair(Action::TRACKED_OBJECT_08, "TrackedObject08"), + makePosePair(Action::TRACKED_OBJECT_09, "TrackedObject09"), + makePosePair(Action::TRACKED_OBJECT_10, "TrackedObject10"), + makePosePair(Action::TRACKED_OBJECT_11, "TrackedObject11"), + makePosePair(Action::TRACKED_OBJECT_12, "TrackedObject12"), + makePosePair(Action::TRACKED_OBJECT_13, "TrackedObject13"), + makePosePair(Action::TRACKED_OBJECT_14, "TrackedObject14"), + makePosePair(Action::TRACKED_OBJECT_15, "TrackedObject15"), + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index ec4800c9aa..6319b5746e 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -146,6 +146,23 @@ enum class Action { RIGHT_HAND_PINKY3, RIGHT_HAND_PINKY4, + TRACKED_OBJECT_00, + TRACKED_OBJECT_01, + TRACKED_OBJECT_02, + TRACKED_OBJECT_03, + TRACKED_OBJECT_04, + TRACKED_OBJECT_05, + TRACKED_OBJECT_06, + TRACKED_OBJECT_07, + TRACKED_OBJECT_08, + TRACKED_OBJECT_09, + TRACKED_OBJECT_10, + TRACKED_OBJECT_11, + TRACKED_OBJECT_12, + TRACKED_OBJECT_13, + TRACKED_OBJECT_14, + TRACKED_OBJECT_15, + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 40b87bc6b2..ed729867df 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -166,6 +166,23 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(DD, "Down"), makePair(DL, "Left"), makePair(DR, "Right"), + + makePair(TRACKED_OBJECT_00, "TrackedObject00"), + makePair(TRACKED_OBJECT_01, "TrackedObject01"), + makePair(TRACKED_OBJECT_02, "TrackedObject02"), + makePair(TRACKED_OBJECT_03, "TrackedObject03"), + makePair(TRACKED_OBJECT_04, "TrackedObject04"), + makePair(TRACKED_OBJECT_05, "TrackedObject05"), + makePair(TRACKED_OBJECT_06, "TrackedObject06"), + makePair(TRACKED_OBJECT_07, "TrackedObject07"), + makePair(TRACKED_OBJECT_08, "TrackedObject08"), + makePair(TRACKED_OBJECT_09, "TrackedObject09"), + makePair(TRACKED_OBJECT_10, "TrackedObject10"), + makePair(TRACKED_OBJECT_11, "TrackedObject11"), + makePair(TRACKED_OBJECT_12, "TrackedObject12"), + makePair(TRACKED_OBJECT_13, "TrackedObject13"), + makePair(TRACKED_OBJECT_14, "TrackedObject14"), + makePair(TRACKED_OBJECT_15, "TrackedObject15") }; return availableInputs; } diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 2d0a2e6d02..dd94f81a99 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -84,7 +84,7 @@ function getAvailableTrackedObjects() { var i; for (i = 0; i < NUM_TRACKED_OBJECTS; i++) { var key = indexToTrackedObjectName(i); - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose && pose.valid) { available.push(i); } @@ -126,8 +126,8 @@ function pad(num, size) { } function update() { - if (attachedEntity && attachedObj && Controller.Hardware.Vive) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[attachedObj.key]); + if (attachedEntity && attachedObj && Controller.Standard) { + var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); diff --git a/scripts/developer/tests/viveTrackedObjects.js b/scripts/developer/tests/viveTrackedObjects.js index 4155afb82b..1d60f658d9 100644 --- a/scripts/developer/tests/viveTrackedObjects.js +++ b/scripts/developer/tests/viveTrackedObjects.js @@ -23,7 +23,7 @@ var BLUE = {x: 0, y: 0, z: 1, w: 1}; function update(dt) { if (Controller.Hardware.Vive) { TRACKED_OBJECT_POSES.forEach(function (key) { - var pose = Controller.getPoseValue(Controller.Hardware.Vive[key]); + var pose = Controller.getPoseValue(Controller.Standard[key]); if (pose.valid) { DebugDraw.addMyAvatarMarker(key, pose.rotation, pose.translation, BLUE); } else { From 620f1a65c0f4cfe1519e008749a9a18c4c6d32c0 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 12:29:30 -0700 Subject: [PATCH 253/269] New puck-attach script --- scripts/developer/tests/puck-attach.js | 205 +++++++++++++++---------- 1 file changed, 121 insertions(+), 84 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index dd94f81a99..25aeee54eb 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -1,21 +1,13 @@ // // Created by Anthony J. Thibault on 2017/06/20 +// Modified by Robbie Uvanni to support multiple pucks and easier placement of pucks on entities, on 2017/08/01 // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // When this script is running, a new app button, named "PUCKTACH", will be added to the toolbar/tablet. -// Click this app to bring up the puck attachment panel. This panel contains the following fields. -// -// * Tracked Object - A drop down list of all the available pucks found. If no pucks are found this list will only have a single NONE entry. -// Closing and re-opening the app will refresh this list. -// * Model URL - A model url of the model you wish to be attached to the specified puck. -// * Position X, Y, Z - used to apply an offset between the puck and the attached model. -// * Rot X, Y, Z - used to apply euler angle offsets, in degrees, between the puck and the attached model. -// * Create Attachment - when this button is pressed a new Entity will be created at the specified puck's location. -// If a puck atttachment already exists, it will be destroyed before the new entity is created. -// * Destroy Attachmetn - destroies the entity attached to the puck. +// Click this app to bring up the puck attachment panel. // /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -25,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var HTML_URL = "https://s3.amazonaws.com/hifi-public/tony/html/puck-attach.html"; +var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -34,20 +26,9 @@ var tabletButton = tablet.addButton({ activeIcon: "https://s3.amazonaws.com/hifi-public/tony/icons/puck-a.svg" }); -tabletButton.clicked.connect(function () { - if (shown) { - tablet.gotoHomeScreen(); - } else { - tablet.gotoWebScreen(HTML_URL); - } -}); - var shown = false; -var attachedEntity; -var attachedObj; - function onScreenChanged(type, url) { - if (type === "Web" && url === HTML_URL) { + if (type === "Web" && url === TABLET_APP_URL) { tabletButton.editProperties({isActive: true}); if (!shown) { // hook up to event bridge @@ -57,9 +38,7 @@ function onScreenChanged(type, url) { // send available tracked objects to the html running in the tablet. var availableTrackedObjects = getAvailableTrackedObjects(); tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - - // print("PUCK-ATTACH: availableTrackedObjects = " + JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. + }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -71,13 +50,15 @@ function onScreenChanged(type, url) { shown = false; } } - tablet.screenChanged.connect(onScreenChanged); +function pad(num, size) { + var tempString = "000000000" + num; + return tempString.substr(tempString.length - size); +} function indexToTrackedObjectName(index) { return "TrackedObject" + pad(index, 2); } - function getAvailableTrackedObjects() { var available = []; var NUM_TRACKED_OBJECTS = 16; @@ -92,83 +73,139 @@ function getAvailableTrackedObjects() { return available; } -function attach(obj) { - attachedEntity = Entities.addEntity({ - type: "Model", - name: "puck-attach-entity", - modelURL: obj.modelurl - }); - attachedObj = obj; - var localPos = {x: Number(obj.posx), y: Number(obj.posy), z: Number(obj.posz)}; - var localRot = Quat.fromVec3Degrees({x: Number(obj.rotx), y: Number(obj.roty), z: Number(obj.rotz)}); - attachedObj.localXform = new Xform(localRot, localPos); - var key = indexToTrackedObjectName(Number(attachedObj.puckno)); - attachedObj.key = key; - - // print("PUCK-ATTACH: attachedObj = " + JSON.stringify(attachedObj)); - - Script.update.connect(update); - update(); +function getRelativePosition(origin, rotation, offset) { + var relativeOffset = Vec3.multiplyQbyV(rotation, offset); + var worldPosition = Vec3.sum(origin, relativeOffset); + return worldPosition; +} +function getPropertyForEntity(entityID, propertyName) { + return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } -function remove() { - if (attachedEntity) { - Script.update.disconnect(update); - Entities.deleteEntity(attachedEntity); - attachedEntity = undefined; - } - attachedObj = undefined; -} +var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_NAME = "Tracked Puck"; -function pad(num, size) { - var tempString = "000000000" + num; - return tempString.substr(tempString.length - size); -} +var trackedPucks = { }; +var lastPuck = { }; -function update() { - if (attachedEntity && attachedObj && Controller.Standard) { - var pose = Controller.getPoseValue(Controller.Standard[attachedObj.key]); - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, attachedObj.localXform)); - if (pose && pose.valid) { - Entities.editEntity(attachedEntity, { - position: finalXform.pos, - rotation: finalXform.rot - }); - } else { - if (pose) { - print("PUCK-ATTACH: WARNING: invalid pose for " + attachedObj.key); - } else { - print("PUCK-ATTACH: WARNING: could not find key " + attachedObj.key); +function createPuck(puck) { + // create a puck entity and add it to our list of pucks + var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + "name": "Tracked Puck", + "type": "Model", + "modelURL": VIVE_PUCK_MODEL, + "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, + "position": spawnPosition, + "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + trackedPucks[puck.puckno] = { + "puckEntityID": puckEntityID, + "trackedEntityID": "" + }; + lastPuck = trackedPucks[puck.puckno]; +} +function finalizePuck() { + // find nearest entity and change its parent to the puck + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); + var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); + + var foundEntity; + var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + + for (var i = 0; i < foundEntities.length; i++) { + var entity = foundEntities[i]; + + if (getPropertyForEntity(entity, "name") !== VIVE_PUCK_NAME) { + var entityPosition = getPropertyForEntity(entity, "position"); + var d = Vec3.distance(entityPosition, puckPosition); + + if (d < leastDistance) { + leastDistance = d; + foundEntity = entity; } } } + + if (foundEntity) { + lastPuck.trackedEntityID = foundEntity; + Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + } +} +function updatePucks() { + // for each puck, update its position and orientation + for (var puck in trackedPucks) { + var action = indexToTrackedObjectName(puck); + var pose = Controller.getPoseValue(Controller.Standard[action]); + if (pose && pose.valid) { + if (trackedPucks[puck].trackedEntityID) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + + Entities.editEntity(trackedPucks[puck].puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + } + } + } +} +function destroyPuck(puckName) { + // unparent entity and delete its parent + var puckEntityID = trackedPucks[puckName].puckEntityID; + var trackedEntityID = trackedPucks[puckName].trackedEntityID; + + Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); + Entities.deleteEntity(puckEntityID); +} +function destroyPucks() { + // remove all pucks and unparent entities + for (var puck in trackedPucks) { + destroyPuck(puck); + } } function onWebEventReceived(msg) { var obj = {}; - try { + + try { obj = JSON.parse(msg); - } catch (err) { - return; + } catch (err) { + return; } - if (obj.cmd === "attach") { - remove(); - attach(obj); - } else if (obj.cmd === "detach") { - remove(); + + switch (obj.cmd) { + case "create": + createPuck(obj); + break; + case "finalize": + finalizePuck(); + break; } } +Script.update.connect(updatePucks); Script.scriptEnding.connect(function () { - remove(); tablet.removeButton(tabletButton); + destroyPucks(); if (shown) { tablet.webEventReceived.disconnect(onWebEventReceived); tablet.gotoHomeScreen(); } tablet.screenChanged.disconnect(onScreenChanged); }); - -}()); // END LOCAL_SCOPE +tabletButton.clicked.connect(function () { + if (shown) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(TABLET_APP_URL); + } +}); +}()); // END LOCAL_SCOPE \ No newline at end of file From 961f748d8761332ba2939937b70cd6cac91d500b Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 13:08:24 -0700 Subject: [PATCH 254/269] Fixed a potential bug with puck-attach parenting --- scripts/developer/tests/puck-attach.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 25aeee54eb..3a5d8736f8 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -140,18 +140,25 @@ function finalizePuck() { } function updatePucks() { // for each puck, update its position and orientation - for (var puck in trackedPucks) { - var action = indexToTrackedObjectName(puck); + for (var puckName in trackedPucks) { + var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { - if (trackedPucks[puck].trackedEntityID) { + var puck = trackedPucks[puckName]; + if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); - Entities.editEntity(trackedPucks[puck].puckEntityID, { + Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, - rotation: finalXform.rot + rotation: finalXform.rot, + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID }); } } From 5a7a6bab7049b899b9b011fbb1bfda7f0a23639a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 2 Aug 2017 13:57:27 -0700 Subject: [PATCH 255/269] Remove PDB files, eliminate sixense --- cmake/macros/GenerateInstallers.cmake | 18 +++----- cmake/macros/InstallBesideConsole.cmake | 5 ++- cmake/templates/FixupBundlePostBuild.cmake.in | 43 ++++++++----------- interface/CMakeLists.txt | 5 ++- plugins/hifiSixense/CMakeLists.txt | 14 +++--- 5 files changed, 40 insertions(+), 45 deletions(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..75190d044a 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -24,21 +24,12 @@ macro(GENERATE_INSTALLERS) set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME}) set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME}) + if (WIN32) - # include CMake module that will install compiler system libraries - # so that we have msvcr120 and msvcp120 installed with targets - set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR}) - - # as long as we're including sixense plugin with installer - # we need re-distributables for VS 2011 as well - # this should be removed if/when sixense support is pulled - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll" - "${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll" - ) - + # Do not install the Visual Studio C runtime libraries. The installer will do this automatically + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include(InstallRequiredSystemLibraries) - set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico") # install and reference the Add/Remove icon @@ -90,3 +81,4 @@ macro(GENERATE_INSTALLERS) include(CPack) endmacro() + diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index d5777fff12..3c991acf86 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -22,9 +22,12 @@ macro(install_beside_console) else () # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${COMPONENT_INSTALL_DIR} COMPONENT ${SERVER_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) # on windows for PR and production builds, sign the executable diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 57d1fd787f..d4726884c2 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -11,34 +11,28 @@ include(BundleUtilities) -# replace copy_resolved_item_into_bundle -# -# The official version of copy_resolved_item_into_bundle will print out a "warning:" when -# the resolved item matches the resolved embedded item. This not not really an issue that -# should rise to the level of a "warning" so we replace this message with a "status:" -# -function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) - if (WIN32) - # ignore case on Windows - string(TOLOWER "${resolved_item}" resolved_item_compare) - string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare) - else() - set(resolved_item_compare "${resolved_item}") - set(resolved_embedded_item_compare "${resolved_embedded_item}") +function(gp_resolved_file_type_override resolved_file type_var) + if( file MATCHES ".*VCRUNTIME140.*" ) + set(type "system" PARENT_SCOPE) endif() - - if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") - # this is our only change from the original version - message(STATUS "status: resolved_item == resolved_embedded_item - not copying...") - else() - #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") - if(UNIX AND NOT APPLE) - file(RPATH_REMOVE FILE "${resolved_embedded_item}") - endif() + if( file MATCHES ".*concrt140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*msvcp140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*vcruntime140.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-crt-conio.*" ) + set(type "system" PARENT_SCOPE) + endif() + if( file MATCHES ".*api-ms-win-core-winrt.*" ) + set(type "system" PARENT_SCOPE) endif() endfunction() + message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}") @@ -52,3 +46,4 @@ endif() file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@") + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c97975e50a..81c8a44baf 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -309,9 +309,12 @@ else (APPLE) # setup install of executable and things copied by fixup/windeployqt install( - FILES "$/" + DIRECTORY "$/" DESTINATION ${INTERFACE_INSTALL_DIR} COMPONENT ${CLIENT_COMPONENT} + PATTERN "*.pdb" EXCLUDE + PATTERN "*.lib" EXCLUDE + PATTERN "*.exp" EXCLUDE ) set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}") diff --git a/plugins/hifiSixense/CMakeLists.txt b/plugins/hifiSixense/CMakeLists.txt index 14676217db..58aeb6c88f 100644 --- a/plugins/hifiSixense/CMakeLists.txt +++ b/plugins/hifiSixense/CMakeLists.txt @@ -6,9 +6,11 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -if (NOT ANDROID) - set(TARGET_NAME hifiSixense) - setup_hifi_plugin(Script Qml Widgets) - link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) - target_sixense() -endif () +# FIXME if we want to re-enable this, we need to fix the mechanism for installing the +# dependency dlls `msvcr100` and `msvcp100` WITHOUT using CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS +#if (NOT ANDROID) +# set(TARGET_NAME hifiSixense) +# setup_hifi_plugin(Script Qml Widgets) +# link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins) +# target_sixense() +#endif () From 82654c7cda887b19bc673ecb51f9c07bcb8652e1 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 2 Aug 2017 15:28:26 -0700 Subject: [PATCH 256/269] Made requested changed to puck-attach.js --- scripts/developer/tests/puck-attach.js | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 3a5d8736f8..65adf35d00 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -83,7 +83,9 @@ function getPropertyForEntity(entityID, propertyName) { } var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; +var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; @@ -91,23 +93,28 @@ var lastPuck = { }; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, 1.0); + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); // should be an overlay var puckEntityProperties = { - "name": "Tracked Puck", - "type": "Model", - "modelURL": VIVE_PUCK_MODEL, - "dimensions": { x: 0.0945, y: 0.0921, z: 0.0423 }, - "position": spawnPosition, - "userData": "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }" + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' }; var puckEntityID = Entities.addEntity(puckEntityProperties); + + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + trackedPucks[puck.puckno] = { - "puckEntityID": puckEntityID, - "trackedEntityID": "" + puckEntityID: puckEntityID, + trackedEntityID: "" }; lastPuck = trackedPucks[puck.puckno]; } @@ -117,7 +124,7 @@ function finalizePuck() { var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); var foundEntity; - var leastDistance = 999999; // this should be something like Integer.MAX_VALUE + var leastDistance = Number.MAX_VALUE; for (var i = 0; i < foundEntities.length; i++) { var entity = foundEntities[i]; @@ -141,6 +148,9 @@ function finalizePuck() { function updatePucks() { // for each puck, update its position and orientation for (var puckName in trackedPucks) { + if (!trackedPucks.hasOwnProperty(puckName)) { + continue; + } var action = indexToTrackedObjectName(puckName); var pose = Controller.getPoseValue(Controller.Standard[action]); if (pose && pose.valid) { @@ -148,7 +158,7 @@ function updatePucks() { if (puck.trackedEntityID) { var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, Xform.mul(puckXform, Vec3.ZERO)); + var finalXform = Xform.mul(avatarXform, puckXform); Entities.editEntity(puck.puckEntityID, { position: finalXform.pos, @@ -169,13 +179,16 @@ function destroyPuck(puckName) { var puckEntityID = trackedPucks[puckName].puckEntityID; var trackedEntityID = trackedPucks[puckName].trackedEntityID; + // remove the puck as a parent entity and then delete the puck Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); Entities.deleteEntity(puckEntityID); } function destroyPucks() { // remove all pucks and unparent entities - for (var puck in trackedPucks) { - destroyPuck(puck); + for (var puckName in trackedPucks) { + if (trackedPucks.hasOwnProperty(puckName)) { + destroyPuck(puckName); + } } } From 3c1607715ecaec1952d9aadfdfcf7d562b79f5f2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 2 Aug 2017 17:42:56 -0700 Subject: [PATCH 257/269] Updated WASAPI plugin --- cmake/externals/wasapi/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 1bf195fc84..4437024962 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi8.zip - URL_MD5 b01510437ea15527156bc25cdf733bd9 + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip + URL_MD5 94f4765bdbcd53cd099f349ae031e769 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 24718e242405f422e2fcfc1a64a55be1725611c6 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 09:03:23 -0400 Subject: [PATCH 258/269] WL21423 Camera mode on startup is determined by user settings and HMD status --- interface/src/Application.cpp | 44 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ceded99f40..8debcbd538 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4146,6 +4146,7 @@ void Application::loadSettings() { //DependencyManager::get()->setAutomaticLODAdjust(false); Menu::getInstance()->loadSettings(); + // If there is a preferred plugin, we probably messed it up with the menu settings, so fix it. auto pluginManager = PluginManager::getInstance(); auto plugins = pluginManager->getPreferredDisplayPlugins(); @@ -4159,24 +4160,44 @@ void Application::loadSettings() { break; } } - } else { + } + + Setting::Handle firstRun { Settings::firstRun, true }; + bool isFirstPerson = false; + if (firstRun.get()) { // If this is our first run, and no preferred devices were set, default to // an HMD device if available. - Setting::Handle firstRun { Settings::firstRun, true }; - if (firstRun.get()) { - auto displayPlugins = pluginManager->getDisplayPlugins(); - for (auto& plugin : displayPlugins) { - if (plugin->isHmd()) { - if (auto action = menu->getActionForOption(plugin->getName())) { - action->setChecked(true); - action->trigger(); - break; - } + auto displayPlugins = pluginManager->getDisplayPlugins(); + for (auto& plugin : displayPlugins) { + if (plugin->isHmd()) { + if (auto action = menu->getActionForOption(plugin->getName())) { + action->setChecked(true); + action->trigger(); + break; } } } + + isFirstPerson = (qApp->isHMDMode()); + } else { + // if this is not the first run, the camera will be initialized differently depending on user settings + + if (qApp->isHMDMode()) { + // if the HMD is active, use first-person camera, unless the appropriate setting is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); + } else { + // if HMD is not active, only use first person if the menu option is checked + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + } } + // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings + // dictated that we should be in first person + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + cameraMenuChanged(); + auto inputs = pluginManager->getInputPlugins(); for (auto plugin : inputs) { if (!plugin->isActive()) { @@ -4225,7 +4246,6 @@ void Application::init() { DependencyManager::get()->init(); DependencyManager::get()->init(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _timerStart.start(); _lastTimeUpdated.start(); From 4b7779f0f55381a1ea10f1b41eb6688ec7ecab69 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 3 Aug 2017 07:57:21 -0700 Subject: [PATCH 259/269] add flag to outline render flags, fix pal.js/edit.js interaction --- interface/src/Application.cpp | 11 ++++++++--- .../src/RenderableModelEntityItem.cpp | 5 ++++- libraries/render/src/render/Args.h | 3 ++- scripts/system/pal.js | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 571f206cb5..d50348d9b7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1489,7 +1489,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["atp_mapping_requests"] = atpMappingRequests; properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; - + QJsonObject bytesDownloaded; bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt(); bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt(); @@ -5420,8 +5420,13 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE; - if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { - renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + auto contextOverlayInterface = DependencyManager::get(); + if (contextOverlayInterface->getEnabled()) { + if (DependencyManager::get()->getIsInMarketplaceInspectionMode()) { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE; + } else { + renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES; + } } renderArgs->_outlineFlags = renderOutlineFlags; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a03360a0e5..911fdf4184 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -372,7 +372,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->updateRenderItems(); } - bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_WIREFRAMES) && getMarketplaceID().length() != 0) || getShouldHighlight(); + // this simple logic should say we set showingEntityHighlight to true whenever we are in marketplace mode and we have a marketplace id, or + // whenever we are not set to none and shouldHighlight is true. + bool showingEntityHighlight = ((bool)(args->_outlineFlags & (int)RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE) && getMarketplaceID().length() != 0) || + (args->_outlineFlags != RenderArgs::RENDER_OUTLINE_NONE && getShouldHighlight()); if (showingEntityHighlight) { static glm::vec4 yellowColor(1.0f, 1.0f, 0.0f, 1.0f); gpu::Batch& batch = *args->_batch; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index f53454cf75..d5b5440c32 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -66,7 +66,8 @@ namespace render { enum OutlineFlags { RENDER_OUTLINE_NONE = 0, RENDER_OUTLINE_WIREFRAMES = 1, - RENDER_OUTLINE_SHADER = 2 + RENDER_OUTLINE_MARKETPLACE_MODE = 2, + RENDER_OUTLINE_SHADER = 4 }; enum DebugFlags { RENDER_DEBUG_NONE = 0, diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 58a54ef859..2a8a89ae7d 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -710,6 +710,7 @@ function off() { Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); tablet.tabletShownChanged.disconnect(tabletVisibilityChanged); isWired = false; + ContextOverlay.enabled = true } if (audioTimer) { Script.clearInterval(audioTimer); @@ -718,7 +719,6 @@ function off() { triggerPressMapping.disable(); // see above removeOverlays(); Users.requestsDomainListData = false; - ContextOverlay.enabled = true; } function tabletVisibilityChanged() { From 6ea4ed80cd7a5ff0bd49aacd8ddb0790aed97e02 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 3 Aug 2017 09:19:08 -0700 Subject: [PATCH 260/269] Suppress initial PacketType::SilentAudioFrame on audio gate startup --- assignment-client/src/Agent.h | 2 +- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index a549df5fb3..da60a07367 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -109,7 +109,7 @@ private: QHash _outgoingScriptAudioSequenceNumbers; AudioGate _audioGate; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; bool _isNoiseGateEnabled { false }; CodecPluginPointer _codec; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 31e36671c7..1c26b1bb08 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -364,7 +364,7 @@ private: AudioIOStats _stats; AudioGate* _audioGate { nullptr }; - bool _audioGateOpen { false }; + bool _audioGateOpen { true }; AudioPositionGetter _positionGetter; AudioOrientationGetter _orientationGetter; From 2279e174e6dd9b16564e7035a85c900576a9d6bf Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Thu, 3 Aug 2017 09:41:41 -0700 Subject: [PATCH 261/269] Switching to completely silent VC++ redistributable installation --- cmake/templates/NSIS.template.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 2445933b52..5af51ff8c9 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -853,7 +853,7 @@ Section "-Core installation" ; Rename the incorrectly cased Raleway font Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" - ExecWait "$INSTDIR\vcredist_x64.exe /install /passive /norestart" + ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" From a439cc18372164e81290faeb325061ba7ab69afd Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 3 Aug 2017 20:58:44 +0200 Subject: [PATCH 262/269] Make connection to tablet shown signal more consistent, eliminates missed signals --- .../ui/src/ui/TabletScriptingInterface.cpp | 24 ++++++++----------- .../ui/src/ui/TabletScriptingInterface.h | 1 + 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 007754ed78..0fd32b42e6 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -187,6 +187,7 @@ TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent) if (QThread::currentThread() != qApp->thread()) { qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name; } + connect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } TabletProxy::~TabletProxy() { @@ -194,6 +195,7 @@ TabletProxy::~TabletProxy() { if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name; } + disconnect(this, &TabletProxy::tabletShownChanged, this, &TabletProxy::onTabletShown); } void TabletProxy::setToolbarMode(bool toolbarMode) { @@ -235,8 +237,7 @@ void TabletProxy::setToolbarMode(bool toolbarMode) { } else { loadHomeScreen(true); } - //check if running scripts opened and save it for reopen in Tablet - qDebug() << __FUNCTION__ << offscreenUi->isVisible("RunningScripts"); + //check if running scripts window opened and save it for reopen in Tablet if (offscreenUi->isVisible("RunningScripts")) { offscreenUi->hide("RunningScripts"); _showRunningScripts = true; @@ -322,6 +323,13 @@ void TabletProxy::emitWebEvent(const QVariant& msg) { emit webEventReceived(msg); } +void TabletProxy::onTabletShown() { + if (_tabletShown && _showRunningScripts) { + _showRunningScripts = false; + pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); + } +} + bool TabletProxy::isPathLoaded(const QVariant& path) { if (QThread::currentThread() != thread()) { bool result = false; @@ -377,18 +385,7 @@ void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) { _initialScreen = false; } - qDebug() << __FUNCTION__ << _showRunningScripts; if (_showRunningScripts) { - _showRunningScripts = false; - //connect to Tablet shown signal to open running scripts - //we cant just call pushOnStack here since RunningScripts draws incorrectly - auto conn = std::make_shared(); - *conn = connect(this, &TabletProxy::tabletShownChanged, this, [this, conn] { - QObject::disconnect(*conn); //not needed anymore, disconnect - if (_tabletShown) { - pushOntoStack("../../hifi/dialogs/TabletRunningScripts.qml"); - } - }); //show Tablet. Make sure, setShown implemented in TabletRoot.qml QMetaObject::invokeMethod(_qmlTabletRoot, "setShown", Q_ARG(const QVariant&, QVariant(true))); } @@ -491,7 +488,6 @@ void TabletProxy::loadQMLSource(const QVariant& path) { if (root) { removeButtonsFromHomeScreen(); //works only in Tablet - qDebug() << __FUNCTION__ << path << _currentPathLoaded << (int)_state; QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, path)); _state = State::QML; if (path != _currentPathLoaded) { diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 82b7ad52b6..af38cb9351 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -232,6 +232,7 @@ protected slots: void addButtonsToHomeScreen(); void desktopWindowClosed(); void emitWebEvent(const QVariant& msg); + void onTabletShown(); protected: void removeButtonsFromHomeScreen(); void loadHomeScreen(bool forceOntoHomeScreen); From dff4e6e32c2d6af0834139cc26150ffb715240c8 Mon Sep 17 00:00:00 2001 From: Armads Date: Thu, 3 Aug 2017 15:40:38 -0400 Subject: [PATCH 263/269] WL21423 Changing order of camera setup steps --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8debcbd538..7728015d70 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4193,9 +4193,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); From 498ac5d0ab65209aa40230435c10572ecde04f9d Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 15:08:06 -0700 Subject: [PATCH 264/269] Updated version of puck-attach.js --- scripts/developer/tests/puck-attach.js | 173 +++++++++++++++++-------- 1 file changed, 121 insertions(+), 52 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 65adf35d00..823308e3d5 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -17,7 +17,7 @@ Script.include("/~/system/libraries/Xform.js"); (function() { // BEGIN LOCAL_SCOPE var TABLET_BUTTON_NAME = "PUCKTACH"; -var TABLET_APP_URL = "http://content.highfidelity.com/seefo/production/puck-attach/puck-attach.html"; +var TABLET_APP_URL = "https://hifi-content.s3.amazonaws.com/seefo/production/puck-attach/puck-attach.html"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var tabletButton = tablet.addButton({ @@ -33,12 +33,6 @@ function onScreenChanged(type, url) { if (!shown) { // hook up to event bridge tablet.webEventReceived.connect(onWebEventReceived); - - Script.setTimeout(function () { - // send available tracked objects to the html running in the tablet. - var availableTrackedObjects = getAvailableTrackedObjects(); - tablet.emitScriptEvent(JSON.stringify(availableTrackedObjects)); - }, 1000); // wait 1 sec before sending.. } shown = true; } else { @@ -72,6 +66,12 @@ function getAvailableTrackedObjects() { } return available; } +function sendAvailableTrackedObjects() { + tablet.emitScriptEvent(JSON.stringify({ + pucks: getAvailableTrackedObjects(), + selectedPuck: ((lastPuck === undefined) ? -1 : lastPuck.name) + })); +} function getRelativePosition(origin, rotation, offset) { var relativeOffset = Vec3.multiplyQbyV(rotation, offset); @@ -81,45 +81,74 @@ function getRelativePosition(origin, rotation, offset) { function getPropertyForEntity(entityID, propertyName) { return Entities.getEntityProperties(entityID, [propertyName])[propertyName]; } +function entityExists(entityID) { + return Object.keys(Entities.getEntityProperties(entityID)).length > 0; +} var VIVE_PUCK_MODEL = "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj"; var VIVE_PUCK_DIMENSIONS = { x: 0.0945, y: 0.0921, z: 0.0423 }; // 1/1000th scale of model var VIVE_PUCK_SEARCH_DISTANCE = 1.5; // metres -var VIVE_PUCK_SPAWN_DISTANCE = 1.0; // metres +var VIVE_PUCK_SPAWN_DISTANCE = 0.5; // metres +var VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE = 10.0; // metres var VIVE_PUCK_NAME = "Tracked Puck"; var trackedPucks = { }; -var lastPuck = { }; +var lastPuck; function createPuck(puck) { // create a puck entity and add it to our list of pucks - var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); - var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); - - // should be an overlay - var puckEntityProperties = { - name: "Tracked Puck", - type: "Model", - modelURL: VIVE_PUCK_MODEL, - dimensions: VIVE_PUCK_DIMENSIONS, - position: spawnPosition, - userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' - }; - - var puckEntityID = Entities.addEntity(puckEntityProperties); + var action = indexToTrackedObjectName(puck.puckno); + var pose = Controller.getPoseValue(Controller.Standard[action]); - if (trackedPucks.hasOwnProperty(puck.puckno)) { - destroyPuck(puck.puckno); + if (pose && pose.valid) { + var spawnOffset = Vec3.multiply(Vec3.FRONT, VIVE_PUCK_SPAWN_DISTANCE); + var spawnPosition = getRelativePosition(MyAvatar.position, MyAvatar.orientation, spawnOffset); + + // should be an overlay + var puckEntityProperties = { + name: "Tracked Puck", + type: "Model", + modelURL: VIVE_PUCK_MODEL, + dimensions: VIVE_PUCK_DIMENSIONS, + position: spawnPosition, + userData: '{ "grabbableKey": { "grabbable": true, "kinematic": false } }' + }; + + var puckEntityID = Entities.addEntity(puckEntityProperties); + + // if we've already created this puck, destroy it + if (trackedPucks.hasOwnProperty(puck.puckno)) { + destroyPuck(puck.puckno); + } + // if we had an unfinalized puck, destroy it + if (lastPuck !== undefined) { + destroyPuck(lastPuck.name); + } + // create our new unfinalized puck + trackedPucks[puck.puckno] = { + puckEntityID: puckEntityID, + trackedEntityID: "" + }; + lastPuck = trackedPucks[puck.puckno]; + lastPuck.name = Number(puck.puckno); } - - trackedPucks[puck.puckno] = { - puckEntityID: puckEntityID, - trackedEntityID: "" - }; - lastPuck = trackedPucks[puck.puckno]; } -function finalizePuck() { +function finalizePuck(puckName) { // find nearest entity and change its parent to the puck + + if (!trackedPucks.hasOwnProperty(puckName)) { + print('2'); + return; + } + if (lastPuck === undefined) { + print('3'); + return; + } + if (lastPuck.name !== Number(puckName)) { + print('1'); + return; + } + var puckPosition = getPropertyForEntity(lastPuck.puckEntityID, "position"); var foundEntities = Entities.findEntities(puckPosition, VIVE_PUCK_SEARCH_DISTANCE); @@ -142,7 +171,16 @@ function finalizePuck() { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID }); + // remember the userdata for the tracked entity since we're about to + // remove it and make it ungrabbable + lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + // update properties of the tracked entity + Entities.editEntity(lastPuck.trackedEntityID, { + "parentID": lastPuck.puckEntityID, + "userData": '{ "grabbableKey": { "grabbable": false } }' + }); + // remove reference to puck since it is now calibrated and finalized + lastPuck = undefined; } } function updatePucks() { @@ -156,32 +194,57 @@ function updatePucks() { if (pose && pose.valid) { var puck = trackedPucks[puckName]; if (puck.trackedEntityID) { - var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); - var puckXform = new Xform(pose.rotation, pose.translation); - var finalXform = Xform.mul(avatarXform, puckXform); + if (entityExists(puck.trackedEntityID)) { + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var puckXform = new Xform(pose.rotation, pose.translation); + var finalXform = Xform.mul(avatarXform, puckXform); + + var d = Vec3.distance(MyAvatar.position, finalXform.pos); + if (d > VIVE_PUCK_TRACKED_OBJECT_MAX_DISTANCE) { + print('tried to move tracked object too far away: ' + d); + return; + } - Entities.editEntity(puck.puckEntityID, { - position: finalXform.pos, - rotation: finalXform.rot, - }); - - // in case someone grabbed both entities and destroyed the - // child/parent relationship - Entities.editEntity(puck.trackedEntityID, { - parentID: puck.puckEntityID - }); + Entities.editEntity(puck.puckEntityID, { + position: finalXform.pos, + rotation: finalXform.rot + }); + + // in case someone grabbed both entities and destroyed the + // child/parent relationship + Entities.editEntity(puck.trackedEntityID, { + parentID: puck.puckEntityID + }); + } else { + destroyPuck(puckName); + } } } } } function destroyPuck(puckName) { // unparent entity and delete its parent - var puckEntityID = trackedPucks[puckName].puckEntityID; - var trackedEntityID = trackedPucks[puckName].trackedEntityID; + if (!trackedPucks.hasOwnProperty(puckName)) { + return; + } + + var puck = trackedPucks[puckName]; + var puckEntityID = puck.puckEntityID; + var trackedEntityID = puck.trackedEntityID; - // remove the puck as a parent entity and then delete the puck - Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}" }); - Entities.deleteEntity(puckEntityID); + // remove the puck as a parent entity and restore the tracked entities + // former userdata + Entities.editEntity(trackedEntityID, { + "parentID": "{00000000-0000-0000-0000-000000000000}", + "userData": puck.trackedEntityUserData + }); + + delete trackedPucks[puckName]; + + Script.setTimeout(function() { + // delete the puck + Entities.deleteEntity(puckEntityID); + }, 100); } function destroyPucks() { // remove all pucks and unparent entities @@ -202,11 +265,17 @@ function onWebEventReceived(msg) { } switch (obj.cmd) { + case "ready": + sendAvailableTrackedObjects(); + break; case "create": createPuck(obj); break; case "finalize": - finalizePuck(); + finalizePuck(obj.puckno); + break; + case "destroy": + destroyPuck(obj.puckno); break; } } From 67b59210796bb2e10b08b9904b4e42a95d1b006f Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 3 Aug 2017 16:01:12 -0700 Subject: [PATCH 265/269] Changed puck-attach to make tracked entities collisionless --- scripts/developer/tests/puck-attach.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/puck-attach.js b/scripts/developer/tests/puck-attach.js index 823308e3d5..04d5db5710 100644 --- a/scripts/developer/tests/puck-attach.js +++ b/scripts/developer/tests/puck-attach.js @@ -171,17 +171,19 @@ function finalizePuck(puckName) { if (foundEntity) { lastPuck.trackedEntityID = foundEntity; - // remember the userdata for the tracked entity since we're about to - // remove it and make it ungrabbable + // remember the userdata and collisionless flag for the tracked entity since + // we're about to remove it and make it ungrabbable and collisionless lastPuck.trackedEntityUserData = getPropertyForEntity(foundEntity, "userData"); + lastPuck.trackedEntityCollisionFlag = getPropertyForEntity(foundEntity, "collisionless"); // update properties of the tracked entity Entities.editEntity(lastPuck.trackedEntityID, { "parentID": lastPuck.puckEntityID, - "userData": '{ "grabbableKey": { "grabbable": false } }' + "userData": '{ "grabbableKey": { "grabbable": false } }', + "collisionless": 1 }); // remove reference to puck since it is now calibrated and finalized lastPuck = undefined; - } + } } function updatePucks() { // for each puck, update its position and orientation @@ -233,14 +235,19 @@ function destroyPuck(puckName) { var trackedEntityID = puck.trackedEntityID; // remove the puck as a parent entity and restore the tracked entities - // former userdata + // former userdata and collision flag Entities.editEntity(trackedEntityID, { "parentID": "{00000000-0000-0000-0000-000000000000}", - "userData": puck.trackedEntityUserData + "userData": puck.trackedEntityUserData, + "collisionless": puck.trackedEntityCollisionFlag }); delete trackedPucks[puckName]; + // in some cases, the entity deletion may occur before the parent change + // has been processed, resulting in both the puck and the tracked entity + // to be deleted so we wait 100ms before deleting the puck, assuming + // that the parent change has occured Script.setTimeout(function() { // delete the puck Entities.deleteEntity(puckEntityID); From 9058a8e2ca73481570ff0198ed90747d2cb82f85 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 4 Aug 2017 16:36:00 +0200 Subject: [PATCH 266/269] Cleanup top menu on menu reinstantiation --- interface/resources/qml/hifi/tablet/TabletMenuStack.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index 2fd33e9cbc..9076cd6c48 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -114,6 +114,7 @@ Item { } function clearMenus() { + topMenu = null d.clear() } From d05e1677c3a58272b14c655bbd940a9d73200edd Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 4 Aug 2017 23:04:15 +0300 Subject: [PATCH 267/269] 6677: Change "Desktop" App to Say "Exit VR" --- scripts/system/hmd.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 3598b461a4..c95d85ebeb 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -43,17 +43,20 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); // Independent and Entity mode make people sick; disable them in hmd. var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; +var switchToVR = "Enter VR"; +var switchToDesktop = "Exit VR"; + function onHmdChanged(isHmd) { HMD.closeTablet(); if (isHmd) { button.editProperties({ icon: "icons/tablet-icons/switch-desk-i.svg", - text: "DESKTOP" + text: switchToDesktop }); } else { button.editProperties({ icon: "icons/tablet-icons/switch-vr-i.svg", - text: "VR" + text: switchToVR }); } desktopOnlyViews.forEach(function (view) { @@ -70,7 +73,7 @@ function onClicked() { if (headset) { button = tablet.addButton({ icon: HMD.active ? "icons/tablet-icons/switch-desk-i.svg" : "icons/tablet-icons/switch-vr-i.svg", - text: HMD.active ? "DESKTOP" : "VR", + text: HMD.active ? switchToDesktop : switchToVR, sortOrder: 2 }); onHmdChanged(HMD.active); From 86e348916724fc8dd290569acd9fd44ee0c37e02 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 25 Jul 2017 09:42:42 -0700 Subject: [PATCH 268/269] Updates from Threaded Rendering project --- interface/src/Application.cpp | 49 +++++------- interface/src/Application.h | 9 ++- interface/src/scripting/AudioDevices.cpp | 1 + interface/src/ui/ResourceImageItem.cpp | 3 +- interface/src/ui/SnapshotAnimated.cpp | 3 +- .../src/RenderableWebEntityItem.cpp | 7 +- .../src/RenderableWebEntityItem.h | 2 +- .../plugins/src/plugins/DisplayPlugin.cpp | 17 +++- libraries/plugins/src/plugins/DisplayPlugin.h | 11 ++- libraries/render/src/render/Args.h | 3 - libraries/shared/src/ThreadHelpers.cpp | 77 ++++++++++++++----- libraries/shared/src/ThreadHelpers.h | 13 +++- libraries/shared/src/shared/QtHelpers.cpp | 29 ++++++- libraries/shared/src/shared/QtHelpers.h | 1 + tests/render-perf/src/main.cpp | 2 +- 15 files changed, 155 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d016c9d020..549e5338a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,7 +2256,7 @@ void Application::paintGL() { QMutexLocker viewLocker(&_viewMutex); _viewFrustum.calculate(); } - renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(), + renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); { @@ -2772,7 +2772,12 @@ bool Application::importSVOFromURL(const QString& urlString) { return true; } -bool _renderRequested { false }; +void Application::onPresent(quint32 frameCount) { + if (shouldPaint()) { + postEvent(this, new QEvent(static_cast(Idle)), Qt::HighEventPriority); + postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); + } +} bool Application::event(QEvent* event) { if (!Menu::getInstance()) { @@ -2788,23 +2793,9 @@ bool Application::event(QEvent* event) { // Explicit idle keeps the idle running at a lower interval, but without any rendering // see (windowMinimizedChanged) case Event::Idle: - { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - _lastTimeUpdated.start(); - idle(nsecsElapsed); - } - return true; - - case Event::Present: - if (!_renderRequested) { - float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); - if (shouldPaint(nsecsElapsed)) { - _renderRequested = true; - _lastTimeUpdated.start(); - idle(nsecsElapsed); - postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); - } - } + idle(); + // Clear the event queue of pending idle calls + removePostedEvents(this, Idle); return true; case Event::Paint: @@ -2812,9 +2803,8 @@ bool Application::event(QEvent* event) { // or AvatarInputs will mysteriously move to the bottom-right AvatarInputs::getInstance()->update(); paintGL(); - // wait for the next present event before starting idle / paint again - removePostedEvents(this, Present); - _renderRequested = false; + // Clear the event queue of pending paint calls + removePostedEvents(this, Paint); return true; default: @@ -3633,7 +3623,7 @@ bool Application::acceptSnapshot(const QString& urlString) { static uint32_t _renderedFrameIndex { INVALID_FRAME }; -bool Application::shouldPaint(float nsecsElapsed) { +bool Application::shouldPaint() { if (_aboutToQuit) { return false; } @@ -3653,11 +3643,9 @@ bool Application::shouldPaint(float nsecsElapsed) { (float)paintDelaySamples / paintDelayUsecs << "us"; } #endif - - float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC; - + // Throttle if requested - if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) { + if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) { return false; } @@ -3874,7 +3862,7 @@ void setupCpuMonitorThread() { #endif -void Application::idle(float nsecsElapsed) { +void Application::idle() { PerformanceTimer perfTimer("idle"); // Update the deadlock watchdog @@ -3931,7 +3919,8 @@ void Application::idle(float nsecsElapsed) { steamClient->runCallbacks(); } - float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; + float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND; + _lastTimeUpdated.start(); // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) { @@ -7106,6 +7095,7 @@ void Application::updateDisplayMode() { auto oldDisplayPlugin = _displayPlugin; if (_displayPlugin) { + disconnect(_displayPluginPresentConnection); _displayPlugin->deactivate(); } @@ -7146,6 +7136,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; + _displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 123aa85e2e..300bd4ac02 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -129,8 +129,7 @@ public: virtual DisplayPluginPointer getActiveDisplayPlugin() const override; enum Event { - Present = DisplayPlugin::Present, - Paint, + Paint = QEvent::User + 1, Idle, Lambda }; @@ -409,6 +408,7 @@ private slots: void clearDomainOctreeDetails(); void clearDomainAvatars(); void onAboutToQuit(); + void onPresent(quint32 frameCount); void resettingDomain(); @@ -455,8 +455,8 @@ private: void cleanupBeforeQuit(); - bool shouldPaint(float nsecsElapsed); - void idle(float nsecsElapsed); + bool shouldPaint(); + void idle(); void update(float deltaTime); // Various helper functions called during update() @@ -518,6 +518,7 @@ private: OffscreenGLCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; + QMetaObject::Connection _displayPluginPresentConnection; mutable std::mutex _displayPluginLock; InputPluginList _activeInputPlugins; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index d02f4d8fcf..f2e6dbf4d7 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "AudioDevices.h" diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 7b9592fa4c..5b7c1896fe 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -//#include "Application.h" #include "ResourceImageItem.h" #include @@ -16,6 +15,8 @@ #include #include +#include + ResourceImageItem::ResourceImageItem() : QQuickFramebufferObject() { auto textureCache = DependencyManager::get(); connect(textureCache.data(), SIGNAL(spectatorCameraFramebufferReset()), this, SLOT(update())); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 70767b007d..3c00be8358 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -13,8 +13,9 @@ #include #include #include +#include -#include +#include #include "SnapshotAnimated.h" QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9a898b4071..ba8e0c18e7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -69,7 +69,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() { } } -bool RenderableWebEntityItem::buildWebSurface(QSharedPointer renderer) { +bool RenderableWebEntityItem::buildWebSurface() { if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) { qWarning() << "Too many concurrent web views to create new view"; return false; @@ -132,6 +132,8 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer handlePointerEvent(event); } }; + + auto renderer = DependencyManager::get(); _mousePressConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent); _mouseReleaseConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent); _mouseMoveConnection = QObject::connect(renderer.data(), &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent); @@ -185,8 +187,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - auto renderer = qSharedPointerCast(args->_renderData); - if (!buildWebSurface(renderer)) { + if (!buildWebSurface()) { return; } _fadeStartTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0f5d307766..a2318081b6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -57,7 +57,7 @@ public: virtual QObject* getRootItem() override; private: - bool buildWebSurface(QSharedPointer renderer); + bool buildWebSurface(); void destroyWebSurface(); glm::vec2 getWindowSize() const; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 747c72c08e..20c72159c4 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -18,6 +18,19 @@ void DisplayPlugin::incrementPresentCount() { ++_presentedFrameIndex; - // Alert the app that it needs to paint a new presentation frame - qApp->postEvent(qApp, new QEvent(static_cast(Present)), Qt::HighEventPriority); + { + QMutexLocker locker(&_presentMutex); + _presentCondition.wakeAll(); + } + + emit presented(_presentedFrameIndex); } + +void DisplayPlugin::waitForPresent() { + QMutexLocker locker(&_presentMutex); + while (isActive()) { + if (_presentCondition.wait(&_presentMutex, MSECS_PER_SECOND)) { + break; + } + } +} \ No newline at end of file diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 481a2609fc..9e18ee534d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -134,10 +136,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay { Q_OBJECT using Parent = Plugin; public: - enum Event { - Present = QEvent::User + 1 - }; - virtual int getRequiredThreadCount() const { return 0; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } @@ -221,12 +219,15 @@ public: virtual void cycleDebugOutput() {} + void waitForPresent(); + static const QString& MENU_PATH(); signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); + void presented(quint32 frame); protected: void incrementPresentCount(); @@ -234,6 +235,8 @@ protected: gpu::ContextPointer _gpuContext; private: + QMutex _presentMutex; + QWaitCondition _presentCondition; std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; QElapsedTimer _paintDelayTimer; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index d5b5440c32..6a91081c95 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -77,7 +77,6 @@ namespace render { Args() {} Args(const gpu::ContextPointer& context, - QSharedPointer renderData = QSharedPointer(nullptr), float sizeScale = 1.0f, int boundaryLevelAdjust = 0, RenderMode renderMode = DEFAULT_RENDER_MODE, @@ -85,7 +84,6 @@ namespace render { DebugFlags debugFlags = RENDER_DEBUG_NONE, gpu::Batch* batch = nullptr) : _context(context), - _renderData(renderData), _sizeScale(sizeScale), _boundaryLevelAdjust(boundaryLevelAdjust), _renderMode(renderMode), @@ -110,7 +108,6 @@ namespace render { std::shared_ptr _context; std::shared_ptr _blitFramebuffer; std::shared_ptr _shapePipeline; - QSharedPointer _renderData; std::stack _viewFrustums; glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f }; glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f }; diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8f3d16a577..8cf8b85284 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -10,29 +10,66 @@ #include +// Support for viewing the thread name in the debugger. +// Note, Qt actually does this for you but only in debug builds +// Code from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx +// and matches logic in `qt_set_thread_name` in qthread_win.cpp +#ifdef Q_OS_WIN +#include +#pragma pack(push,8) +struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) +#endif + +void setThreadName(const std::string& name) { +#ifdef Q_OS_WIN + static const DWORD MS_VC_EXCEPTION = 0x406D1388; + THREADNAME_INFO info{ 0x1000, name.c_str(), (DWORD)-1, 0 }; + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } __except (EXCEPTION_EXECUTE_HANDLER) { } +#endif +} + +void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { + Q_ASSERT(QThread::currentThread() == object->thread()); + // setup a thread for the NodeList and its PacketReceiver + QThread* thread = new QThread(); + thread->setObjectName(name); + + // Execute any additional work to do before the thread starts (like moving members to the target thread + preStartCallback(thread); + + // Link the in-thread initialization code + QObject::connect(thread, &QThread::started, [name, startCallback] { + if (!name.isEmpty()) { + // Make it easy to spot our thread processes inside the debugger + setThreadName("Hifi_" + name.toStdString()); + } + startCallback(); + }); + + // Make sure the thread will be destroyed and cleaned up + QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); + QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + // put the object on the thread + object->moveToThread(thread); + thread->start(); + if (priority != QThread::InheritPriority) { + thread->setPriority(priority); + } +} void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority) { - Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver - QThread* thread = new QThread(); - thread->setObjectName(name); - - QString tempName = name; - QObject::connect(thread, &QThread::started, [startCallback] { - startCallback(); - }); - // Make sure the thread will be destroyed and cleaned up - QObject::connect(object, &QObject::destroyed, thread, &QThread::quit); - QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); - - // put the object on the thread - object->moveToThread(thread); - thread->start(); - if (priority != QThread::InheritPriority) { - thread->setPriority(priority); - } + moveToNewNamedThread(object, name, [](QThread*){}, startCallback, priority); } void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { - moveToNewNamedThread(object, name, [] {}, priority); + moveToNewNamedThread(object, name, [](QThread*){}, []{}, priority); } diff --git a/libraries/shared/src/ThreadHelpers.h b/libraries/shared/src/ThreadHelpers.h index 6e024f787a..d236344dc5 100644 --- a/libraries/shared/src/ThreadHelpers.h +++ b/libraries/shared/src/ThreadHelpers.h @@ -32,8 +32,17 @@ void withLock(QMutex& lock, F function) { function(); } -void moveToNewNamedThread(QObject* object, const QString& name, std::function startCallback, QThread::Priority priority = QThread::InheritPriority); -void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority); +void moveToNewNamedThread(QObject* object, const QString& name, + std::function preStartCallback, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + std::function startCallback, + QThread::Priority priority = QThread::InheritPriority); + +void moveToNewNamedThread(QObject* object, const QString& name, + QThread::Priority priority = QThread::InheritPriority); class ConditionalGuard { public: diff --git a/libraries/shared/src/shared/QtHelpers.cpp b/libraries/shared/src/shared/QtHelpers.cpp index 1ce1c3e07c..3e8c6d57ed 100644 --- a/libraries/shared/src/shared/QtHelpers.cpp +++ b/libraries/shared/src/shared/QtHelpers.cpp @@ -11,11 +11,24 @@ #include #include #include +#include +#include "../Profile.h" Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety") namespace hifi { namespace qt { +static QHash threadHash; +static QReadWriteLock threadHashLock; + +void addBlockingForbiddenThread(const QString& name, QThread* thread) { + if (!thread) { + thread = QThread::currentThread(); + } + QWriteLocker locker(&threadHashLock); + threadHash[thread] = name; +} + bool blockingInvokeMethod( const char* function, QObject *obj, const char *member, @@ -30,9 +43,23 @@ bool blockingInvokeMethod( QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { - if (QThread::currentThread() == qApp->thread()) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function; + return QMetaObject::invokeMethod(obj, member, + Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + + { + QReadLocker locker(&threadHashLock); + for (const auto& thread : threadHash.keys()) { + if (currentThread == thread) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << threadHash[thread]; + } + } } + + PROFILE_RANGE(app, function); return QMetaObject::invokeMethod(obj, member, Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 5da65a378f..2133119324 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -14,6 +14,7 @@ namespace hifi { namespace qt { +void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr); bool blockingInvokeMethod( const char* function, diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ce47a896aa..dbb315a9ae 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -681,7 +681,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); - RenderArgs renderArgs(_renderThread._gpuContext, _octree, DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); From 0e508432c4c745d08d890502d7279c062fc7b105 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 4 Aug 2017 15:27:33 -0700 Subject: [PATCH 269/269] PR comments --- libraries/shared/src/ThreadHelpers.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/ThreadHelpers.cpp b/libraries/shared/src/ThreadHelpers.cpp index 8cf8b85284..654b8e0252 100644 --- a/libraries/shared/src/ThreadHelpers.cpp +++ b/libraries/shared/src/ThreadHelpers.cpp @@ -38,11 +38,14 @@ void setThreadName(const std::string& name) { void moveToNewNamedThread(QObject* object, const QString& name, std::function preStartCallback, std::function startCallback, QThread::Priority priority) { Q_ASSERT(QThread::currentThread() == object->thread()); - // setup a thread for the NodeList and its PacketReceiver + + // Create the target thread QThread* thread = new QThread(); thread->setObjectName(name); - // Execute any additional work to do before the thread starts (like moving members to the target thread + // Execute any additional work to do before the thread starts like moving members to the target thread. + // This is required as QObject::moveToThread isn't virutal, so we can't override it on objects that contain + // an OpenGLContext and ensure that the context moves to the target thread as well. preStartCallback(thread); // Link the in-thread initialization code @@ -54,8 +57,10 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function