From 0efce7c8eef89e77349748ef56bc2aa54765f845 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 17 Sep 2014 13:50:57 -0700 Subject: [PATCH 01/18] Add hotkey to reload all scripts --- interface/src/Menu.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d7679968ae..2417d156a6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -148,7 +148,8 @@ Menu::Menu() : addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::SHIFT | Qt::Key_R, + appInstance, SLOT(reloadAllScripts())); addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, appInstance, SLOT(toggleRunningScriptsWidget())); From b55a5acdd3cf4553d73884e49290fca465bcaf55 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 17 Sep 2014 14:27:11 -0700 Subject: [PATCH 02/18] Update style of running scripts widget --- interface/ui/runningScriptsWidget.ui | 109 ++++++++++++--------------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index ddc6ad6c27..b4d17b1561 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -6,8 +6,8 @@ 0 0 - 324 - 643 + 319 + 481 @@ -29,57 +29,12 @@ 20 - - - - - 6 - - - 0 - - - 6 - - - - - color: #0e7077; -font-size: 20px; - - - Running Scripts - - - 0 - - - -1 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + 0 - 1 + 0 @@ -157,7 +112,7 @@ font: bold 16px; 0 - 8 + 4 @@ -218,6 +173,12 @@ font: bold 16px; + + + 0 + 0 + + 0 @@ -236,6 +197,12 @@ font: bold 16px; + + + 0 + 0 + + Helvetica,Arial,sans-serif @@ -252,11 +219,14 @@ font: bold 16px; 0 - Qt::ScrollBarAsNeeded + Qt::ScrollBarAlwaysOn Qt::ScrollBarAlwaysOff + + QAbstractScrollArea::AdjustToContents + true @@ -268,7 +238,7 @@ font: bold 16px; 0 0 - 284 + 264 16 @@ -278,6 +248,9 @@ font: bold 16px; 0 + + Qt::LeftToRight + font-size: 14px; @@ -303,14 +276,20 @@ font: bold 16px; + + + 0 + 0 + + - font: 14px; + font: 14px; color: #5f5f5f; - There are no scripts currently running. + There are no scripts running. - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -325,15 +304,9 @@ font: bold 16px; 0 - 2 + 0 - - - 0 - 300 - - 0 @@ -352,6 +325,12 @@ font: bold 16px; + + + 0 + 0 + + 0 @@ -432,6 +411,12 @@ font: bold 16px; + + + 0 + 0 + + Qt::ScrollBarAlwaysOn From 145425276d74ac9d480bf70e671602918de445ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 19 Sep 2014 14:17:08 -0700 Subject: [PATCH 03/18] Fix script list not being hidden --- interface/src/ui/RunningScriptsWidget.cpp | 1 + interface/ui/runningScriptsWidget.ui | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 6efa2f8fe6..baf3995d0e 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -136,6 +136,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { ui->noRunningScriptsLabel->setVisible(list.isEmpty()); + ui->runningScriptsList->setVisible(!list.isEmpty()); ui->reloadAllButton->setVisible(!list.isEmpty()); ui->stopAllButton->setVisible(!list.isEmpty()); diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index b4d17b1561..a55e949d7a 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -222,7 +222,7 @@ font: bold 16px; Qt::ScrollBarAlwaysOn - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAsNeeded QAbstractScrollArea::AdjustToContents From 900daf48eb2a6740b7635fc8ee271df6a8fc7be5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Sep 2014 14:48:44 -0700 Subject: [PATCH 04/18] initial working connection of an xbox gamepad --- examples/xbox.js | 24 +++++++++ interface/CMakeLists.txt | 10 +++- interface/src/devices/JoystickManager.cpp | 33 ++++++------ interface/src/devices/JoystickManager.h | 16 ++---- interface/src/devices/PrioVR.cpp | 2 +- .../ControllerScriptingInterface.cpp | 11 ++++ .../scripting/ControllerScriptingInterface.h | 1 + .../src/JoystickInputController.cpp | 52 +++++++++++++++++++ .../src/JoystickInputController.h | 50 ++++++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 16 ++++-- 10 files changed, 183 insertions(+), 32 deletions(-) create mode 100644 examples/xbox.js create mode 100644 libraries/script-engine/src/JoystickInputController.cpp create mode 100644 libraries/script-engine/src/JoystickInputController.h diff --git a/examples/xbox.js b/examples/xbox.js new file mode 100644 index 0000000000..03378995bf --- /dev/null +++ b/examples/xbox.js @@ -0,0 +1,24 @@ +// +// xbox.js +// examples +// +// Created by Stephen Birarda on September 23, 2014 +// +// 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 +// + +gamepad = Controller.joystick(); +print("THE GAMEPAD NAME is " + gamepad.name); +print("THE GAMEPAD HAS " + gamepad.numAxes + " AXES") + +function printValues() { + controllerAxes = gamepad.axes; + for (i = 0; i < controllerAxes.size; i++) { + // print("The value for axis " + i + " is " + controllerAxes[i]); + } +} + +Script.update.connect(printValues); \ No newline at end of file diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 5653286104..a2a832afe0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp") +set(OPTIONAL_EXTERNALS "Faceplus" "Faceshift" "LibOVR" "PrioVR" "Sixense" "Visage" "LeapMotion" "RtMidi" "Qxmpp" "SDL") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) @@ -120,6 +120,10 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE}) # include the library directories (ignoring warnings) + if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS) + set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR}) + endif () + include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) # perform the system include hack for OS X to ignore warnings @@ -129,6 +133,10 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) endforeach() endif () + if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES) + set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY}) + endif () + target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES}) endif () diff --git a/interface/src/devices/JoystickManager.cpp b/interface/src/devices/JoystickManager.cpp index 8169c6d06e..afcc2823ac 100644 --- a/interface/src/devices/JoystickManager.cpp +++ b/interface/src/devices/JoystickManager.cpp @@ -25,12 +25,13 @@ JoystickManager::JoystickManager() { SDL_Init(SDL_INIT_JOYSTICK); int joystickCount = SDL_NumJoysticks(); for (int i = 0; i < joystickCount; i++) { - SDL_Joystick* joystick = SDL_JoystickOpen(i); - if (joystick) { - JoystickState state = { SDL_JoystickName(i), QVector(SDL_JoystickNumAxes(joystick)), - QVector(SDL_JoystickNumButtons(joystick)) }; - _joystickStates.append(state); - _joysticks.append(joystick); + SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i); + if (sdlJoystick) { + _sdlJoysticks.append(sdlJoystick); + + JoystickInputController controller(SDL_JoystickName(i), + SDL_JoystickNumAxes(sdlJoystick), SDL_JoystickNumButtons(sdlJoystick)); + _joysticks.append(controller); } } #endif @@ -38,8 +39,8 @@ JoystickManager::JoystickManager() { JoystickManager::~JoystickManager() { #ifdef HAVE_SDL - foreach (SDL_Joystick* joystick, _joysticks) { - SDL_JoystickClose(joystick); + foreach (SDL_Joystick* sdlJoystick, _sdlJoysticks) { + SDL_JoystickClose(sdlJoystick); } SDL_Quit(); #endif @@ -50,16 +51,16 @@ void JoystickManager::update() { PerformanceTimer perfTimer("joystick"); SDL_JoystickUpdate(); - for (int i = 0; i < _joystickStates.size(); i++) { - SDL_Joystick* joystick = _joysticks.at(i); - JoystickState& state = _joystickStates[i]; - for (int j = 0; j < state.axes.size(); j++) { - float value = glm::round(SDL_JoystickGetAxis(joystick, j) + 0.5f) / numeric_limits::max(); + for (int i = 0; i < _joysticks.size(); i++) { + SDL_Joystick* sdlJoystick = _sdlJoysticks.at(i); + JoystickInputController& joystick = _joysticks[i]; + for (int j = 0; j < joystick.getNumAxes(); j++) { + float value = glm::round(SDL_JoystickGetAxis(sdlJoystick, j) + 0.5f) / numeric_limits::max(); const float DEAD_ZONE = 0.1f; - state.axes[j] = glm::abs(value) < DEAD_ZONE ? 0.0f : value; + joystick.updateAxis(j, glm::abs(value) < DEAD_ZONE ? 0.0f : value); } - for (int j = 0; j < state.buttons.size(); j++) { - state.buttons[j] = SDL_JoystickGetButton(joystick, j); + for (int j = 0; j < joystick.getNumButtons(); j++) { + joystick.updateButton(j, SDL_JoystickGetButton(sdlJoystick, j)); } } #endif diff --git a/interface/src/devices/JoystickManager.h b/interface/src/devices/JoystickManager.h index 53a255e129..31c2dc934b 100644 --- a/interface/src/devices/JoystickManager.h +++ b/interface/src/devices/JoystickManager.h @@ -20,6 +20,8 @@ #undef main #endif +#include "JoystickInputController.h" + class JoystickState; /// Handles joystick input through SDL. @@ -27,27 +29,19 @@ class JoystickManager : public QObject { Q_OBJECT public: - JoystickManager(); virtual ~JoystickManager(); - const QVector& getJoystickStates() const { return _joystickStates; } + QVector& getJoysticks() { return _joysticks; } void update(); private: - QVector _joystickStates; + QVector _joysticks; #ifdef HAVE_SDL - QVector _joysticks; + QVector _sdlJoysticks; #endif }; -class JoystickState { -public: - QString name; - QVector axes; - QVector buttons; -}; - #endif // hifi_JoystickManager_h diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index fad80fda6f..30c5d74ce1 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -61,7 +61,7 @@ static void setPalm(float deltaTime, int index) { palm->setActive(true); // Read controller buttons and joystick into the hand - if (!Application::getInstance()->getJoystickManager()->getJoystickStates().isEmpty()) { + if (!Application::getInstance()->getJoystickManager()->getJoysticks().isEmpty()) { const JoystickState& state = Application::getInstance()->getJoystickManager()->getJoystickStates().at(0); if (state.axes.size() >= 4 && state.buttons.size() >= 4) { if (index == LEFT_HAND_INDEX) { diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index f2e65a6e28..e8047b1ceb 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -297,6 +297,17 @@ AbstractInputController* ControllerScriptingInterface::createInputController(con } } +JoystickInputController* ControllerScriptingInterface::joystick() const { + // stub to just return the first joystick - assume we only have one connected + QVector& activeJoysticks = Application::getInstance()->getJoystickManager()->getJoysticks(); + + if (activeJoysticks.size() > 0) { + return &activeJoysticks[0]; + } else { + return NULL; + } +} + void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) { _inputControllers.erase(input->getKey()); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 62ef2e9b24..ada79e95cd 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -113,6 +113,7 @@ public slots: /// Factory to create an InputController virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker); + JoystickInputController* joystick() const; virtual void releaseInputController(AbstractInputController* input); diff --git a/libraries/script-engine/src/JoystickInputController.cpp b/libraries/script-engine/src/JoystickInputController.cpp new file mode 100644 index 0000000000..fedb3a7643 --- /dev/null +++ b/libraries/script-engine/src/JoystickInputController.cpp @@ -0,0 +1,52 @@ +// +// JoystickInputController.cpp +// interface/src/devices +// +// Created by Stephen Birarda on 2014-09-23. +// 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 + +#include "JoystickInputController.h" + +JoystickInputController::JoystickInputController() : + _name(), + _axes(0), + _buttons(0) +{ + +} + +JoystickInputController::JoystickInputController(const QString& name, int numAxes, int numButtons) : + _name(name), + _axes(numAxes), + _buttons(numButtons) +{ + +} + +JoystickInputController::JoystickInputController(const JoystickInputController& otherJoystickController) : + _name(otherJoystickController._name), + _axes(otherJoystickController._axes), + _buttons(otherJoystickController._buttons) +{ + +} + +JoystickInputController& JoystickInputController::operator=(const JoystickInputController& otherJoystickController) { + JoystickInputController temp(otherJoystickController); + swap(temp); + return *this; +} + +void JoystickInputController::swap(JoystickInputController& otherJoystickController) { + using std::swap; + + swap(_name, otherJoystickController._name); + swap(_axes, otherJoystickController._axes); + swap(_buttons, otherJoystickController._buttons); +} diff --git a/libraries/script-engine/src/JoystickInputController.h b/libraries/script-engine/src/JoystickInputController.h new file mode 100644 index 0000000000..27166c47fd --- /dev/null +++ b/libraries/script-engine/src/JoystickInputController.h @@ -0,0 +1,50 @@ +// +// JoystickInputController.h +// interface/src/devices +// +// Created by Stephen Birarda on 2014-09-23. +// 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_JoystickInputController_h +#define hifi_JoystickInputController_h + +#include +#include + +class JoystickInputController : public QObject { + Q_OBJECT + + Q_PROPERTY(QString name READ getName) + + Q_PROPERTY(int numAxes READ getNumAxes) + Q_PROPERTY(QVector axes READ getAxes) +public: + JoystickInputController(); + JoystickInputController(const QString& name, int numAxes, int numButtons); + JoystickInputController(const JoystickInputController& otherJoystickController); + JoystickInputController& operator=(const JoystickInputController& otherJoystickController); + + const QString& getName() const { return _name; } + + void updateAxis(int index, float value) { _axes[index] = value; } + void updateButton(int index, bool isActive) { _buttons[index] = isActive; } + + const QVector& getAxes() const { return _axes; } + const QVector& getButtons() const { return _buttons; } + + int getNumAxes() const { return _axes.size(); } + int getNumButtons() const { return _buttons.size(); } + +private: + void swap(JoystickInputController& otherJoystickController); + + QString _name; + QVector _axes; + QVector _buttons; +}; + +#endif // hifi_JoystickTracker_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cfea0d6b86..0ab5f37f29 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -35,6 +35,7 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" #include "DataViewClass.h" +#include "JoystickInputController.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -72,14 +73,22 @@ void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) { out = qobject_cast(object.toQObject()); } -QScriptValue injectorToScriptValueInputController(QScriptEngine *engine, AbstractInputController* const &in) { +QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { return engine->newQObject(in); } -void injectorFromScriptValueInputController(const QScriptValue &object, AbstractInputController* &out) { +void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { out = qobject_cast(object.toQObject()); } +QScriptValue joystickToScriptValue(QScriptEngine *engine, JoystickInputController* const &in) { + return engine->newQObject(in); +} + +void joystickFromScriptValue(const QScriptValue &object, JoystickInputController* &out) { + out = qobject_cast(object.toQObject()); +} + ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface) : @@ -277,7 +286,8 @@ void ScriptEngine::init() { globalObject().setProperty("LocalVoxels", localVoxelsValue); qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); - qScriptRegisterMetaType( this, injectorToScriptValueInputController, injectorFromScriptValueInputController); + qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); + qScriptRegisterMetaType(this, joystickToScriptValue, joystickFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); From 00b0806c4d7dfd0716ae0cd91df2dc6aca7b2119 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Sep 2014 17:09:46 -0700 Subject: [PATCH 05/18] move JoystickManager to JoystickScriptingInterface, add Joystick class for SDL Joystick --- examples/xbox.js | 15 +--- interface/src/Application.cpp | 5 +- interface/src/Application.h | 3 - interface/src/devices/Joystick.cpp | 60 ++++++++++++++ .../src/devices/Joystick.h | 40 +++++---- interface/src/devices/JoystickManager.cpp | 67 --------------- interface/src/devices/JoystickManager.h | 47 ----------- .../ControllerScriptingInterface.cpp | 11 --- .../scripting/ControllerScriptingInterface.h | 1 - .../scripting/JoystickScriptingInterface.cpp | 82 +++++++++++++++++++ .../scripting/JoystickScriptingInterface.h | 43 ++++++++++ .../src/JoystickInputController.cpp | 52 ------------ libraries/script-engine/src/ScriptEngine.cpp | 10 --- 13 files changed, 217 insertions(+), 219 deletions(-) create mode 100644 interface/src/devices/Joystick.cpp rename libraries/script-engine/src/JoystickInputController.h => interface/src/devices/Joystick.h (50%) delete mode 100644 interface/src/devices/JoystickManager.cpp delete mode 100644 interface/src/devices/JoystickManager.h create mode 100644 interface/src/scripting/JoystickScriptingInterface.cpp create mode 100644 interface/src/scripting/JoystickScriptingInterface.h delete mode 100644 libraries/script-engine/src/JoystickInputController.cpp diff --git a/examples/xbox.js b/examples/xbox.js index 03378995bf..9190ee23ac 100644 --- a/examples/xbox.js +++ b/examples/xbox.js @@ -10,15 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -gamepad = Controller.joystick(); -print("THE GAMEPAD NAME is " + gamepad.name); -print("THE GAMEPAD HAS " + gamepad.numAxes + " AXES") +joysticks = Joysticks.availableJoystickNames; -function printValues() { - controllerAxes = gamepad.axes; - for (i = 0; i < controllerAxes.size; i++) { - // print("The value for axis " + i + " is " + controllerAxes[i]); - } -} - -Script.update.connect(printValues); \ No newline at end of file +for (i = 0; i < joysticks.length; i++) { + print("Joystick " + i + " is " + joysticks[i]); +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 105fb6a973..c49a1e234a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -82,6 +82,7 @@ #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" +#include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -2155,7 +2156,7 @@ void Application::update(float deltaTime) { updateFaceshift(); updateVisage(); _sixenseManager.update(deltaTime); - _joystickManager.update(); + JoystickScriptingInterface::getInstance().update(); _prioVR.update(deltaTime); } @@ -3847,6 +3848,8 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AvatarManager", &_avatarManager); + + scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); diff --git a/interface/src/Application.h b/interface/src/Application.h index d17bacd413..7c644b61ea 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -59,7 +59,6 @@ #include "avatar/MyAvatar.h" #include "devices/Faceplus.h" #include "devices/Faceshift.h" -#include "devices/JoystickManager.h" #include "devices/PrioVR.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" @@ -221,7 +220,6 @@ public: FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } PrioVR* getPrioVR() { return &_prioVR; } - JoystickManager* getJoystickManager() { return &_joystickManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } QSystemTrayIcon* getTrayIcon() { return _trayIcon; } @@ -511,7 +509,6 @@ private: SixenseManager _sixenseManager; PrioVR _prioVR; - JoystickManager _joystickManager; Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode diff --git a/interface/src/devices/Joystick.cpp b/interface/src/devices/Joystick.cpp new file mode 100644 index 0000000000..d220a827f1 --- /dev/null +++ b/interface/src/devices/Joystick.cpp @@ -0,0 +1,60 @@ +// +// Joystick.cpp +// interface/src/devices +// +// Created by Stephen Birarda on 2014-09-23. +// 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 + +#include + +#include "Joystick.h" + +#ifdef HAVE_SDL + +Joystick::Joystick(const QString& name, SDL_Joystick* sdlJoystick) : + _name(name), + _axes(QVector(SDL_JoystickNumAxes(sdlJoystick))), + _buttons(QVector(SDL_JoystickNumButtons(sdlJoystick))), + _sdlJoystick(sdlJoystick) +{ + +} + +#endif + +Joystick::~Joystick() { +#ifdef HAVE_SDL + SDL_JoystickClose(_sdlJoystick); +#endif +} + +void Joystick::update() { +#ifdef HAVE_SDL + // update our current values, emit a signal when there is a change + for (int j = 0; j < getNumAxes(); j++) { + float value = glm::round(SDL_JoystickGetAxis(_sdlJoystick, j) + 0.5f) / std::numeric_limits::max(); + const float DEAD_ZONE = 0.1f; + float cleanValue = glm::abs(value) < DEAD_ZONE ? 0.0f : value; + + if (_axes[j] != cleanValue) { + float oldValue = _axes[j]; + _axes[j] = cleanValue; + emit axisValueChanged(j, cleanValue, oldValue); + } + } + for (int j = 0; j < getNumButtons(); j++) { + bool newValue = SDL_JoystickGetButton(_sdlJoystick, j); + if (_buttons[j] != newValue) { + bool oldValue = _buttons[j]; + _buttons[j] = newValue; + emit buttonStateChanged(j, newValue, oldValue); + } + } +#endif +} \ No newline at end of file diff --git a/libraries/script-engine/src/JoystickInputController.h b/interface/src/devices/Joystick.h similarity index 50% rename from libraries/script-engine/src/JoystickInputController.h rename to interface/src/devices/Joystick.h index 27166c47fd..c1e9acaff0 100644 --- a/libraries/script-engine/src/JoystickInputController.h +++ b/interface/src/devices/Joystick.h @@ -1,5 +1,5 @@ // -// JoystickInputController.h +// Joystick.h // interface/src/devices // // Created by Stephen Birarda on 2014-09-23. @@ -9,13 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_JoystickInputController_h -#define hifi_JoystickInputController_h +#ifndef hifi_Joystick_h +#define hifi_Joystick_h #include #include -class JoystickInputController : public QObject { +#ifdef HAVE_SDL +#include +#undef main +#endif + +class Joystick : public QObject { Q_OBJECT Q_PROPERTY(QString name READ getName) @@ -23,28 +28,31 @@ class JoystickInputController : public QObject { Q_PROPERTY(int numAxes READ getNumAxes) Q_PROPERTY(QVector axes READ getAxes) public: - JoystickInputController(); - JoystickInputController(const QString& name, int numAxes, int numButtons); - JoystickInputController(const JoystickInputController& otherJoystickController); - JoystickInputController& operator=(const JoystickInputController& otherJoystickController); + Joystick(); + ~Joystick(); + +#ifdef HAVE_SDL + Joystick(const QString& name, SDL_Joystick* sdlJoystick); +#endif + + void update(); const QString& getName() const { return _name; } - void updateAxis(int index, float value) { _axes[index] = value; } - void updateButton(int index, bool isActive) { _buttons[index] = isActive; } - - const QVector& getAxes() const { return _axes; } - const QVector& getButtons() const { return _buttons; } - int getNumAxes() const { return _axes.size(); } int getNumButtons() const { return _buttons.size(); } +signals: + void axisValueChanged(int axis, float newValue, float oldValue); + void buttonStateChanged(int button, float newValue, float oldValue); private: - void swap(JoystickInputController& otherJoystickController); - QString _name; QVector _axes; QVector _buttons; + +#ifdef HAVE_SDL + SDL_Joystick* _sdlJoystick; +#endif }; #endif // hifi_JoystickTracker_h \ No newline at end of file diff --git a/interface/src/devices/JoystickManager.cpp b/interface/src/devices/JoystickManager.cpp deleted file mode 100644 index afcc2823ac..0000000000 --- a/interface/src/devices/JoystickManager.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// JoystickManager.cpp -// interface/src/devices -// -// Created by Andrzej Kapolka on 5/15/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 - -#include -#include - -#include - -#include "JoystickManager.h" - -using namespace std; - -JoystickManager::JoystickManager() { -#ifdef HAVE_SDL - SDL_Init(SDL_INIT_JOYSTICK); - int joystickCount = SDL_NumJoysticks(); - for (int i = 0; i < joystickCount; i++) { - SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i); - if (sdlJoystick) { - _sdlJoysticks.append(sdlJoystick); - - JoystickInputController controller(SDL_JoystickName(i), - SDL_JoystickNumAxes(sdlJoystick), SDL_JoystickNumButtons(sdlJoystick)); - _joysticks.append(controller); - } - } -#endif -} - -JoystickManager::~JoystickManager() { -#ifdef HAVE_SDL - foreach (SDL_Joystick* sdlJoystick, _sdlJoysticks) { - SDL_JoystickClose(sdlJoystick); - } - SDL_Quit(); -#endif -} - -void JoystickManager::update() { -#ifdef HAVE_SDL - PerformanceTimer perfTimer("joystick"); - SDL_JoystickUpdate(); - - for (int i = 0; i < _joysticks.size(); i++) { - SDL_Joystick* sdlJoystick = _sdlJoysticks.at(i); - JoystickInputController& joystick = _joysticks[i]; - for (int j = 0; j < joystick.getNumAxes(); j++) { - float value = glm::round(SDL_JoystickGetAxis(sdlJoystick, j) + 0.5f) / numeric_limits::max(); - const float DEAD_ZONE = 0.1f; - joystick.updateAxis(j, glm::abs(value) < DEAD_ZONE ? 0.0f : value); - } - for (int j = 0; j < joystick.getNumButtons(); j++) { - joystick.updateButton(j, SDL_JoystickGetButton(sdlJoystick, j)); - } - } -#endif -} diff --git a/interface/src/devices/JoystickManager.h b/interface/src/devices/JoystickManager.h deleted file mode 100644 index 31c2dc934b..0000000000 --- a/interface/src/devices/JoystickManager.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// JoystickManager.h -// interface/src/devices -// -// Created by Andrzej Kapolka on 5/15/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 -// - -#ifndef hifi_JoystickManager_h -#define hifi_JoystickManager_h - -#include -#include - -#ifdef HAVE_SDL -#include -#undef main -#endif - -#include "JoystickInputController.h" - -class JoystickState; - -/// Handles joystick input through SDL. -class JoystickManager : public QObject { - Q_OBJECT - -public: - JoystickManager(); - virtual ~JoystickManager(); - - QVector& getJoysticks() { return _joysticks; } - - void update(); - -private: - QVector _joysticks; - -#ifdef HAVE_SDL - QVector _sdlJoysticks; -#endif -}; - -#endif // hifi_JoystickManager_h diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index e8047b1ceb..f2e65a6e28 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -297,17 +297,6 @@ AbstractInputController* ControllerScriptingInterface::createInputController(con } } -JoystickInputController* ControllerScriptingInterface::joystick() const { - // stub to just return the first joystick - assume we only have one connected - QVector& activeJoysticks = Application::getInstance()->getJoystickManager()->getJoysticks(); - - if (activeJoysticks.size() > 0) { - return &activeJoysticks[0]; - } else { - return NULL; - } -} - void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) { _inputControllers.erase(input->getKey()); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index ada79e95cd..62ef2e9b24 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -113,7 +113,6 @@ public slots: /// Factory to create an InputController virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker); - JoystickInputController* joystick() const; virtual void releaseInputController(AbstractInputController* input); diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp new file mode 100644 index 0000000000..cf370cf0e9 --- /dev/null +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -0,0 +1,82 @@ +// +// JoystickManager.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/15/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 + +#ifdef HAVE_SDL +#include +#undef main +#endif + +#include + +#include "JoystickScriptingInterface.h" + +JoystickScriptingInterface& JoystickScriptingInterface::getInstance() { + static JoystickScriptingInterface sharedInstance; + return sharedInstance; +} + +JoystickScriptingInterface::JoystickScriptingInterface() : + _openJoysticks(), + _availableDeviceNames() +{ +#ifdef HAVE_SDL + SDL_Init(SDL_INIT_JOYSTICK); + + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + _availableDeviceNames << SDL_JoystickName(i); + } +#endif +} + +JoystickScriptingInterface::~JoystickScriptingInterface() { + qDeleteAll(_openJoysticks); + +#ifdef HAVE_SDL + SDL_Quit(); +#endif +} + +void JoystickScriptingInterface::update() { +#ifdef HAVE_SDL + PerformanceTimer perfTimer("JoystickScriptingInterface::update"); + SDL_JoystickUpdate(); + + foreach(Joystick* joystick, _openJoysticks) { + joystick->update(); + } + +#endif +} + +Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { + Joystick* matchingJoystick = _openJoysticks.value(name); + if (!matchingJoystick) { + // we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists + int joystickCount = SDL_NumJoysticks(); + + for (int i = 0; i < joystickCount; i++) { + if (SDL_JoystickName(i) == name) { + matchingJoystick = _openJoysticks.insert(name, new Joystick(name, SDL_JoystickOpen(i))).value(); + break; + } + } + + qDebug() << "No matching joystick found with name" << name << "- returning NULL pointer."; + } + + return matchingJoystick; +} + + diff --git a/interface/src/scripting/JoystickScriptingInterface.h b/interface/src/scripting/JoystickScriptingInterface.h new file mode 100644 index 0000000000..98e38f5698 --- /dev/null +++ b/interface/src/scripting/JoystickScriptingInterface.h @@ -0,0 +1,43 @@ +// +// JoystickScriptingInterface.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/15/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 +// + +#ifndef hifi_JoystickScriptingInterface_h +#define hifi_JoystickScriptingInterface_h + +#include +#include + +#include "devices/Joystick.h" + +/// Handles joystick input through SDL. +class JoystickScriptingInterface : public QObject { + Q_OBJECT + + Q_PROPERTY(QStringList availableJoystickNames READ getAvailableJoystickNames) +public: + static JoystickScriptingInterface& getInstance(); + + const QStringList& getAvailableJoystickNames() const { return _availableDeviceNames; } + + void update(); + +public slots: + Joystick* joystickWithName(const QString& name); + +private: + JoystickScriptingInterface(); + ~JoystickScriptingInterface(); + + QMap _openJoysticks; + QStringList _availableDeviceNames; +}; + +#endif // hifi_JoystickScriptingInterface_h diff --git a/libraries/script-engine/src/JoystickInputController.cpp b/libraries/script-engine/src/JoystickInputController.cpp deleted file mode 100644 index fedb3a7643..0000000000 --- a/libraries/script-engine/src/JoystickInputController.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// -// JoystickInputController.cpp -// interface/src/devices -// -// Created by Stephen Birarda on 2014-09-23. -// 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 - -#include "JoystickInputController.h" - -JoystickInputController::JoystickInputController() : - _name(), - _axes(0), - _buttons(0) -{ - -} - -JoystickInputController::JoystickInputController(const QString& name, int numAxes, int numButtons) : - _name(name), - _axes(numAxes), - _buttons(numButtons) -{ - -} - -JoystickInputController::JoystickInputController(const JoystickInputController& otherJoystickController) : - _name(otherJoystickController._name), - _axes(otherJoystickController._axes), - _buttons(otherJoystickController._buttons) -{ - -} - -JoystickInputController& JoystickInputController::operator=(const JoystickInputController& otherJoystickController) { - JoystickInputController temp(otherJoystickController); - swap(temp); - return *this; -} - -void JoystickInputController::swap(JoystickInputController& otherJoystickController) { - using std::swap; - - swap(_name, otherJoystickController._name); - swap(_axes, otherJoystickController._axes); - swap(_buttons, otherJoystickController._buttons); -} diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0ab5f37f29..545e4709f9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -35,7 +35,6 @@ #include "AnimationObject.h" #include "ArrayBufferViewClass.h" #include "DataViewClass.h" -#include "JoystickInputController.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -81,14 +80,6 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon out = qobject_cast(object.toQObject()); } -QScriptValue joystickToScriptValue(QScriptEngine *engine, JoystickInputController* const &in) { - return engine->newQObject(in); -} - -void joystickFromScriptValue(const QScriptValue &object, JoystickInputController* &out) { - out = qobject_cast(object.toQObject()); -} - ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface) : @@ -287,7 +278,6 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); - qScriptRegisterMetaType(this, joystickToScriptValue, joystickFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); From d50c81e4a1f952cbda71fec369537014734a4332 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Sep 2014 17:10:25 -0700 Subject: [PATCH 06/18] add a property for number of buttons in Joystick --- interface/src/devices/Joystick.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/Joystick.h b/interface/src/devices/Joystick.h index c1e9acaff0..63386db5c5 100644 --- a/interface/src/devices/Joystick.h +++ b/interface/src/devices/Joystick.h @@ -26,7 +26,7 @@ class Joystick : public QObject { Q_PROPERTY(QString name READ getName) Q_PROPERTY(int numAxes READ getNumAxes) - Q_PROPERTY(QVector axes READ getAxes) + Q_PROPERTY(int numButtons READ getNumButtons) public: Joystick(); ~Joystick(); From 13d3eb02e30b47b39c461f3e1f2e00295db0122e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Sep 2014 17:19:57 -0700 Subject: [PATCH 07/18] report the 360 axis values in xbox.js --- examples/xbox.js | 10 ++++++---- interface/src/Application.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/xbox.js b/examples/xbox.js index 9190ee23ac..603e0dbf56 100644 --- a/examples/xbox.js +++ b/examples/xbox.js @@ -10,8 +10,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -joysticks = Joysticks.availableJoystickNames; +gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); -for (i = 0; i < joysticks.length; i++) { - print("Joystick " + i + " is " + joysticks[i]); -} \ No newline at end of file +function reportAxisValue(axis, newValue, oldValue) { + print("The value for axis " + axis + " has changed to " + newValue + ". It was " + oldValue); +} + +gamepad.axisValueChanged.connect(reportAxisValue); \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c49a1e234a..25f2bd6817 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3766,6 +3766,14 @@ void Application::saveScripts() { _settings->endArray(); } +QScriptValue joystickToScriptValue(QScriptEngine *engine, Joystick* const &in) { + return engine->newQObject(in); +} + +void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) { + out = qobject_cast(object.toQObject()); +} + ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow) { QUrl scriptUrl(scriptFilename); @@ -3850,6 +3858,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser scriptEngine->registerGlobalObject("AvatarManager", &_avatarManager); scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance()); + qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue); #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); From 9348888ae5a2498b5adcced641a73ef99f1cc7e8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 23 Sep 2014 17:42:48 -0700 Subject: [PATCH 08/18] map PrioVR to new JoystickScriptingInterface --- interface/src/devices/Joystick.h | 3 +++ interface/src/devices/PrioVR.cpp | 23 ++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/interface/src/devices/Joystick.h b/interface/src/devices/Joystick.h index 63386db5c5..228e993204 100644 --- a/interface/src/devices/Joystick.h +++ b/interface/src/devices/Joystick.h @@ -39,6 +39,9 @@ public: const QString& getName() const { return _name; } + const QVector& getAxes() const { return _axes; } + const QVector& getButtons() const { return _buttons; } + int getNumAxes() const { return _axes.size(); } int getNumButtons() const { return _buttons.size(); } diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 30c5d74ce1..e810f9e370 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -17,6 +17,7 @@ #include "Application.h" #include "PrioVR.h" +#include "scripting/JoystickScriptingInterface.h" #include "ui/TextRenderer.h" #ifdef HAVE_PRIOVR @@ -61,18 +62,22 @@ static void setPalm(float deltaTime, int index) { palm->setActive(true); // Read controller buttons and joystick into the hand - if (!Application::getInstance()->getJoystickManager()->getJoysticks().isEmpty()) { - const JoystickState& state = Application::getInstance()->getJoystickManager()->getJoystickStates().at(0); - if (state.axes.size() >= 4 && state.buttons.size() >= 4) { + const QString PRIO_JOYSTICK_NAME = "PrioVR"; + Joystick* prioJoystick = JoystickScriptingInterface::getInstance().joystickWithName(PRIO_JOYSTICK_NAME); + if (prioJoystick) { + const QVector axes = prioJoystick->getAxes(); + const QVector buttons = prioJoystick->getButtons(); + + if (axes.size() >= 4 && buttons.size() >= 4) { if (index == LEFT_HAND_INDEX) { - palm->setControllerButtons(state.buttons.at(1) ? BUTTON_FWD : 0); - palm->setTrigger(state.buttons.at(0) ? 1.0f : 0.0f); - palm->setJoystick(state.axes.at(0), -state.axes.at(1)); + palm->setControllerButtons(buttons[1] ? BUTTON_FWD : 0); + palm->setTrigger(buttons[0] ? 1.0f : 0.0f); + palm->setJoystick(axes[0], -axes[1]); } else { - palm->setControllerButtons(state.buttons.at(3) ? BUTTON_FWD : 0); - palm->setTrigger(state.buttons.at(2) ? 1.0f : 0.0f); - palm->setJoystick(state.axes.at(2), -state.axes.at(3)); + palm->setControllerButtons(buttons[3] ? BUTTON_FWD : 0); + palm->setTrigger(buttons[2] ? 1.0f : 0.0f); + palm->setJoystick(axes[2], -axes[3]); } } } From 875c1dba724ae1995240ed85dace255accb18de3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 24 Sep 2014 08:40:39 -0700 Subject: [PATCH 09/18] change name in header comment to match new class name --- interface/src/scripting/JoystickScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp index cf370cf0e9..8911f292b5 100644 --- a/interface/src/scripting/JoystickScriptingInterface.cpp +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -1,5 +1,5 @@ // -// JoystickManager.cpp +// JoystickScriptingInterface.cpp // interface/src/devices // // Created by Andrzej Kapolka on 5/15/14. From 3ab24d6ea56b90ff23fb3c75eab67bbef6401fbb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 24 Sep 2014 11:59:44 -0700 Subject: [PATCH 10/18] add a missing HAVE_SDL block --- interface/src/scripting/JoystickScriptingInterface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/scripting/JoystickScriptingInterface.cpp b/interface/src/scripting/JoystickScriptingInterface.cpp index 8911f292b5..1e35c11f61 100644 --- a/interface/src/scripting/JoystickScriptingInterface.cpp +++ b/interface/src/scripting/JoystickScriptingInterface.cpp @@ -62,6 +62,7 @@ void JoystickScriptingInterface::update() { Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { Joystick* matchingJoystick = _openJoysticks.value(name); +#ifdef HAVE_SDL if (!matchingJoystick) { // we haven't opened a joystick with this name yet - enumerate our SDL devices and see if it exists int joystickCount = SDL_NumJoysticks(); @@ -75,6 +76,7 @@ Joystick* JoystickScriptingInterface::joystickWithName(const QString& name) { qDebug() << "No matching joystick found with name" << name << "- returning NULL pointer."; } +#endif return matchingJoystick; } From 11e60c864a5b9e326f018ad8e3fd234dee968be0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 24 Sep 2014 15:17:23 -0700 Subject: [PATCH 11/18] Changed recording file format --- libraries/avatars/src/Recording.cpp | 61 +++++++++++++++++------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index fb3ee2b5e7..b34391d580 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -32,7 +32,7 @@ static const int MAGIC_NUMBER_SIZE = 8; static const char MAGIC_NUMBER[MAGIC_NUMBER_SIZE] = {17, 72, 70, 82, 13, 10, 26, 10}; // Version (Major, Minor) -static const QPair VERSION(0, 1); +static const QPair VERSION(0, 2); int SCALE_RADIX = 10; int BLENDSHAPE_RADIX = 15; @@ -118,12 +118,6 @@ bool readQuat(QDataStream& stream, glm::quat& value) { return true; } -void writeFloat(QDataStream& stream, float value, int radix) { - unsigned char buffer[256]; - int writtenToBuffer = packFloatScalarToSignedTwoByteFixed(buffer, value, radix); - stream.writeRawData(reinterpret_cast(buffer), writtenToBuffer); -} - bool readFloat(QDataStream& stream, float& value, int radix) { int floatByteSize = 2; // 1 floats * 2 bytes int16_t buffer[256]; @@ -185,7 +179,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { // Orientation writeQuat(fileStream, context.orientation); // Scale - writeFloat(fileStream, context.scale, SCALE_RADIX); + fileStream << context.scale; // Head model fileStream << context.headModel; // Skeleton model @@ -204,7 +198,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { // Orientation writeQuat(fileStream, data.rotation); // Scale - writeFloat(fileStream, data.scale, SCALE_RADIX); + fileStream << data.scale; } // RECORDING @@ -231,7 +225,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { for (quint32 j = 0; j < numBlendshapes; ++j) { if (i == 0 || frame._blendshapeCoefficients[j] != previousFrame._blendshapeCoefficients[j]) { - writeFloat(stream, frame.getBlendshapeCoefficients()[j], BLENDSHAPE_RADIX); + stream << frame.getBlendshapeCoefficients()[j]; mask.setBit(maskIndex); } ++maskIndex; @@ -277,7 +271,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._scale != previousFrame._scale) { - writeFloat(stream, frame._scale, SCALE_RADIX); + stream << frame._scale; mask.setBit(maskIndex); } maskIndex++; @@ -297,7 +291,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._leanSideways != previousFrame._leanSideways) { - writeFloat(stream, frame._leanSideways, LEAN_RADIX); + stream << frame._leanSideways; mask.setBit(maskIndex); } maskIndex++; @@ -307,7 +301,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.resize(mask.size() + 1); } if (i == 0 || frame._leanForward != previousFrame._leanForward) { - writeFloat(stream, frame._leanForward, LEAN_RADIX); + stream << frame._leanForward; mask.setBit(maskIndex); } maskIndex++; @@ -438,7 +432,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString QPair version; fileStream >> version; // File format version - if (version != VERSION) { + if (version != VERSION && version != QPair(0,1)) { qDebug() << "ERROR: This file format version is not supported."; return recording; } @@ -484,10 +478,10 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } // Scale - if (!readFloat(fileStream, context.scale, SCALE_RADIX)) { - qDebug() << "Couldn't read file correctly. (Invalid float)"; - recording.clear(); - return recording; + if (version == QPair(0,1)) { + readFloat(fileStream, context.scale, SCALE_RADIX); + } else { + fileStream >> context.scale; } // Head model fileStream >> context.headModel; @@ -519,9 +513,10 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } // Scale - if (!readFloat(fileStream, data.scale, SCALE_RADIX)) { - qDebug() << "Couldn't read attachment correctly. (Invalid float)"; - continue; + if (version == QPair(0,1)) { + readFloat(fileStream, data.scale, SCALE_RADIX); + } else { + fileStream >> data.scale; } context.attachments << data; } @@ -548,8 +543,12 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } frame._blendshapeCoefficients.resize(numBlendshapes); for (quint32 j = 0; j < numBlendshapes; ++j) { - if (!mask[maskIndex++] || !readFloat(stream, frame._blendshapeCoefficients[j], BLENDSHAPE_RADIX)) { + if (!mask[maskIndex++]) { frame._blendshapeCoefficients[j] = previousFrame._blendshapeCoefficients[j]; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._blendshapeCoefficients[j], BLENDSHAPE_RADIX); + } else { + stream >> frame._blendshapeCoefficients[j]; } } // Joint Rotations @@ -571,20 +570,32 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString frame._rotation = previousFrame._rotation; } - if (!mask[maskIndex++] || !readFloat(stream, frame._scale, SCALE_RADIX)) { + if (!mask[maskIndex++]) { frame._scale = previousFrame._scale; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._scale, SCALE_RADIX); + } else { + stream >> frame._scale; } if (!mask[maskIndex++] || !readQuat(stream, frame._headRotation)) { frame._headRotation = previousFrame._headRotation; } - if (!mask[maskIndex++] || !readFloat(stream, frame._leanSideways, LEAN_RADIX)) { + if (!mask[maskIndex++]) { frame._leanSideways = previousFrame._leanSideways; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._leanSideways, LEAN_RADIX); + } else { + stream >> frame._leanSideways; } - if (!mask[maskIndex++] || !readFloat(stream, frame._leanForward, LEAN_RADIX)) { + if (!mask[maskIndex++]) { frame._leanForward = previousFrame._leanForward; + } else if (version == QPair(0,1)) { + readFloat(stream, frame._leanForward, LEAN_RADIX); + } else { + stream >> frame._leanForward; } if (!mask[maskIndex++] || !readVec3(stream, frame._lookAtPosition)) { From f43ba4a68b4806584d89328deb34b6425d4bf34b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 15:28:04 -0700 Subject: [PATCH 12/18] first cut at getting entities to collide --- interface/src/Application.cpp | 6 + interface/src/Application.h | 2 + .../entities/src/EntityCollisionSystem.cpp | 351 ++++++++++++++++++ .../entities/src/EntityCollisionSystem.h | 73 ++++ libraries/entities/src/EntityItem.cpp | 81 +++- libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityItemProperties.cpp | 2 +- libraries/entities/src/EntityItemProperties.h | 22 +- libraries/octree/src/Octree.cpp | 6 +- libraries/particles/src/Particle.cpp | 3 + tests/octree/CMakeLists.txt | 2 +- 11 files changed, 541 insertions(+), 11 deletions(-) create mode 100644 libraries/entities/src/EntityCollisionSystem.cpp create mode 100644 libraries/entities/src/EntityCollisionSystem.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f2ae7a2d8b..974fee4cab 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1822,6 +1822,8 @@ void Application::init() { _entities.init(); _entities.setViewFrustum(getViewFrustum()); + _entityCollisionSystem.init(&_entityEditSender, _entities.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + _entityClipboardRenderer.init(); _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); @@ -2189,6 +2191,10 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("entities"); _entities.update(); // update the models... + { + PerformanceTimer perfTimer("collisions"); + _entityCollisionSystem.update(); // collide the entities... + } } { diff --git a/interface/src/Application.h b/interface/src/Application.h index d17bacd413..49e9cab57a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -482,6 +483,7 @@ private: ParticleCollisionSystem _particleCollisionSystem; EntityTreeRenderer _entities; + EntityCollisionSystem _entityCollisionSystem; EntityTreeRenderer _entityClipboardRenderer; EntityTree _entityClipboard; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp new file mode 100644 index 0000000000..5a4e18c67a --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -0,0 +1,351 @@ +// +// EntityCollisionSystem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-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 +#include +#include +#include +#include +#include + +#include "EntityItem.h" +#include "EntityCollisionSystem.h" +#include "EntityEditPacketSender.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +const int MAX_COLLISIONS_PER_Entity = 16; + +EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) { + init(packetSender, Entities, voxels, audio, avatars); +} + +void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { + _packetSender = packetSender; + _entities = Entities; + _voxels = voxels; + _audio = audio; + _avatars = avatars; +} + +EntityCollisionSystem::~EntityCollisionSystem() { +} + +bool EntityCollisionSystem::updateOperation(OctreeElement* element, void* extraData) { + EntityCollisionSystem* system = static_cast(extraData); + EntityTreeElement* entityTreeElement = static_cast(element); + + // iterate the Entities... + QList& entities = entityTreeElement->getEntities(); + uint16_t numberOfEntities = entities.size(); + for (uint16_t i = 0; i < numberOfEntities; i++) { + EntityItem* entity = entities[i]; + system->checkEntity(entity); + } + + return true; +} + + +void EntityCollisionSystem::update() { + // update all Entities + if (_entities->tryLockForRead()) { + _entities->recurseTreeWithOperation(updateOperation, this); + _entities->unlock(); + } +} + + +void EntityCollisionSystem::checkEntity(EntityItem* entity) { + updateCollisionWithVoxels(entity); + updateCollisionWithEntities(entity); + updateCollisionWithAvatars(entity); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithVoxel(EntityItem* entity, + VoxelDetail* voxelDetails, const CollisionInfo& collision) { + EntityItemID entityItemID = entity->getEntityItemID(); + emit EntityCollisionWithVoxel(entityItemID, *voxelDetails, collision); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA, + EntityItem* entityB, const CollisionInfo& collision) { + + EntityItemID idA = entityA->getEntityItemID(); + EntityItemID idB = entityB->getEntityItemID(); + emit EntityCollisionWithEntity(idA, idB, collision); +} + +void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.4f; + const float DAMPING = 0.05f; + const float COLLISION_FREQUENCY = 0.5f; + CollisionInfo collisionInfo; + collisionInfo._damping = DAMPING; + collisionInfo._elasticity = ELASTICITY; + VoxelDetail* voxelDetails = NULL; + if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) { + + // let the Entities run their collision scripts if they have them + //entity->collisionWithVoxel(voxelDetails, collisionInfo._penetration); + + // findSpherePenetration() only computes the penetration but we also want some other collision info + // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding + // the results to systems outside of this octree reference frame. + updateCollisionSound(entity, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration)); + // let the global script run their collision scripts for Entities if they have them + emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo); + + // we must scale back down to the octree reference frame before updating the Entity properties + collisionInfo._penetration /= (float)(TREE_SCALE); + collisionInfo._contactPoint /= (float)(TREE_SCALE); + entity->applyHardCollision(collisionInfo); + queueEntityPropertiesUpdate(entity); + + delete voxelDetails; // cleanup returned details + } +} + +void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { + glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); + float radius = entityA->getRadius() * (float)(TREE_SCALE); + //const float ELASTICITY = 0.4f; + //const float DAMPING = 0.0f; + const float COLLISION_FREQUENCY = 0.5f; + glm::vec3 penetration; + EntityItem* entityB; + if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { + // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. + +qDebug() << "penetration:" << penetration << "in meters"; + + glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); + +qDebug() << "penetrationInTreeUnits:" << penetrationInTreeUnits; + + // Even if the Entities overlap... when the Entities are already moving appart + // we don't want to count this as a collision. + glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); + +qDebug() << "A old velocity:" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "B old velocity:" << entityB->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "relativeVelocity:" << relativeVelocity * (float)TREE_SCALE << "in meters"; + + bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; + bool doCollisions = true; + + if (doCollisions) { + //entityA->collisionWithEntity(entityB, penetration); + //entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed + + CollisionInfo collision; + collision._penetration = penetration; + // for now the contactPoint is the average between the the two paricle centers + collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); + //emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); + + glm::vec3 axis = glm::normalize(penetration); + glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; + + float massA = entityA->getMass(); + float massB = entityB->getMass(); + float totalMass = massA + massB; +qDebug() << "massA:" << massA; +qDebug() << "massB:" << massB; +qDebug() << "totalMass:" << totalMass; + + // handle Entity A + +qDebug() << "OLD - VALUES - entityA ----"; +qDebug() << "entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; +qDebug() << "entityA->getVelocity():" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; + + + glm::vec3 newVelocity = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); + glm::vec3 newPosition = entityA->getPosition() - 0.5f * penetrationInTreeUnits; + +qDebug() << "A new Velocity:" << newVelocity * (float)TREE_SCALE << "in meters"; +qDebug() << "A new Position:" << newPosition * (float)TREE_SCALE << "in meters"; + + EntityItemProperties propertiesA = entityA->getProperties(); + +qDebug() << "propertiesA = entityA->getProperties() --- BEFORE SETTING ----"; +qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; +qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; +qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); + + EntityItemID idA(entityA->getID()); + propertiesA.setVelocity(newVelocity * (float)TREE_SCALE); + propertiesA.setPosition(newPosition * (float)TREE_SCALE); + +qDebug() << "updateEntity(idA, propertiesA)...."; +qDebug() << " idA:" << idA; +qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; +qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; +qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); + + _entities->updateEntity(idA, propertiesA); + +qDebug() << "AFTER updateEntity()...."; +qDebug() << " entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; +qDebug() << " entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; + + + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); + //queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties); + +qDebug() << "OLD - VALUES - entityB ----"; +qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; +qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; + + glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass); + glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; + +qDebug() << "B new Velocity:" << newVelocityB << "no scaling"; +qDebug() << "B new Position:" << newPositionB * (float)TREE_SCALE << "in meters"; + + EntityItemProperties propertiesB = entityB->getProperties(); + +qDebug() << "propertiesB = entityB->getProperties()"; +qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition() << "not scaled, should be meters"; +qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity() << "not scaled, should be ???"; +qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); + + EntityItemID idB(entityB->getID()); + propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); + propertiesB.setPosition(newPositionB * (float)TREE_SCALE); + +qDebug() << "updateEntity(idB, propertiesB)...."; +qDebug() << " idB:" << idB; +qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition(); +qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity(); +qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); + + _entities->updateEntity(idB, propertiesB); + +qDebug() << "AFTER updateEntity()...."; +qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; +qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; + + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); + //_packetSender->releaseQueuedMessages(); + + //updateCollisionSound(entityA, penetration, COLLISION_FREQUENCY); + } + } +} + +void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { + +#if 0 + // Entities that are in hand, don't collide with avatars + if (!_avatars) { + return; + } + + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.9f; + const float DAMPING = 0.1f; + const float COLLISION_FREQUENCY = 0.5f; + glm::vec3 penetration; + + _collisions.clear(); + foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { + AvatarData* avatar = avatarPointer.data(); + + float totalRadius = avatar->getBoundingRadius() + radius; + glm::vec3 relativePosition = center - avatar->getPosition(); + if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) { + continue; + } + + if (avatar->findSphereCollisions(center, radius, _collisions)) { + int numCollisions = _collisions.size(); + for (int i = 0; i < numCollisions; ++i) { + CollisionInfo* collision = _collisions.getCollision(i); + collision->_damping = DAMPING; + collision->_elasticity = ELASTICITY; + + collision->_addedVelocity /= (float)(TREE_SCALE); + glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity(); + + if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { + // only collide when Entity and collision point are moving toward each other + // (doing this prevents some "collision snagging" when Entity penetrates the object) + updateCollisionSound(entity, collision->_penetration, COLLISION_FREQUENCY); + collision->_penetration /= (float)(TREE_SCALE); + entity->applyHardCollision(*collision); + queueEntityPropertiesUpdate(entity); + } + } + } + } + +#endif +} + +void EntityCollisionSystem::queueEntityPropertiesUpdate(EntityItem* entity) { + // queue the result for sending to the Entity server + EntityItemProperties properties = entity->getProperties(); + EntityItemID entityItemID(entity->getID()); + + properties.setPosition(entity->getPosition() * (float)TREE_SCALE); + properties.setVelocity(entity->getVelocity() * (float)TREE_SCALE); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); +} + + +void EntityCollisionSystem::updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency) { + + // consider whether to have the collision make a sound + const float AUDIBLE_COLLISION_THRESHOLD = 0.3f; + const float COLLISION_LOUDNESS = 1.f; + const float DURATION_SCALING = 0.004f; + const float NOISE_SCALING = 0.1f; + glm::vec3 velocity = Entity->getVelocity() * (float)(TREE_SCALE); + + /* + // how do we want to handle this?? + // + glm::vec3 gravity = Entity->getGravity() * (float)(TREE_SCALE); + + if (glm::length(gravity) > EPSILON) { + // If gravity is on, remove the effect of gravity on velocity for this + // frame, so that we are not constantly colliding with the surface + velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); + } + */ + float normalSpeed = glm::dot(velocity, glm::normalize(penetration)); + // NOTE: it is possible for normalSpeed to be NaN at this point + // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized) + // however the check below will fail (NaN comparisons always fail) and everything will be fine. + + if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) { + // Volume is proportional to collision velocity + // Base frequency is modified upward by the angle of the collision + // Noise is a function of the angle of collision + // Duration of the sound is a function of both base frequency and velocity of impact + float tangentialSpeed = glm::length(velocity) - normalSpeed; + _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), + frequency * (1.f + tangentialSpeed / normalSpeed), + std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); + } +} diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h new file mode 100644 index 0000000000..fe38209c09 --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -0,0 +1,73 @@ +// +// EntityCollisionSystem.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-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_EntityCollisionSystem_h +#define hifi_EntityCollisionSystem_h + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "EntityItem.h" + +class AbstractAudioInterface; +class AvatarData; +class EntityEditPacketSender; +class EntityTree; +class VoxelTree; + +class EntityCollisionSystem : public QObject { +Q_OBJECT +public: + EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL, + VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, + AvatarHashMap* avatars = NULL); + + void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels, + AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL); + + ~EntityCollisionSystem(); + + void update(); + + void checkEntity(EntityItem* Entity); + void updateCollisionWithVoxels(EntityItem* Entity); + void updateCollisionWithEntities(EntityItem* Entity); + void updateCollisionWithAvatars(EntityItem* Entity); + void queueEntityPropertiesUpdate(EntityItem* Entity); + void updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency); + +signals: + void EntityCollisionWithVoxel(const EntityItemID& entityItemID, const VoxelDetail& voxel, const CollisionInfo& penetration); + void EntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& penetration); + +private: + static bool updateOperation(OctreeElement* element, void* extraData); + void emitGlobalEntityCollisionWithVoxel(EntityItem* Entity, VoxelDetail* voxelDetails, const CollisionInfo& penetration); + void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration); + + EntityEditPacketSender* _packetSender; + EntityTree* _entities; + VoxelTree* _voxels; + AbstractAudioInterface* _audio; + AvatarHashMap* _avatars; + CollisionList _collisions; +}; + +#endif // hifi_EntityCollisionSystem_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1406239f23..8a0a92c6a5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -535,6 +535,10 @@ bool EntityItem::isRestingOnSurface() const { void EntityItem::update(const quint64& updateTime) { bool wantDebug = false; + + if (_lastUpdated == 0) { + _lastUpdated = updateTime; + } float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND); @@ -578,7 +582,7 @@ void EntityItem::update(const quint64& updateTime) { _lastUpdated = updateTime; if (wantDebug) { - qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; + qDebug() << " ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; } if (hasAngularVelocity()) { @@ -614,7 +618,7 @@ void EntityItem::update(const quint64& updateTime) { glm::vec3 newPosition = position + (velocity * timeElapsed); if (wantDebug) { - qDebug() << "EntityItem::update()...."; + qDebug() << " EntityItem::update()...."; qDebug() << " timeElapsed:" << timeElapsed; qDebug() << " old AACube:" << getMaximumAACube(); qDebug() << " old position:" << position; @@ -899,3 +903,76 @@ float EntityItem::getRadius() const { return radius; } +void EntityItem::applyHardCollision(const CollisionInfo& collisionInfo) { + // HALTING_* params are determined using expected acceleration of gravity over some timescale. + // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal. + const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second + const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE); + + // + // Update the entity in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) + // if elasticity = 1.0, collision is 100% elastic. + // + glm::vec3 position = getPosition(); + glm::vec3 velocity = getVelocity(); + + const float EPSILON = 0.0f; + glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; + float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); + if (velocityDotPenetration < EPSILON) { + // entity is moving into collision surface + // + // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing + // (other's mass could be stored in the Collision Info). The smaller mass should surrender more + // position offset and should slave more to the other's velocity in the static-friction case. + position -= collisionInfo._penetration; + + if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) { + // static friction kicks in and entities moves with colliding object + velocity = collisionInfo._addedVelocity; + } else { + glm::vec3 direction = glm::normalize(collisionInfo._penetration); + velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection + velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction + } + } + + // TODO: how do we handle fixing up the location in the octree cells? + // change the local entity too... + setPosition(position); + setVelocity(velocity); +} + +void EntityItem::collisionWithEntity(EntityItem* other, const glm::vec3& penetration) { + // TODO: how do we want to handle collision related scripts for entities? + /* + // Only run this particle script if there's a script attached directly to the entity. + if (!_script.isEmpty()) { + ScriptEngine engine(_script); + ParticleScriptObject particleScriptable(this); + startParticleScriptContext(engine, particleScriptable); + ParticleScriptObject otherParticleScriptable(other); + particleScriptable.emitCollisionWithParticle(&otherParticleScriptable, penetration); + endParticleScriptContext(engine, particleScriptable); + } + */ +} + +void EntityItem::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { + // TODO: how do we want to handle collision related scripts for entities? + /* + // Only run this entity script if there's a script attached directly to the entity. + if (!_script.isEmpty()) { + ScriptEngine engine(_script); + ParticleScriptObject particleScriptable(this); + startParticleScriptContext(engine, particleScriptable); + particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); + endParticleScriptContext(engine, particleScriptable); + } + */ +} + + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a41d4523f9..7b03c786ed 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -20,6 +20,7 @@ #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include +#include #include "EntityItemID.h" #include "EntityItemProperties.h" @@ -227,6 +228,9 @@ public: // TODO: We need to get rid of these users of getRadius()... float getRadius() const; + void applyHardCollision(const CollisionInfo& collisionInfo); + void collisionWithEntity(EntityItem* other, const glm::vec3& penetration); + void collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration); protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f2f2483bb4..facc7e5448 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() : _id(UNKNOWN_ENTITY_ID), _idSet(false), - _lastEdited(0), + _lastEdited(usecTimestampNow()), _created(UNKNOWN_CREATED_TIME), _type(EntityTypes::Unknown), diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a52a75e071..40e76e1e34 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -90,6 +90,8 @@ public: // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } + float getEditedAgo() const /// Elapsed seconds since this entity was last edited + { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } EntityPropertyFlags getChangedProperties() const; /// used by EntityScriptingInterface to return EntityItemProperties for unknown models @@ -121,14 +123,14 @@ public: float getMass() const { return _mass; } void setMass(float value) { _mass = value; _massChanged = true; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second const glm::vec3& getVelocity() const { return _velocity; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared const glm::vec3& getGravity() const { return _gravity; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; } float getDamping() const { return _damping; } @@ -285,4 +287,16 @@ Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); + +inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { + debug << "EntityItemProperties[" << "\n" + << " position:" << properties.getPosition() << "in meters" << "\n" + << " velocity:" << properties.getVelocity() << "in meters" << "\n" + << " last edited:" << properties.getLastEdited() << "\n" + << " edited ago:" << properties.getEditedAgo() << "\n" + << "]"; + + return debug; +} + #endif // hifi_EntityItemProperties_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 1a1412d305..df2e40d96f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -732,9 +732,6 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (!box.expandedContains(args->center, args->radius)) { return false; } - if (!element->isLeaf()) { - return true; // recurse on children - } if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { @@ -744,6 +741,9 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { args->found = true; } } + if (!element->isLeaf()) { + return true; // recurse on children + } return false; } diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index e3b568365b..c7a94985cf 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -930,6 +930,9 @@ void Particle::applyHardCollision(const CollisionInfo& collisionInfo) { } void Particle::update(const quint64& now) { + if (_lastUpdated == 0) { + _lastUpdated = now; + } float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND); _lastUpdated = now; diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 45277d6165..4907298a3c 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project(Script Network) include_glm() # link in the shared libraries -link_hifi_libraries(animation fbx entities networking octree shared) +link_hifi_libraries(animation avatars fbx entities networking octree shared voxels) link_shared_dependencies() From 10865944f0981959a6d276d8ed99edd2bf2378e3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 15:44:09 -0700 Subject: [PATCH 13/18] removed some debug --- .../entities/src/EntityCollisionSystem.cpp | 81 +++---------------- 1 file changed, 11 insertions(+), 70 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 5a4e18c67a..0db409a954 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -131,33 +131,24 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { EntityItem* entityB; if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. - -qDebug() << "penetration:" << penetration << "in meters"; - glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); -qDebug() << "penetrationInTreeUnits:" << penetrationInTreeUnits; - // Even if the Entities overlap... when the Entities are already moving appart // we don't want to count this as a collision. glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); -qDebug() << "A old velocity:" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; -qDebug() << "B old velocity:" << entityB->getVelocity() * (float)TREE_SCALE << "in meters"; -qDebug() << "relativeVelocity:" << relativeVelocity * (float)TREE_SCALE << "in meters"; - bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; bool doCollisions = true; if (doCollisions) { - //entityA->collisionWithEntity(entityB, penetration); - //entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed + entityA->collisionWithEntity(entityB, penetration); + entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed CollisionInfo collision; collision._penetration = penetration; // for now the contactPoint is the average between the the two paricle centers collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); - //emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); + emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); glm::vec3 axis = glm::normalize(penetration); glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; @@ -165,85 +156,33 @@ qDebug() << "relativeVelocity:" << relativeVelocity * (float)TREE_SCALE << "in m float massA = entityA->getMass(); float massB = entityB->getMass(); float totalMass = massA + massB; -qDebug() << "massA:" << massA; -qDebug() << "massB:" << massB; -qDebug() << "totalMass:" << totalMass; // handle Entity A - -qDebug() << "OLD - VALUES - entityA ----"; -qDebug() << "entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; -qDebug() << "entityA->getVelocity():" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; -qDebug() << "entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; - - - glm::vec3 newVelocity = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); - glm::vec3 newPosition = entityA->getPosition() - 0.5f * penetrationInTreeUnits; - -qDebug() << "A new Velocity:" << newVelocity * (float)TREE_SCALE << "in meters"; -qDebug() << "A new Position:" << newPosition * (float)TREE_SCALE << "in meters"; + glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); + glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits; EntityItemProperties propertiesA = entityA->getProperties(); - -qDebug() << "propertiesA = entityA->getProperties() --- BEFORE SETTING ----"; -qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; -qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; -qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); - EntityItemID idA(entityA->getID()); - propertiesA.setVelocity(newVelocity * (float)TREE_SCALE); - propertiesA.setPosition(newPosition * (float)TREE_SCALE); - -qDebug() << "updateEntity(idA, propertiesA)...."; -qDebug() << " idA:" << idA; -qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; -qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; -qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); + propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); + propertiesA.setPosition(newPositionA * (float)TREE_SCALE); _entities->updateEntity(idA, propertiesA); - -qDebug() << "AFTER updateEntity()...."; -qDebug() << " entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; -qDebug() << " entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; - - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); - //queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties); - -qDebug() << "OLD - VALUES - entityB ----"; -qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; -qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass); glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; -qDebug() << "B new Velocity:" << newVelocityB << "no scaling"; -qDebug() << "B new Position:" << newPositionB * (float)TREE_SCALE << "in meters"; - EntityItemProperties propertiesB = entityB->getProperties(); -qDebug() << "propertiesB = entityB->getProperties()"; -qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition() << "not scaled, should be meters"; -qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity() << "not scaled, should be ???"; -qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); - EntityItemID idB(entityB->getID()); propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); propertiesB.setPosition(newPositionB * (float)TREE_SCALE); -qDebug() << "updateEntity(idB, propertiesB)...."; -qDebug() << " idB:" << idB; -qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition(); -qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity(); -qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); - _entities->updateEntity(idB, propertiesB); -qDebug() << "AFTER updateEntity()...."; -qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; -qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); + + // TODO: Do we need this? //_packetSender->releaseQueuedMessages(); //updateCollisionSound(entityA, penetration, COLLISION_FREQUENCY); @@ -252,6 +191,8 @@ qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TRE } void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { + + // TODO: implement support for colliding with avatars #if 0 // Entities that are in hand, don't collide with avatars From 8d9a923add7b403a10e32fa4dc6d0c942565fffa Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 16:04:39 -0700 Subject: [PATCH 14/18] fix the collision last edited behavior correctly, delete collision sound code --- .../entities/src/EntityCollisionSystem.cpp | 54 ++----------------- .../entities/src/EntityItemProperties.cpp | 2 +- libraries/entities/src/EntityItemProperties.h | 3 +- 3 files changed, 8 insertions(+), 51 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 0db409a954..688ce70345 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -93,7 +93,6 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { float radius = entity->getRadius() * (float)(TREE_SCALE); const float ELASTICITY = 0.4f; const float DAMPING = 0.05f; - const float COLLISION_FREQUENCY = 0.5f; CollisionInfo collisionInfo; collisionInfo._damping = DAMPING; collisionInfo._elasticity = ELASTICITY; @@ -106,7 +105,6 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { // findSpherePenetration() only computes the penetration but we also want some other collision info // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding // the results to systems outside of this octree reference frame. - updateCollisionSound(entity, collisionInfo._penetration, COLLISION_FREQUENCY); collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration)); // let the global script run their collision scripts for Entities if they have them emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo); @@ -124,9 +122,6 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); float radius = entityA->getRadius() * (float)(TREE_SCALE); - //const float ELASTICITY = 0.4f; - //const float DAMPING = 0.0f; - const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; EntityItem* entityB; if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { @@ -141,6 +136,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { bool doCollisions = true; if (doCollisions) { + quint64 now = usecTimestampNow(); + entityA->collisionWithEntity(entityB, penetration); entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed @@ -165,7 +162,8 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { EntityItemID idA(entityA->getID()); propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); propertiesA.setPosition(newPositionA * (float)TREE_SCALE); - + propertiesA.setLastEdited(now); + _entities->updateEntity(idA, propertiesA); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); @@ -177,15 +175,13 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { EntityItemID idB(entityB->getID()); propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); propertiesB.setPosition(newPositionB * (float)TREE_SCALE); + propertiesB.setLastEdited(now); _entities->updateEntity(idB, propertiesB); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); // TODO: Do we need this? //_packetSender->releaseQueuedMessages(); - - //updateCollisionSound(entityA, penetration, COLLISION_FREQUENCY); } } } @@ -230,7 +226,6 @@ void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { // only collide when Entity and collision point are moving toward each other // (doing this prevents some "collision snagging" when Entity penetrates the object) - updateCollisionSound(entity, collision->_penetration, COLLISION_FREQUENCY); collision->_penetration /= (float)(TREE_SCALE); entity->applyHardCollision(*collision); queueEntityPropertiesUpdate(entity); @@ -251,42 +246,3 @@ void EntityCollisionSystem::queueEntityPropertiesUpdate(EntityItem* entity) { properties.setVelocity(entity->getVelocity() * (float)TREE_SCALE); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); } - - -void EntityCollisionSystem::updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency) { - - // consider whether to have the collision make a sound - const float AUDIBLE_COLLISION_THRESHOLD = 0.3f; - const float COLLISION_LOUDNESS = 1.f; - const float DURATION_SCALING = 0.004f; - const float NOISE_SCALING = 0.1f; - glm::vec3 velocity = Entity->getVelocity() * (float)(TREE_SCALE); - - /* - // how do we want to handle this?? - // - glm::vec3 gravity = Entity->getGravity() * (float)(TREE_SCALE); - - if (glm::length(gravity) > EPSILON) { - // If gravity is on, remove the effect of gravity on velocity for this - // frame, so that we are not constantly colliding with the surface - velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); - } - */ - float normalSpeed = glm::dot(velocity, glm::normalize(penetration)); - // NOTE: it is possible for normalSpeed to be NaN at this point - // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized) - // however the check below will fail (NaN comparisons always fail) and everything will be fine. - - if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) { - // Volume is proportional to collision velocity - // Base frequency is modified upward by the angle of the collision - // Noise is a function of the angle of collision - // Duration of the sound is a function of both base frequency and velocity of impact - float tangentialSpeed = glm::length(velocity) - normalSpeed; - _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), - frequency * (1.f + tangentialSpeed / normalSpeed), - std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); - } -} diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index facc7e5448..7cc5ebb545 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() : _id(UNKNOWN_ENTITY_ID), _idSet(false), - _lastEdited(usecTimestampNow()), + _lastEdited(0), // ???? _created(UNKNOWN_CREATED_TIME), _type(EntityTypes::Unknown), diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 40e76e1e34..f4f9343f76 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -221,9 +221,10 @@ public: bool getVisible() const { return _visible; } void setVisible(bool value) { _visible = value; _visibleChanged = true; } -private: void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } +private: + QUuid _id; bool _idSet; quint64 _lastEdited; From 6b8c9bba4274fffd6cb8ee9797722c92043b1eb7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 16:05:04 -0700 Subject: [PATCH 15/18] improved debug in entity renderer --- interface/src/entities/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 095277d960..e744d74da3 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -296,7 +296,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) ModelEntityItem* modelEntity = static_cast(entityItem); qDebug() << " url:" << modelEntity->getModelURL(); } - qDebug() << " entityBox:" << entityBox; + qDebug() << " entityBox:" << entityItem->getAABox(); qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters"; qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters"; qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance); From 4b52fddffdbe80e0e68dffc85835c36dacb2a308 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 17:37:56 -0700 Subject: [PATCH 16/18] collions with voxels and avatars --- .../entities/src/EntityCollisionSystem.cpp | 64 +++++++++++----- .../entities/src/EntityCollisionSystem.h | 2 + libraries/entities/src/EntityItem.cpp | 73 ------------------- libraries/entities/src/EntityItem.h | 2 - libraries/voxels/src/VoxelDetail.h | 15 ++++ tests/octree/CMakeLists.txt | 2 +- 6 files changed, 62 insertions(+), 96 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 688ce70345..871bcbf76c 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -99,9 +99,6 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { VoxelDetail* voxelDetails = NULL; if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) { - // let the Entities run their collision scripts if they have them - //entity->collisionWithVoxel(voxelDetails, collisionInfo._penetration); - // findSpherePenetration() only computes the penetration but we also want some other collision info // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding // the results to systems outside of this octree reference frame. @@ -112,9 +109,8 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { // we must scale back down to the octree reference frame before updating the Entity properties collisionInfo._penetration /= (float)(TREE_SCALE); collisionInfo._contactPoint /= (float)(TREE_SCALE); - entity->applyHardCollision(collisionInfo); - queueEntityPropertiesUpdate(entity); + applyHardCollision(entity, collisionInfo); delete voxelDetails; // cleanup returned details } } @@ -138,9 +134,6 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { if (doCollisions) { quint64 now = usecTimestampNow(); - entityA->collisionWithEntity(entityB, penetration); - entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed - CollisionInfo collision; collision._penetration = penetration; // for now the contactPoint is the average between the the two paricle centers @@ -188,9 +181,6 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { - // TODO: implement support for colliding with avatars - -#if 0 // Entities that are in hand, don't collide with avatars if (!_avatars) { return; @@ -200,7 +190,6 @@ void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { float radius = entity->getRadius() * (float)(TREE_SCALE); const float ELASTICITY = 0.9f; const float DAMPING = 0.1f; - const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; _collisions.clear(); @@ -227,22 +216,57 @@ void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { // only collide when Entity and collision point are moving toward each other // (doing this prevents some "collision snagging" when Entity penetrates the object) collision->_penetration /= (float)(TREE_SCALE); - entity->applyHardCollision(*collision); - queueEntityPropertiesUpdate(entity); + applyHardCollision(entity, *collision); } } } } - -#endif } -void EntityCollisionSystem::queueEntityPropertiesUpdate(EntityItem* entity) { - // queue the result for sending to the Entity server +void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo) { + // HALTING_* params are determined using expected acceleration of gravity over some timescale. + // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal. + const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second + const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE); + + // + // Update the entity in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) + // if elasticity = 1.0, collision is 100% elastic. + // + glm::vec3 position = entity->getPosition(); + glm::vec3 velocity = entity->getVelocity(); + + const float EPSILON = 0.0f; + glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; + float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); + if (velocityDotPenetration < EPSILON) { + // entity is moving into collision surface + // + // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing + // (other's mass could be stored in the Collision Info). The smaller mass should surrender more + // position offset and should slave more to the other's velocity in the static-friction case. + position -= collisionInfo._penetration; + + if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) { + // static friction kicks in and entities moves with colliding object + velocity = collisionInfo._addedVelocity; + } else { + glm::vec3 direction = glm::normalize(collisionInfo._penetration); + velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection + velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction + } + } + EntityItemProperties properties = entity->getProperties(); EntityItemID entityItemID(entity->getID()); - properties.setPosition(entity->getPosition() * (float)TREE_SCALE); - properties.setVelocity(entity->getVelocity() * (float)TREE_SCALE); + properties.setPosition(position * (float)TREE_SCALE); + properties.setVelocity(velocity * (float)TREE_SCALE); + properties.setLastEdited(usecTimestampNow()); + + _entities->updateEntity(entityItemID, properties); _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); } diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h index fe38209c09..b2cf7a9df4 100644 --- a/libraries/entities/src/EntityCollisionSystem.h +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -58,6 +58,8 @@ signals: void EntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& penetration); private: + void applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo); + static bool updateOperation(OctreeElement* element, void* extraData); void emitGlobalEntityCollisionWithVoxel(EntityItem* Entity, VoxelDetail* voxelDetails, const CollisionInfo& penetration); void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8a0a92c6a5..cfed16c443 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -903,76 +903,3 @@ float EntityItem::getRadius() const { return radius; } -void EntityItem::applyHardCollision(const CollisionInfo& collisionInfo) { - // HALTING_* params are determined using expected acceleration of gravity over some timescale. - // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal. - const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second - const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE); - - // - // Update the entity in response to a hard collision. Position will be reset exactly - // to outside the colliding surface. Velocity will be modified according to elasticity. - // - // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) - // if elasticity = 1.0, collision is 100% elastic. - // - glm::vec3 position = getPosition(); - glm::vec3 velocity = getVelocity(); - - const float EPSILON = 0.0f; - glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; - float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); - if (velocityDotPenetration < EPSILON) { - // entity is moving into collision surface - // - // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing - // (other's mass could be stored in the Collision Info). The smaller mass should surrender more - // position offset and should slave more to the other's velocity in the static-friction case. - position -= collisionInfo._penetration; - - if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) { - // static friction kicks in and entities moves with colliding object - velocity = collisionInfo._addedVelocity; - } else { - glm::vec3 direction = glm::normalize(collisionInfo._penetration); - velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection - velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction - } - } - - // TODO: how do we handle fixing up the location in the octree cells? - // change the local entity too... - setPosition(position); - setVelocity(velocity); -} - -void EntityItem::collisionWithEntity(EntityItem* other, const glm::vec3& penetration) { - // TODO: how do we want to handle collision related scripts for entities? - /* - // Only run this particle script if there's a script attached directly to the entity. - if (!_script.isEmpty()) { - ScriptEngine engine(_script); - ParticleScriptObject particleScriptable(this); - startParticleScriptContext(engine, particleScriptable); - ParticleScriptObject otherParticleScriptable(other); - particleScriptable.emitCollisionWithParticle(&otherParticleScriptable, penetration); - endParticleScriptContext(engine, particleScriptable); - } - */ -} - -void EntityItem::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { - // TODO: how do we want to handle collision related scripts for entities? - /* - // Only run this entity script if there's a script attached directly to the entity. - if (!_script.isEmpty()) { - ScriptEngine engine(_script); - ParticleScriptObject particleScriptable(this); - startParticleScriptContext(engine, particleScriptable); - particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); - endParticleScriptContext(engine, particleScriptable); - } - */ -} - - diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 7b03c786ed..bbfba30e9c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -229,8 +229,6 @@ public: float getRadius() const; void applyHardCollision(const CollisionInfo& collisionInfo); - void collisionWithEntity(EntityItem* other, const glm::vec3& penetration); - void collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration); protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h index 50a60828e7..b94601392b 100644 --- a/libraries/voxels/src/VoxelDetail.h +++ b/libraries/voxels/src/VoxelDetail.h @@ -51,4 +51,19 @@ Q_DECLARE_METATYPE(RayToVoxelIntersectionResult) QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToVoxelIntersectionResult& results); void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, RayToVoxelIntersectionResult& results); + +inline QDebug operator<<(QDebug debug, const VoxelDetail& details) { + const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe + + debug << "VoxelDetail[ (" + << details.x * (float)TREE_SCALE << "," << details.y * (float)TREE_SCALE << "," << details.z * (float)TREE_SCALE + << " ) to (" + << details.x + details.s * (float)TREE_SCALE << "," << details.y + details.s * (float)TREE_SCALE + << "," << details.z + details.s * (float)TREE_SCALE << ") size: (" + << details.s * (float)TREE_SCALE << "," << details.s * (float)TREE_SCALE << "," << details.s * (float)TREE_SCALE << ")" + << " in meters]"; + + return debug; +} + #endif // hifi_VoxelDetail_h diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 4907298a3c..5143c04918 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project(Script Network) include_glm() # link in the shared libraries -link_hifi_libraries(animation avatars fbx entities networking octree shared voxels) +link_hifi_libraries(shared octree voxels fbx metavoxels networking particles entities avatars audio animation script-engine) link_shared_dependencies() From 2cfa14d5ad1a5f2d20919d36f3e9bb986bd9b147 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 18:06:08 -0700 Subject: [PATCH 17/18] changed to only collide with moving entities --- .../entities/src/EntityCollisionSystem.cpp | 21 ++++--------------- libraries/entities/src/EntityTree.h | 2 ++ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 871bcbf76c..8d5941e3e0 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -43,26 +43,13 @@ void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, EntityCollisionSystem::~EntityCollisionSystem() { } -bool EntityCollisionSystem::updateOperation(OctreeElement* element, void* extraData) { - EntityCollisionSystem* system = static_cast(extraData); - EntityTreeElement* entityTreeElement = static_cast(element); - - // iterate the Entities... - QList& entities = entityTreeElement->getEntities(); - uint16_t numberOfEntities = entities.size(); - for (uint16_t i = 0; i < numberOfEntities; i++) { - EntityItem* entity = entities[i]; - system->checkEntity(entity); - } - - return true; -} - - void EntityCollisionSystem::update() { // update all Entities if (_entities->tryLockForRead()) { - _entities->recurseTreeWithOperation(updateOperation, this); + QList& movingEntities = _entities->getMovingEntities(); + foreach (EntityItem* entity, movingEntities) { + checkEntity(entity); + } _entities->unlock(); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d97a87faca..634a04a688 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -139,6 +139,8 @@ public: void trackDeletedEntity(const EntityItemID& entityID); + QList& getMovingEntities() { return _movingEntities; } + private: void updateChangingEntities(quint64 now, QSet& entitiesToDelete); From 9cc1fe35dba2176e8a3a5cebb88a6dee4e1f127e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 18:22:28 -0700 Subject: [PATCH 18/18] only do collisions on entities moving toward each other --- libraries/entities/src/EntityCollisionSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 8d5941e3e0..c9040c7a6e 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -116,7 +116,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; - bool doCollisions = true; + bool doCollisions = movingTowardEachOther; // don't do collisions if the entities are moving away from each other if (doCollisions) { quint64 now = usecTimestampNow();