From a83616a7dcb911204bcde7decf3c44db4d384db3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 30 Apr 2015 10:32:19 -0700 Subject: [PATCH 01/28] Don't prevent building on Visual Studio 2015 --- CMakeLists.txt | 2 +- cmake/macros/SetupExternalsBinaryDir.cmake | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b271664c35..24deac8393 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter -ggdb") endif(WIN32) -if (NOT MSVC12) +if ((NOT MSVC12) AND (NOT MSVC14)) include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) diff --git a/cmake/macros/SetupExternalsBinaryDir.cmake b/cmake/macros/SetupExternalsBinaryDir.cmake index a118dcf543..118df9c8fa 100644 --- a/cmake/macros/SetupExternalsBinaryDir.cmake +++ b/cmake/macros/SetupExternalsBinaryDir.cmake @@ -14,12 +14,12 @@ macro(SETUP_EXTERNALS_BINARY_DIR) # get a short name for the generator to use in the path STRING(REGEX REPLACE " " "-" CMAKE_GENERATOR_FOLDER_NAME ${CMAKE_GENERATOR}) - if (MSVC12) + if (MSVC12) set(CMAKE_GENERATOR_FOLDER_NAME "vc12") - else () - if (CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles") - set(CMAKE_GENERATOR_FOLDER_NAME "makefiles") - endif () + elseif (MSVC14) + set(CMAKE_GENERATOR_FOLDER_NAME "vc14") + elseif(CMAKE_GENERATOR_FOLDER_NAME STREQUAL "Unix-Makefiles") + set(CMAKE_GENERATOR_FOLDER_NAME "makefiles") endif () set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext") From 984d449bf9a391d6385ec9fd239b002e4cf5b8ad Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 4 Jun 2015 16:29:58 -0700 Subject: [PATCH 02/28] exposed input key bindings to js to enable key remapping --- interface/src/Application.h | 1 + interface/src/devices/KeyboardMouseDevice.cpp | 18 ++- .../ControllerScriptingInterface.cpp | 117 +++++++++++++++++- .../scripting/ControllerScriptingInterface.h | 14 ++- interface/src/ui/UserInputMapper.cpp | 96 +++++++++++++- interface/src/ui/UserInputMapper.h | 56 +++++++-- .../AbstractControllerScriptingInterface.h | 2 + libraries/script-engine/src/ScriptEngine.cpp | 1 + 8 files changed, 289 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index b6efb6420b..9b815cb51a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -148,6 +148,7 @@ public: static glm::quat getOrientationForPath() { return getInstance()->_myAvatar->getOrientation(); } static glm::vec3 getPositionForAudio() { return getInstance()->_myAvatar->getHead()->getPosition(); } static glm::quat getOrientationForAudio() { return getInstance()->_myAvatar->getHead()->getFinalOrientationInWorldFrame(); } + static UserInputMapper* getUserInputMapper() { return &getInstance()->_userInputMapper; } static void initPlugins(); static void shutdownPlugins(); diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index 7b0f8c056c..a7e85d28e1 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -159,9 +159,25 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); - auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy()); + auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("Keyboard")); proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input._channel); }; proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + for (int i = 0; i <= 9; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(48 + i)), QKeySequence(Qt::Key(48 + i)).toString())); + } + for (int i = 0; i < 26; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(65 + i)), QKeySequence(Qt::Key(65 + i)).toString())); + } + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &_mapper = mapper, &device = _deviceID] () -> bool { + _mapper.removeAllInputChannelsForDevice(device); + this->assignDefaultInputMapping(_mapper); + return true; + }; mapper.registerDevice(_deviceID, proxy); } diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 5f12d73b37..fe2eda12df 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -28,6 +28,90 @@ ControllerScriptingInterface::ControllerScriptingInterface() : { } +static int actionMetaTypeId = qRegisterMetaType(); +static int inputChannelMetaTypeId = qRegisterMetaType(); +static int inputMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); +QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel); +void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel); +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { + QScriptValue obj = engine->newObject(); + obj.setProperty("device", input.getDevice()); + obj.setProperty("channel", input.getChannel()); + obj.setProperty("type", input._type); + obj.setProperty("id", input.getID()); + return obj; +} + +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { + input._device = object.property("device").toUInt16(); + input._channel = object.property("channel").toUInt16(); + input._type = object.property("type").toUInt16(); + input._id = object.property("id").toInt32(); +} + +QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputChannel._input)); + obj.setProperty("modifier", inputToScriptValue(engine, inputChannel._modifier)); + obj.setProperty("action", inputChannel._action); + obj.setProperty("scale", inputChannel._scale); + return obj; +} + +void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) { + inputFromScriptValue(object.property("input"), inputChannel._input); + inputFromScriptValue(object.property("modifier"), inputChannel._modifier); + inputChannel._action = UserInputMapper::Action(object.property("action").toVariant().toInt()); + inputChannel._scale = object.property("scale").toVariant().toFloat(); +} + +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { + QScriptValue obj = engine->newObject(); + QVector inputChannels = Application::getUserInputMapper()->getInputChannelsForAction(action); + QScriptValue _inputChannels = engine->newArray(inputChannels.size()); + for (int i = 0; i < inputChannels.size(); i++) { + _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i])); + } + obj.setProperty("action", (int) action); + obj.setProperty("actionName", Application::getUserInputMapper()->getActionName(action)); + obj.setProperty("inputChannels", _inputChannels); + return obj; +} + +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { + action = UserInputMapper::Action(object.property("action").toVariant().toInt()); +} + +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); + obj.setProperty("inputName", inputPair.second); + return obj; +} + +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { + inputFromScriptValue(object.property("input"), inputPair.first); + inputPair.second = QString(object.property("inputName").toVariant().toString()); +} + +void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); + qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); + qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); + qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); +} void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { @@ -337,6 +421,37 @@ void ControllerScriptingInterface::updateInputControllers() { } } +QVector ControllerScriptingInterface::getAllActions() { + return Application::getUserInputMapper()->getAllActions(); +} + +QVector ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) { + return Application::getUserInputMapper()->getInputChannelsForAction(action); +} + +QString ControllerScriptingInterface::getDeviceName(unsigned int device) { + return Application::getUserInputMapper()->getDeviceName((unsigned short) device); +} + +QVector ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) { + return Application::getUserInputMapper()->getAllInputsForDevice(device); +} + +bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) { + return Application::getUserInputMapper()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale); +} + +bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) { + return Application::getUserInputMapper()->removeInputChannel(inputChannel); +} + +QVector ControllerScriptingInterface::getAvailableInputs(unsigned int device) { + return Application::getUserInputMapper()->getAvailableInputs((unsigned short) device); +} + +void ControllerScriptingInterface::resetAllDeviceBindings() { + Application::getUserInputMapper()->resetAllDeviceBindings(); +} InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : AbstractInputController(), @@ -373,4 +488,4 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; InputController::Key InputController::getKey() const { return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); -} +} \ No newline at end of file diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index c088dd6c9a..9414dc887b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -14,10 +14,11 @@ #include +#include "ui/UserInputMapper.h" + #include class PalmData; - class InputController : public AbstractInputController { Q_OBJECT @@ -54,6 +55,9 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface public: ControllerScriptingInterface(); + + virtual void registerControllerTypes(QScriptEngine* engine); + void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } @@ -79,6 +83,14 @@ public: void updateInputControllers(); public slots: + Q_INVOKABLE virtual QVector getAllActions(); + Q_INVOKABLE virtual QVector getInputChannelsForAction(UserInputMapper::Action action); + Q_INVOKABLE virtual QString getDeviceName(unsigned int device); + Q_INVOKABLE virtual QVector getAllInputsForDevice(unsigned int device); + Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE virtual QVector getAvailableInputs(unsigned int device); + Q_INVOKABLE virtual void resetAllDeviceBindings(); virtual bool isPrimaryButtonPressed() const; virtual glm::vec2 getPrimaryJoystickPosition() const; diff --git a/interface/src/ui/UserInputMapper.cpp b/interface/src/ui/UserInputMapper.cpp index 892ab6a9b6..e994b3cf30 100755 --- a/interface/src/ui/UserInputMapper.cpp +++ b/interface/src/ui/UserInputMapper.cpp @@ -13,7 +13,15 @@ // UserInputMapper Class + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + assignDefaulActionScales(); + createActionNames(); +} + bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ + proxy->_name += " (" + QString::number(deviceID) + ")"; _registeredDevices[deviceID] = proxy; return true; } @@ -27,6 +35,12 @@ UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Inpu } } +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { return addInputChannel(action, input, Input(), scale); } @@ -37,7 +51,7 @@ bool UserInputMapper::addInputChannel(Action action, const Input& input, const I qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; return false; } - + auto inputChannel = InputChannel(input, modifier, action, scale); // Insert or replace the input to modifiers @@ -61,6 +75,37 @@ int UserInputMapper::addInputChannels(const InputChannels& channels) { return nbAdded; } +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + int UserInputMapper::getInputChannels(InputChannels& channels) const { for (auto& channel : _actionToInputsMap) { channels.push_back(channel.second); @@ -69,6 +114,20 @@ int UserInputMapper::getInputChannels(InputChannels& channels) const { return _actionToInputsMap.size(); } +QVector UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + void UserInputMapper::update(float deltaTime) { // Reset the axis state for next loop @@ -130,6 +189,24 @@ void UserInputMapper::update(float deltaTime) { } } +QVector UserInputMapper::getAllActions() { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + void UserInputMapper::assignDefaulActionScales() { _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit @@ -144,3 +221,20 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[BOOM_IN] = 1.0f; // 1m per unit _actionScales[BOOM_OUT] = 1.0f; // 1m per unit } + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; +} \ No newline at end of file diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index ab63bdbef7..34188ae3f5 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -21,6 +21,7 @@ class UserInputMapper : public QObject { Q_OBJECT + Q_ENUMS(Action) public: typedef unsigned short uint16; typedef unsigned int uint32; @@ -64,6 +65,7 @@ public: explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {} Input(const Input& src) : _id(src._id) {} Input& operator = (const Input& src) { _id = src._id; return (*this); } + bool operator ==(const Input& right) const { return _id == right._id; } bool operator < (const Input& src) const { return _id < src._id; } }; @@ -83,22 +85,32 @@ public: typedef std::function ButtonGetter; typedef std::function AxisGetter; typedef std::function JointGetter; + typedef QPair InputPair; + typedef std::function ()> AvailableInputGetter; + typedef std::function ResetBindings; + + typedef QVector AvailableInput; class DeviceProxy { public: - DeviceProxy() {} - - ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; - AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; }; - JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); }; - - typedef std::shared_ptr Pointer; + DeviceProxy(QString name) { _name = name; } + + QString _name; + ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; + AxisGetter getAxis = [] (const Input& input, int timestamp) -> bool { return 0.0f; }; + JointGetter getJoint = [] (const Input& input, int timestamp) -> JointValue { return JointValue(); }; + AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector(); }; + ResetBindings resetDeviceBindings = [] () -> bool { return true; }; + + typedef std::shared_ptr Pointer; }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); DeviceProxy::Pointer getDeviceProxy(const Input& input); - + QString getDeviceName(uint16 deviceID) { return _registeredDevices[deviceID]->_name; } + QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } + void resetAllDeviceBindings(); // Actions are the output channels of the Mapper, that's what the InputChannel map to // For now the Actions are hardcoded, this is bad, but we will fix that in the near future @@ -123,8 +135,14 @@ public: NUM_ACTIONS, }; + + std::vector _actionNames = std::vector(NUM_ACTIONS); + void createActionNames(); + QVector getAllActions(); + QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; } float getActionState(Action action) const { return _actionStates[action]; } +// QVector void assignDefaulActionScales(); // Add input channel to the mapper and check that all the used channels are registered. @@ -146,21 +164,27 @@ public: _input(input), _modifier(modifier), _action(action), _scale(scale) {} InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {} InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); } - + bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; } bool hasModifier() { return _modifier.isValid(); } }; typedef std::vector< InputChannel > InputChannels; // Add a bunch of input channels, return the true number of channels that successfully were added int addInputChannels(const InputChannels& channels); + // Remove the first found instance of the input channel from the input mapper, true if found + bool removeInputChannel(InputChannel channel); + void removeAllInputChannels(); + void removeAllInputChannelsForDevice(uint16 device); //Grab all the input channels currently in use, return the number int getInputChannels(InputChannels& channels) const; + QVector getAllInputsForDevice(uint16 device); + QVector getInputChannelsForAction(UserInputMapper::Action action); + std::multimap getActionToInputsMap() { return _actionToInputsMap; } // Update means go grab all the device input channels and update the output channel values void update(float deltaTime); - - // Default contruct allocate the poutput size with the current hardcoded action channels - UserInputMapper() { assignDefaulActionScales(); } + + UserInputMapper(); protected: typedef std::map DevicesMap; @@ -177,4 +201,12 @@ protected: std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); }; +Q_DECLARE_METATYPE(UserInputMapper::InputPair) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(UserInputMapper::Input) +Q_DECLARE_METATYPE(UserInputMapper::InputChannel) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(UserInputMapper::Action) +Q_DECLARE_METATYPE(QVector) + #endif // hifi_UserInputMapper_h diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index 11755add8e..43076039a9 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -52,6 +52,8 @@ class AbstractControllerScriptingInterface : public QObject { Q_OBJECT public slots: + virtual void registerControllerTypes(QScriptEngine* engine) = 0; + virtual bool isPrimaryButtonPressed() const = 0; virtual glm::vec2 getPrimaryJoystickPosition() const = 0; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 30c3fbe8e4..182a0aea8d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -317,6 +317,7 @@ void ScriptEngine::init() { registerAnimationTypes(this); registerAvatarTypes(this); registerAudioMetaTypes(this); + _controllerScriptingInterface->registerControllerTypes(this); qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValueHonorReadOnly); qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); From dd12e4740e036895929ddd42e63eab9caab6fab0 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Fri, 5 Jun 2015 10:14:13 -0700 Subject: [PATCH 03/28] example script for controller scripting, input mapping --- .../scripts/controllerScriptingExamples.js | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 examples/example/scripts/controllerScriptingExamples.js diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js new file mode 100644 index 0000000000..3e693c42ea --- /dev/null +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -0,0 +1,94 @@ +// +// controllerScriptingExamples.js +// examples +// +// Created by Sam Gondelman on 6/2/15 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// Resets every device to its default key bindings: +Controller.resetAllDeviceBindings() + +// Query all actions +print("All Actions: \n" + Controller.getAllActions()) + +// Each action stores: +// action: int representation of enum +print("Action 5 int: \n" + Controller.getAllActions()[5].action) + +// actionName: string representation of enum +print("Action 5 name: \n" + Controller.getAllActions()[5].actionName) + +// inputChannels: list of all inputchannels that control that action +print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n") + + +// Each input channel stores: +// action: Action that this InputChannel maps to +print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action) + +// scale: sensitivity of input +print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale) + +// input and modifier: Inputs +print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n") + + +// Each Input stores: +// device: device of input +print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device) + +// channel: channel of input +print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel) + +// type: type of input (Unknown, Button, Axis, Joint) +print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type) + +// id: id of input +print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n") + + +// You can get the name of a device from its id +print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)) + +// You can also get all of a devices input channels +print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n") + + +// Modifying properties: +// The following code will switch the "w" and "s" key functionality and adjust their scales +var s = Controller.getAllActions()[0].inputChannels[0] +var w = Controller.getAllActions()[1].inputChannels[0] + +// You must remove an input controller before modifying it so the old input controller isn't registered anymore +// removeInputChannel and addInputChannel return true if successful, false otherwise +Controller.removeInputChannel(s) +Controller.removeInputChannel(w) +print(s.scale) +s.action = 1 +s.scale = .01 + +w.action = 0 +w.scale = 10000 +Controller.addInputChannel(s) +Controller.addInputChannel(w) +print(s.scale) + +// You can get all the available inputs for any device +// Each AvailableInput has: +// input: the Input itself +// inputName: string representing the input +var availableInputs = Controller.getAvailableInputs(1) +for (i = 0; i < availableInputs.length; i++) { + print(availableInputs[i].inputName); +} + +// You can modify key bindings by using these avaiable inputs +// This will replace e (up) with 6 +var e = Controller.getAllActions()[5].inputChannels[0] +Controller.removeInputChannel(e) +e.input = availableInputs[6].input +Controller.addInputChannel(e) \ No newline at end of file From 1e858d8bc5bbc3e135c5463dc3b38bb69ffb9c18 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 8 Jun 2015 14:16:03 -0700 Subject: [PATCH 04/28] start on spring action --- .../entities/src/EntityActionInterface.cpp | 62 +++++++++- .../entities/src/EntityActionInterface.h | 4 +- libraries/physics/src/ObjectActionSpring.cpp | 107 ++++++++++++++++++ libraries/physics/src/ObjectActionSpring.h | 37 ++++++ .../physics/src/PhysicalEntitySimulation.cpp | 4 + 5 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 libraries/physics/src/ObjectActionSpring.cpp create mode 100644 libraries/physics/src/ObjectActionSpring.h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index f26dd006ff..d92771fce1 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -22,6 +22,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "pulltopoint") { return ACTION_TYPE_PULL_TO_POINT; } + if (normalizedActionTypeString == "spring") { + return ACTION_TYPE_SPRING; + } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -33,6 +36,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "none"; case ACTION_TYPE_PULL_TO_POINT: return "pullToPoint"; + case ACTION_TYPE_SPRING: + return "spring"; } assert(false); return "none"; @@ -43,21 +48,21 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian if (!arguments.contains(argumentName)) { qDebug() << objectName << "requires argument:" << argumentName; ok = false; - return vec3(); + return glm::vec3(); } QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { qDebug() << objectName << "argument" << argumentName << "must be a map"; ok = false; - return vec3(); + return glm::vec3(); } QVariantMap resultVM = resultV.toMap(); if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; ok = false; - return vec3(); + return glm::vec3(); } QVariant xV = resultVM["x"]; @@ -73,13 +78,60 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian if (!xOk || !yOk || !zOk) { qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float."; ok = false; - return vec3(); + return glm::vec3(); } - return vec3(x, y, z); + return glm::vec3(x, y, z); } +glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok) { + if (!arguments.contains(argumentName)) { + qDebug() << objectName << "requires argument:" << argumentName; + ok = false; + return glm::quat(); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qDebug() << objectName << "argument" << argumentName << "must be a map"; + ok = false; + return glm::quat(); + } + + QVariantMap resultVM = resultV.toMap(); + if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { + qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z"; + ok = false; + return glm::quat(); + } + + QVariant xV = resultVM["x"]; + QVariant yV = resultVM["y"]; + QVariant zV = resultVM["z"]; + QVariant wV = resultVM["w"]; + + bool xOk = true; + bool yOk = true; + bool zOk = true; + bool wOk = true; + float x = xV.toFloat(&xOk); + float y = yV.toFloat(&yOk); + float z = zV.toFloat(&zOk); + float w = wV.toFloat(&wOk); + if (!xOk || !yOk || !zOk || !wOk) { + qDebug() << objectName << "argument" << argumentName + << "must be a map with keys of x, y, z, w and values of type float."; + ok = false; + return glm::quat(); + } + + return glm::quat(x, y, z, w); +} + + + float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok) { if (!arguments.contains(argumentName)) { diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 74efae3239..3baee06c3e 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -19,7 +19,8 @@ class EntitySimulation; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString ACTION_TYPE_NONE, - ACTION_TYPE_PULL_TO_POINT + ACTION_TYPE_PULL_TO_POINT, + ACTION_TYPE_SPRING }; @@ -41,6 +42,7 @@ public: protected: static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); + static glm::quat extractQuatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); }; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp new file mode 100644 index 0000000000..8a436096a8 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -0,0 +1,107 @@ +// +// ObjectActionSpring.cpp +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ObjectMotionState.h" +#include "BulletUtil.h" + +#include "ObjectActionSpring.h" + +ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) : + ObjectAction(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::ObjectActionSpring"; + #endif +} + +ObjectActionSpring::~ObjectActionSpring() { + #if WANT_DEBUG + qDebug() << "ObjectActionSpring::~ObjectActionSpring"; + #endif +} + +void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + return; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + + if (_active && physicsInfo) { + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (rigidBody) { + glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + + + // btQuaternion getOrientation() const; + // const btTransform& getCenterOfMassTransform() const; + + float offsetLength = glm::length(offset); + float speed = offsetLength; // XXX use _positionalSpringConstant + + float interpolation_value = 0.5; // XXX + const glm::quat slerped_quat = glm::slerp(bulletToGLM(rigidBody->getOrientation()), + _rotationalTarget, + interpolation_value); + + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + // void setAngularVelocity (const btVector3 &ang_vel); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + } + } + } + unlock(); +} + + +bool ObjectActionSpring::updateArguments(QVariantMap arguments) { + // targets are required, spring-constants are optional + bool ok = true; + glm::vec3 positionalTarget = + EntityActionInterface::extractVec3Argument("spring action", arguments, "positionalTarget", ok); + bool pscOK = true; + float positionalSpringConstant = + EntityActionInterface::extractFloatArgument("spring action", arguments, "positionalSpringConstant", pscOK); + + glm::quat rotationalTarget = + EntityActionInterface::extractQuatArgument("spring action", arguments, "rotationalTarget", ok); + bool rscOK = true; + float rotationalSpringConstant = + EntityActionInterface::extractFloatArgument("spring action", arguments, "rotationalSpringConstant", rscOK); + + if (!ok) { + return false; + } + + lockForWrite(); + + _positionalTarget = positionalTarget; + if (pscOK) { + _positionalSpringConstant = positionalSpringConstant; + } else { + _positionalSpringConstant = 0.5; // XXX pick a good default; + } + + _rotationalTarget = rotationalTarget; + if (rscOK) { + _rotationalSpringConstant = rotationalSpringConstant; + } else { + _rotationalSpringConstant = 0.5; // XXX pick a good default; + } + + _active = true; + unlock(); + return true; +} diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h new file mode 100644 index 0000000000..77502e8544 --- /dev/null +++ b/libraries/physics/src/ObjectActionSpring.h @@ -0,0 +1,37 @@ +// +// ObjectActionSpring.h +// libraries/physics/src +// +// Created by Seth Alves 2015-6-5 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ObjectActionSpring_h +#define hifi_ObjectActionSpring_h + +#include + +#include +#include "ObjectAction.h" + +class ObjectActionSpring : public ObjectAction { +public: + ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity); + virtual ~ObjectActionSpring(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + +private: + + glm::vec3 _positionalTarget; + float _positionalSpringConstant; + + glm::quat _rotationalTarget; + float _rotationalSpringConstant; +}; + +#endif // hifi_ObjectActionSpring_h diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 711c5e49da..56d497f8a1 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -13,6 +13,7 @@ #include "PhysicsLogging.h" #include "ShapeManager.h" #include "ObjectActionPullToPoint.h" +#include "ObjectActionSpring.h" #include "PhysicalEntitySimulation.h" @@ -245,6 +246,9 @@ EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType typ case ACTION_TYPE_PULL_TO_POINT: action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); break; + case ACTION_TYPE_SPRING: + action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); + break; } bool ok = action->updateArguments(arguments); From b3bc9c3ef05d871b5b6dfc0cc0b2378b2192636e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 8 Jun 2015 17:10:13 -0700 Subject: [PATCH 05/28] first attempt at getting spring-action to handle rotation --- .../entities/src/EntityActionInterface.cpp | 20 ++-- .../entities/src/EntityActionInterface.h | 9 +- libraries/physics/src/ObjectActionSpring.cpp | 96 +++++++++++-------- libraries/physics/src/ObjectActionSpring.h | 6 +- 4 files changed, 80 insertions(+), 51 deletions(-) diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index d92771fce1..b3e774df96 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -44,9 +44,11 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { } glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; return glm::vec3(); } @@ -86,16 +88,18 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; return glm::quat(); } QVariant resultV = arguments[argumentName]; if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { - qDebug() << objectName << "argument" << argumentName << "must be a map"; + qDebug() << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); ok = false; return glm::quat(); } @@ -133,9 +137,11 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok) { + QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { - qDebug() << objectName << "requires argument:" << argumentName; + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } ok = false; return 0.0f; } diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 3baee06c3e..a0a3db9b68 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -41,9 +41,12 @@ public: protected: - static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); - static glm::quat extractQuatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); - static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok); + static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + static float extractFloatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); }; typedef std::shared_ptr EntityActionPointer; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 8a436096a8..665ec352fe 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -38,27 +38,30 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (rigidBody) { - glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + // handle the linear part + if (_positionalTargetSet) { + glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + float speed = offsetLength / _linearTimeScale; + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + // void setAngularVelocity (const btVector3 &ang_vel); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + } + } - // btQuaternion getOrientation() const; - // const btTransform& getCenterOfMassTransform() const; - - float offsetLength = glm::length(offset); - float speed = offsetLength; // XXX use _positionalSpringConstant - - float interpolation_value = 0.5; // XXX - const glm::quat slerped_quat = glm::slerp(bulletToGLM(rigidBody->getOrientation()), - _rotationalTarget, - interpolation_value); - - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * speed; - rigidBody->setLinearVelocity(glmToBullet(newVelocity)); - // void setAngularVelocity (const btVector3 &ang_vel); - rigidBody->activate(); - } else { - rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); + // handle rotation + if (_rotationalTargetSet) { + glm::quat qZeroInverse = glm::inverse(bulletToGLM(rigidBody->getOrientation())); + glm::quat deltaQ = _rotationalTarget * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + glm::vec3 newAngularVelocity = (-angle / _angularTimeScale) * glm::normalize(axis); + rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); } } } @@ -68,37 +71,52 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar bool ObjectActionSpring::updateArguments(QVariantMap arguments) { // targets are required, spring-constants are optional - bool ok = true; + bool ptOk = true; glm::vec3 positionalTarget = - EntityActionInterface::extractVec3Argument("spring action", arguments, "positionalTarget", ok); - bool pscOK = true; - float positionalSpringConstant = - EntityActionInterface::extractFloatArgument("spring action", arguments, "positionalSpringConstant", pscOK); + EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false); + bool pscOk = true; + float linearTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", pscOk, false); + if (ptOk && pscOk && linearTimeScale <= 0.0f) { + qDebug() << "spring action -- linearTimeScale must be greater than zero."; + return false; + } + bool rtOk = true; glm::quat rotationalTarget = - EntityActionInterface::extractQuatArgument("spring action", arguments, "rotationalTarget", ok); - bool rscOK = true; - float rotationalSpringConstant = - EntityActionInterface::extractFloatArgument("spring action", arguments, "rotationalSpringConstant", rscOK); + EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false); + bool rscOk = true; + float angularTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", rscOk, false); - if (!ok) { + if (!ptOk && !rtOk) { + qDebug() << "spring action requires either targetPosition or targetRotation argument"; return false; } lockForWrite(); - _positionalTarget = positionalTarget; - if (pscOK) { - _positionalSpringConstant = positionalSpringConstant; - } else { - _positionalSpringConstant = 0.5; // XXX pick a good default; + _positionalTargetSet = _rotationalTargetSet = false; + + if (ptOk) { + _positionalTarget = positionalTarget; + _positionalTargetSet = true; + + if (pscOk) { + _linearTimeScale = linearTimeScale; + } else { + _linearTimeScale = 0.1; + } } - _rotationalTarget = rotationalTarget; - if (rscOK) { - _rotationalSpringConstant = rotationalSpringConstant; - } else { - _rotationalSpringConstant = 0.5; // XXX pick a good default; + if (rtOk) { + _rotationalTarget = rotationalTarget; + + if (rscOk) { + _angularTimeScale = angularTimeScale; + } else { + _angularTimeScale = 0.1; + } } _active = true; diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index 77502e8544..b211259866 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -28,10 +28,12 @@ public: private: glm::vec3 _positionalTarget; - float _positionalSpringConstant; + float _linearTimeScale; + bool _positionalTargetSet; glm::quat _rotationalTarget; - float _rotationalSpringConstant; + float _angularTimeScale; + bool _rotationalTargetSet; }; #endif // hifi_ObjectActionSpring_h From 14a45e83492e411b66726d31fd8287b46e68b1b9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 8 Jun 2015 18:25:58 -0700 Subject: [PATCH 06/28] convert grab.js to use spring action. rotation doesn't work right, yet --- examples/grab.js | 95 +++++++++++-------- .../physics/src/ObjectActionPullToPoint.cpp | 4 + libraries/physics/src/ObjectActionSpring.cpp | 5 + 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index 306af86c68..d04e46b17b 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -4,7 +4,7 @@ // Created by Eric Levin on May 1, 2015 // Copyright 2015 High Fidelity, Inc. // -// Grab's physically moveable entities with the mouse, by applying a spring force. +// Grab's physically moveable entities with the mouse, by applying a spring force. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -20,6 +20,7 @@ var ANGULAR_DAMPING_RATE = 0.40; // NOTE: to improve readability global variable names start with 'g' var gIsGrabbing = false; var gGrabbedEntity = null; +var gActionID = null; var gPrevMouse = {x: 0, y: 0}; var gEntityProperties; var gStartPosition; @@ -31,20 +32,20 @@ var gPlaneNormal = ZERO_VEC3; // gMaxGrabDistance is a function of the size of the object. var gMaxGrabDistance; -// gGrabMode defines the degrees of freedom of the grab target positions -// relative to gGrabStartPosition options include: +// gGrabMode defines the degrees of freedom of the grab target positions +// relative to gGrabStartPosition options include: // xzPlane (default) // verticalCylinder (SHIFT) // rotate (CONTROL) // Modes to eventually support?: -// xyPlane -// yzPlane +// xyPlane +// yzPlane // polar // elevationAzimuth -var gGrabMode = "xzplane"; +var gGrabMode = "xzplane"; -// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection -// with the move-plane to object center (at the moment the grab is initiated). Future target positions +// gGrabOffset allows the user to grab an object off-center. It points from ray's intersection +// with the move-plane to object center (at the moment the grab is initiated). Future target positions // are relative to the ray's intersection by the same offset. var gGrabOffset = { x: 0, y: 0, z: 0 }; @@ -162,7 +163,7 @@ function computeNewGrabPlane() { var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition()); xzOffset.y = 0; gXzDistanceToGrab = Vec3.length(xzOffset); - + if (gGrabMode !== "rotate" && maybeResetMousePosition) { // we reset the mouse position whenever we stop rotating Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y); @@ -193,6 +194,7 @@ function mousePressEvent(event) { var cameraPosition = Camera.getPosition(); gBeaconHeight = Vec3.length(entityProperties.dimensions); + print("gBeaconHeight = " + gBeaconHeight); gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) { // don't allow grabs of things far away @@ -231,6 +233,8 @@ function mouseReleaseEvent() { } gIsGrabbing = false + Entities.deleteAction(grabbedEntity, gActionID); + gActionID = null; Overlays.editOverlay(gBeacon, { visible: false }); @@ -250,6 +254,8 @@ function mouseMoveEvent(event) { gOriginalGravity = entityProperties.gravity; } + var actionArgs; + if (gGrabMode === "rotate") { var deltaMouse = { x: 0, y: 0 }; var dx = event.x - gPreviousMouse.x; @@ -259,9 +265,12 @@ function mouseMoveEvent(event) { var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); - var axis = Vec3.normalize(axis); - var ROTATE_STRENGTH = 8.0; // magic number tuned by hand - gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis); + // var axis = Vec3.normalize(axis); + // var ROTATE_STRENGTH = 8.0; // magic number tuned by hand + // gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis); + + var targetRotation = Quat.angleAxis(Vec3.length(axis), Vec3.normalize(axis)); + actionArgs = {targetRotation: targetRotation, angularTimeScale: 1.0}; } else { var newTargetPosition; if (gGrabMode === "verticalCylinder") { @@ -284,9 +293,18 @@ function mouseMoveEvent(event) { } } gTargetPosition = Vec3.sum(newTargetPosition, gGrabOffset); + actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1}; } gPreviousMouse = { x: event.x, y: event.y }; gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() }; + + if (!gActionID) { + gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs); + } else { + Entities.updateAction(gGrabbedEntity, gActionID, actionArgs); + } + + updateDropLine(gTargetPosition); } function keyReleaseEvent(event) { @@ -309,38 +327,37 @@ function keyPressEvent(event) { computeNewGrabPlane(); } -function update(deltaTime) { - if (!gIsGrabbing) { - return; - } +// function update(deltaTime) { +// if (!gIsGrabbing) { +// return; +// } - var entityProperties = Entities.getEntityProperties(gGrabbedEntity); - gCurrentPosition = entityProperties.position; - if (gGrabMode === "rotate") { - gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE)); - Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, }); - } +// var entityProperties = Entities.getEntityProperties(gGrabbedEntity); +// gCurrentPosition = entityProperties.position; +// if (gGrabMode === "rotate") { +// gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE)); +// Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, }); +// } - // always push toward linear grab position, even when rotating - var newVelocity = ZERO_VEC3; - var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition); - var delta = Vec3.length(dPosition); - if (delta > CLOSE_ENOUGH) { - var MAX_POSITION_DELTA = 4.0; - if (delta > MAX_POSITION_DELTA) { - dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); - } - // desired speed is proportional to displacement by the inverse of timescale - // (for critically damped motion) - newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE); - } - Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, }); - updateDropLine(gTargetPosition); -} +// // always push toward linear grab position, even when rotating +// var newVelocity = ZERO_VEC3; +// var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition); +// var delta = Vec3.length(dPosition); +// if (delta > CLOSE_ENOUGH) { +// var MAX_POSITION_DELTA = 4.0; +// if (delta > MAX_POSITION_DELTA) { +// dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); +// } +// // desired speed is proportional to displacement by the inverse of timescale +// // (for critically damped motion) +// newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE); +// } +// Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, }); +// } Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.update.connect(update); +// Script.update.connect(update); diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 78f202a24f..28c0e08bd9 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -28,6 +28,10 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { } void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { + if (!_ownerEntity) { + qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; + return; + } if (!tryLockForRead()) { // don't risk hanging the thread running the physics simulation return; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 665ec352fe..f8b8d92ad5 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -28,6 +28,10 @@ ObjectActionSpring::~ObjectActionSpring() { } void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { + if (!_ownerEntity) { + qDebug() << "ObjectActionSpring::updateAction no owner entity"; + return; + } if (!tryLockForRead()) { // don't risk hanging the thread running the physics simulation return; @@ -111,6 +115,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { if (rtOk) { _rotationalTarget = rotationalTarget; + _rotationalTargetSet = true; if (rscOk) { _angularTimeScale = angularTimeScale; From 2fb64aad91cab4a1ec3089a88cc6abed90be79b0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 8 Jun 2015 20:31:35 -0700 Subject: [PATCH 07/28] fix variable name --- examples/grab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/grab.js b/examples/grab.js index d04e46b17b..f2c74bb252 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -233,7 +233,7 @@ function mouseReleaseEvent() { } gIsGrabbing = false - Entities.deleteAction(grabbedEntity, gActionID); + Entities.deleteAction(gGrabbedEntity, gActionID); gActionID = null; Overlays.editOverlay(gBeacon, { visible: false }); From b1b2d1f85cf54f9d0bf65a6674bb4c4c3fdc0fb6 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Tue, 9 Jun 2015 09:45:19 -0700 Subject: [PATCH 08/28] ;) (added semicolons to js example) --- .../scripts/controllerScriptingExamples.js | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index 3e693c42ea..26a1999bbb 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -9,86 +9,88 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Assumes you only have the default keyboard connected + // Resets every device to its default key bindings: -Controller.resetAllDeviceBindings() +Controller.resetAllDeviceBindings(); // Query all actions -print("All Actions: \n" + Controller.getAllActions()) +print("All Actions: \n" + Controller.getAllActions()); // Each action stores: // action: int representation of enum -print("Action 5 int: \n" + Controller.getAllActions()[5].action) +print("Action 5 int: \n" + Controller.getAllActions()[5].action); // actionName: string representation of enum -print("Action 5 name: \n" + Controller.getAllActions()[5].actionName) +print("Action 5 name: \n" + Controller.getAllActions()[5].actionName); // inputChannels: list of all inputchannels that control that action -print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n") +print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n"); // Each input channel stores: // action: Action that this InputChannel maps to -print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action) +print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action); // scale: sensitivity of input -print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale) +print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale); // input and modifier: Inputs -print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n") +print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n"); // Each Input stores: // device: device of input -print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device) +print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device); // channel: channel of input -print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel) +print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel); // type: type of input (Unknown, Button, Axis, Joint) -print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type) +print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type); // id: id of input -print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n") +print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n"); // You can get the name of a device from its id -print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)) +print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)); // You can also get all of a devices input channels -print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n") +print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n"); // Modifying properties: // The following code will switch the "w" and "s" key functionality and adjust their scales -var s = Controller.getAllActions()[0].inputChannels[0] -var w = Controller.getAllActions()[1].inputChannels[0] +var s = Controller.getAllActions()[0].inputChannels[0]; +var w = Controller.getAllActions()[1].inputChannels[0]; // You must remove an input controller before modifying it so the old input controller isn't registered anymore // removeInputChannel and addInputChannel return true if successful, false otherwise -Controller.removeInputChannel(s) -Controller.removeInputChannel(w) -print(s.scale) -s.action = 1 -s.scale = .01 +Controller.removeInputChannel(s); +Controller.removeInputChannel(w); +print(s.scale); +s.action = 1; +s.scale = .01; -w.action = 0 -w.scale = 10000 -Controller.addInputChannel(s) -Controller.addInputChannel(w) -print(s.scale) +w.action = 0; +w.scale = 10000; +Controller.addInputChannel(s); +Controller.addInputChannel(w); +print(s.scale); // You can get all the available inputs for any device // Each AvailableInput has: // input: the Input itself // inputName: string representing the input -var availableInputs = Controller.getAvailableInputs(1) +var availableInputs = Controller.getAvailableInputs(1); for (i = 0; i < availableInputs.length; i++) { print(availableInputs[i].inputName); } // You can modify key bindings by using these avaiable inputs // This will replace e (up) with 6 -var e = Controller.getAllActions()[5].inputChannels[0] -Controller.removeInputChannel(e) -e.input = availableInputs[6].input -Controller.addInputChannel(e) \ No newline at end of file +var e = Controller.getAllActions()[5].inputChannels[0]; +Controller.removeInputChannel(e); +e.input = availableInputs[6].input; +Controller.addInputChannel(e); \ No newline at end of file From d3a4eec5c5a935bfacd56221672829f246c18e2f Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Tue, 9 Jun 2015 09:56:40 -0700 Subject: [PATCH 09/28] removed dead code, magic numbers --- interface/src/devices/KeyboardMouseDevice.cpp | 8 ++++---- interface/src/ui/UserInputMapper.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index a7e85d28e1..d0f04e5636 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -164,11 +164,11 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input._channel); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; - for (int i = 0; i <= 9; i++) { - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(48 + i)), QKeySequence(Qt::Key(48 + i)).toString())); + for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } - for (int i = 0; i < 26; i++) { - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(65 + i)), QKeySequence(Qt::Key(65 + i)).toString())); + for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { + availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); return availableInputs; diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index 34188ae3f5..9019a1de48 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -142,7 +142,6 @@ public: QVector getAllActions(); QString getActionName(Action action) { return UserInputMapper::_actionNames[(int) action]; } float getActionState(Action action) const { return _actionStates[action]; } -// QVector void assignDefaulActionScales(); // Add input channel to the mapper and check that all the used channels are registered. From 0769593ca89c74a0b84f38e8066e2b43cfa5e42f Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Tue, 9 Jun 2015 10:20:36 -0700 Subject: [PATCH 10/28] added getters/setters for Inputs and InputChannels --- .../ControllerScriptingInterface.cpp | 30 +++++++++++-------- interface/src/ui/UserInputMapper.h | 17 ++++++++++- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index fe2eda12df..2c747195a7 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -46,32 +46,36 @@ QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::In QScriptValue obj = engine->newObject(); obj.setProperty("device", input.getDevice()); obj.setProperty("channel", input.getChannel()); - obj.setProperty("type", input._type); + obj.setProperty("type", (unsigned short) input.getType()); obj.setProperty("id", input.getID()); return obj; } void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { - input._device = object.property("device").toUInt16(); - input._channel = object.property("channel").toUInt16(); - input._type = object.property("type").toUInt16(); - input._id = object.property("id").toInt32(); + input.setDevice(object.property("device").toUInt16()); + input.setChannel(object.property("channel").toUInt16()); + input.setType(object.property("type").toUInt16()); + input.setID(object.property("id").toInt32()); } QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) { QScriptValue obj = engine->newObject(); - obj.setProperty("input", inputToScriptValue(engine, inputChannel._input)); - obj.setProperty("modifier", inputToScriptValue(engine, inputChannel._modifier)); - obj.setProperty("action", inputChannel._action); - obj.setProperty("scale", inputChannel._scale); + obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput())); + obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier())); + obj.setProperty("action", inputChannel.getAction()); + obj.setProperty("scale", inputChannel.getScale()); return obj; } void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) { - inputFromScriptValue(object.property("input"), inputChannel._input); - inputFromScriptValue(object.property("modifier"), inputChannel._modifier); - inputChannel._action = UserInputMapper::Action(object.property("action").toVariant().toInt()); - inputChannel._scale = object.property("scale").toVariant().toFloat(); + UserInputMapper::Input input; + UserInputMapper::Input modifier; + inputFromScriptValue(object.property("input"), input); + inputChannel.setInput(input); + inputFromScriptValue(object.property("modifier"), modifier); + inputChannel.setModifier(modifier); + inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt())); + inputChannel.setScale(object.property("scale").toVariant().toFloat()); } QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { diff --git a/interface/src/ui/UserInputMapper.h b/interface/src/ui/UserInputMapper.h index 9019a1de48..0a08e277db 100755 --- a/interface/src/ui/UserInputMapper.h +++ b/interface/src/ui/UserInputMapper.h @@ -52,8 +52,13 @@ public: uint16 getDevice() const { return _device; } uint16 getChannel() const { return _channel; } uint32 getID() const { return _id; } - ChannelType getType() const { return (ChannelType) _type; } + + void setDevice(uint16 device) { _device = device; } + void setChannel(uint16 channel) { _channel = channel; } + void setType(uint16 type) { _type = type; } + void setID(uint32 ID) { _id = ID; } + bool isButton() const { return getType() == ChannelType::BUTTON; } bool isAxis() const { return getType() == ChannelType::AXIS; } bool isJoint() const { return getType() == ChannelType::JOINT; } @@ -157,6 +162,16 @@ public: Input _modifier = Input(); // make it invalid by default, meaning no modifier Action _action = LONGITUDINAL_BACKWARD; float _scale = 0.0f; + + Input getInput() const { return _input; } + Input getModifier() const { return _modifier; } + Action getAction() const { return _action; } + float getScale() const { return _scale; } + + void setInput(Input input) { _input = input; } + void setModifier(Input modifier) { _modifier = modifier; } + void setAction(Action action) { _action = action; } + void setScale(float scale) { _scale = scale; } InputChannel() {} InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) : From ccb2f17b33f48d46cfa3d1c51325ab04402ff2ec Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 9 Jun 2015 11:21:13 -0700 Subject: [PATCH 11/28] grab.js rotation sort-of works now --- examples/grab.js | 18 +++++++++--------- .../entities/src/EntityActionInterface.cpp | 2 +- libraries/physics/src/ObjectActionSpring.cpp | 3 ++- .../physics/src/PhysicalEntitySimulation.cpp | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index f2c74bb252..8f733e9c9d 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -60,7 +60,7 @@ var gMouseAtRotateStart = { x: 0, y: 0 }; var gBeaconHeight = 0.10; -var gAngularVelocity = ZERO_VEC3; +// var gAngularVelocity = ZERO_VEC3; // TODO: play sounds again when we aren't leaking AudioInjector threads // var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); @@ -194,7 +194,6 @@ function mousePressEvent(event) { var cameraPosition = Camera.getPosition(); gBeaconHeight = Vec3.length(entityProperties.dimensions); - print("gBeaconHeight = " + gBeaconHeight); gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) { // don't allow grabs of things far away @@ -254,7 +253,7 @@ function mouseMoveEvent(event) { gOriginalGravity = entityProperties.gravity; } - var actionArgs; + var actionArgs = {}; if (gGrabMode === "rotate") { var deltaMouse = { x: 0, y: 0 }; @@ -265,12 +264,13 @@ function mouseMoveEvent(event) { var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); - // var axis = Vec3.normalize(axis); - // var ROTATE_STRENGTH = 8.0; // magic number tuned by hand - // gAngularVelocity = Vec3.multiply(ROTATE_STRENGTH, axis); - - var targetRotation = Quat.angleAxis(Vec3.length(axis), Vec3.normalize(axis)); - actionArgs = {targetRotation: targetRotation, angularTimeScale: 1.0}; + axis = Vec3.normalize(axis); + var ROTATE_STRENGTH = 8.0; // magic number tuned by hand + var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); + var deltaQ = Quat.angleAxis(angle, axis); + var qZero = entityProperties.rotation; + var qOne = Quat.multiply(deltaQ, qZero); + actionArgs = {targetRotation: qOne, angularTimeScale: 0.1}; } else { var newTargetPosition; if (gGrabMode === "verticalCylinder") { diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index b3e774df96..399c529aa2 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -131,7 +131,7 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian return glm::quat(); } - return glm::quat(x, y, z, w); + return glm::quat(w, x, y, z); } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index f8b8d92ad5..5badc91f6a 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -64,8 +64,9 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar glm::quat deltaQ = _rotationalTarget * qZeroInverse; glm::vec3 axis = glm::axis(deltaQ); float angle = glm::angle(deltaQ); - glm::vec3 newAngularVelocity = (-angle / _angularTimeScale) * glm::normalize(axis); + glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); + rigidBody->activate(); } } } diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 56d497f8a1..a4d2113be6 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -236,9 +236,9 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE } EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { EntityActionPointer action = nullptr; switch (type) { case ACTION_TYPE_NONE: From 23dab530f911396cea8896f927dbf74d77b5c52b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 9 Jun 2015 14:11:01 -0700 Subject: [PATCH 12/28] fix some glitchy behavior during rotation --- examples/grab.js | 33 +------------------- libraries/physics/src/ObjectActionSpring.cpp | 33 +++++++++++++++----- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index 8f733e9c9d..3355ad65c0 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -21,7 +21,6 @@ var ANGULAR_DAMPING_RATE = 0.40; var gIsGrabbing = false; var gGrabbedEntity = null; var gActionID = null; -var gPrevMouse = {x: 0, y: 0}; var gEntityProperties; var gStartPosition; var gStartRotation; @@ -259,13 +258,12 @@ function mouseMoveEvent(event) { var deltaMouse = { x: 0, y: 0 }; var dx = event.x - gPreviousMouse.x; var dy = event.y - gPreviousMouse.y; - var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); axis = Vec3.normalize(axis); - var ROTATE_STRENGTH = 8.0; // magic number tuned by hand + var ROTATE_STRENGTH = 16.0; // magic number tuned by hand var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); var deltaQ = Quat.angleAxis(angle, axis); var qZero = entityProperties.rotation; @@ -327,37 +325,8 @@ function keyPressEvent(event) { computeNewGrabPlane(); } -// function update(deltaTime) { -// if (!gIsGrabbing) { -// return; -// } - -// var entityProperties = Entities.getEntityProperties(gGrabbedEntity); -// gCurrentPosition = entityProperties.position; -// if (gGrabMode === "rotate") { -// gAngularVelocity = Vec3.subtract(gAngularVelocity, Vec3.multiply(gAngularVelocity, ANGULAR_DAMPING_RATE)); -// Entities.editEntity(gGrabbedEntity, { angularVelocity: gAngularVelocity, }); -// } - -// // always push toward linear grab position, even when rotating -// var newVelocity = ZERO_VEC3; -// var dPosition = Vec3.subtract(gTargetPosition, gCurrentPosition); -// var delta = Vec3.length(dPosition); -// if (delta > CLOSE_ENOUGH) { -// var MAX_POSITION_DELTA = 4.0; -// if (delta > MAX_POSITION_DELTA) { -// dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); -// } -// // desired speed is proportional to displacement by the inverse of timescale -// // (for critically damped motion) -// newVelocity = Vec3.multiply(dPosition, INV_MOVE_TIMESCALE); -// } -// Entities.editEntity(gGrabbedEntity, { velocity: newVelocity, }); -// } - Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); -// Script.update.connect(update); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 5badc91f6a..cf9a226c89 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -60,13 +60,32 @@ void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar // handle rotation if (_rotationalTargetSet) { - glm::quat qZeroInverse = glm::inverse(bulletToGLM(rigidBody->getOrientation())); - glm::quat deltaQ = _rotationalTarget * qZeroInverse; - glm::vec3 axis = glm::axis(deltaQ); - float angle = glm::angle(deltaQ); - glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); - rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); - rigidBody->activate(); + glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); + // if qZero and qOne are too close to each other, we can get NaN for angle. + auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); + const float almostOne = 0.99999; + if (glm::abs(alignmentDot) < almostOne) { + glm::quat target = _rotationalTarget; + if (alignmentDot < 0) { + target = -target; + } + glm::quat qZeroInverse = glm::inverse(bodyRotation); + glm::quat deltaQ = target * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + if (isNaN(angle)) { + qDebug() << "ObjectActionSpring::updateAction angle =" << angle + << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w + << "target-rotation =" + << target.x << target.y << target.z<< target.w; + } + assert(!isNaN(angle)); + glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); + rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); + rigidBody->activate(); + } else { + rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); + } } } } From 6cce8459847567f6508d4cbd0235710418b67ea2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 9 Jun 2015 14:26:44 -0700 Subject: [PATCH 13/28] rotate based on initial mouse-position and initial entity rotation rather than doing small deltas as the mouse moves --- examples/grab.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index 3355ad65c0..6a27e1b73b 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -53,6 +53,7 @@ var gTargetRotation; var gLiftKey = false; // SHIFT var gRotateKey = false; // CONTROL +var gInitialMouse = { x: 0, y: 0 }; var gPreviousMouse = { x: 0, y: 0 }; var gMouseCursorLocation = { x: 0, y: 0 }; var gMouseAtRotateStart = { x: 0, y: 0 }; @@ -173,6 +174,7 @@ function mousePressEvent(event) { if (!event.isLeftButton) { return; } + gInitialMouse = {x: event.x, y: event.y }; gPreviousMouse = {x: event.x, y: event.y }; var pickRay = Camera.computePickRay(event.x, event.y); @@ -189,12 +191,13 @@ function mousePressEvent(event) { var clickedEntity = pickResults.entityID; var entityProperties = Entities.getEntityProperties(clickedEntity) - var objectPosition = entityProperties.position; + gStartPosition = entityProperties.position; + gStartRotation = entityProperties.rotation; var cameraPosition = Camera.getPosition(); gBeaconHeight = Vec3.length(entityProperties.dimensions); gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; - if (Vec3.distance(objectPosition, cameraPosition) > gMaxGrabDistance) { + if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) { // don't allow grabs of things far away return; } @@ -205,20 +208,20 @@ function mousePressEvent(event) { gGrabbedEntity = clickedEntity; gCurrentPosition = entityProperties.position; gOriginalGravity = entityProperties.gravity; - gTargetPosition = objectPosition; + gTargetPosition = gStartPosition; // compute the grab point - var nearestPoint = Vec3.subtract(objectPosition, cameraPosition); + var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition); var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction); nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction); gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint); // compute the grab offset - gGrabOffset = Vec3.subtract(objectPosition, gPointOnPlane); + gGrabOffset = Vec3.subtract(gStartPosition, gPointOnPlane); computeNewGrabPlane(); - updateDropLine(objectPosition); + updateDropLine(gStartPosition); // TODO: play sounds again when we aren't leaking AudioInjector threads //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME }); @@ -256,17 +259,18 @@ function mouseMoveEvent(event) { if (gGrabMode === "rotate") { var deltaMouse = { x: 0, y: 0 }; - var dx = event.x - gPreviousMouse.x; - var dy = event.y - gPreviousMouse.y; + var dx = event.x - gInitialMouse.x; + var dy = event.y - gInitialMouse.y; var orientation = Camera.getOrientation(); var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); axis = Vec3.normalize(axis); - var ROTATE_STRENGTH = 16.0; // magic number tuned by hand + var ROTATE_STRENGTH = 0.4; // magic number tuned by hand var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); var deltaQ = Quat.angleAxis(angle, axis); - var qZero = entityProperties.rotation; + // var qZero = entityProperties.rotation; + var qZero = gStartRotation; var qOne = Quat.multiply(deltaQ, qZero); actionArgs = {targetRotation: qOne, angularTimeScale: 0.1}; } else { From b1a209b9db9e6cdad15ef47c37a47a7cc2208ee7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 9 Jun 2015 16:17:48 -0700 Subject: [PATCH 14/28] pull some common code out of subclasses and into ObjectAction --- .../src/EntityTreeRenderer.cpp | 4 +- libraries/physics/src/ObjectAction.cpp | 22 +++- libraries/physics/src/ObjectAction.h | 11 +- .../physics/src/ObjectActionPullToPoint.cpp | 39 ++----- .../physics/src/ObjectActionPullToPoint.h | 3 +- libraries/physics/src/ObjectActionSpring.cpp | 106 +++++++----------- libraries/physics/src/ObjectActionSpring.h | 3 +- 7 files changed, 90 insertions(+), 98 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 98cbc1f845..3e83c6f559 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1012,7 +1012,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { checkAndCallPreload(entityID); auto entity = static_cast(_tree)->findEntityByID(entityID); - addEntityToScene(entity); + if (entity) { + addEntityToScene(entity); + } } void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 6ff4098ba8..5f67db4355 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -24,7 +24,27 @@ ObjectAction::~ObjectAction() { } void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - qDebug() << "ObjectAction::updateAction called"; + if (!_active) { + return; + } + if (!_ownerEntity) { + qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; + return; + } + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + return; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + + if (physicsInfo) { + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (rigidBody) { + updateActionWorker(collisionWorld, deltaTimeStep, motionState, rigidBody); + } + } + unlock(); } void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 10b086c07d..7ff11b8ba0 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -13,11 +13,14 @@ #ifndef hifi_ObjectAction_h #define hifi_ObjectAction_h -#include - #include +#include + #include +#include "ObjectMotionState.h" +#include "BulletUtil.h" + class ObjectAction : public btActionInterface, public EntityActionInterface { public: @@ -30,6 +33,10 @@ public: virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } virtual bool updateArguments(QVariantMap arguments) { return false; } + // this is called from updateAction and should be overridden by subclasses + virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, + ObjectMotionState* motionState, btRigidBody* rigidBody) {} + // these are from btActionInterface virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); virtual void debugDraw(btIDebugDraw* debugDrawer); diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 28c0e08bd9..2e66b948f5 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ObjectMotionState.h" -#include "BulletUtil.h" - #include "ObjectActionPullToPoint.h" ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) : @@ -27,33 +24,17 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { #endif } -void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - if (!_ownerEntity) { - qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; - return; +void ObjectActionPullToPoint::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, + ObjectMotionState* motionState, btRigidBody* rigidBody) { + glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * _speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); } - if (!tryLockForRead()) { - // don't risk hanging the thread running the physics simulation - return; - } - void* physicsInfo = _ownerEntity->getPhysicsInfo(); - - if (_active && physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState->getRigidBody(); - if (rigidBody) { - glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); - float offsetLength = glm::length(offset); - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * _speed; - rigidBody->setLinearVelocity(glmToBullet(newVelocity)); - rigidBody->activate(); - } else { - rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); - } - } - } - unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h index 3aca70d640..55fd748921 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.h +++ b/libraries/physics/src/ObjectActionPullToPoint.h @@ -23,7 +23,8 @@ public: virtual ~ObjectActionPullToPoint(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, + ObjectMotionState* motionState, btRigidBody* rigidBody); private: diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index cf9a226c89..101af665f7 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ObjectMotionState.h" -#include "BulletUtil.h" - #include "ObjectActionSpring.h" ObjectActionSpring::ObjectActionSpring(QUuid id, EntityItemPointer ownerEntity) : @@ -27,69 +24,52 @@ ObjectActionSpring::~ObjectActionSpring() { #endif } -void ObjectActionSpring::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - if (!_ownerEntity) { - qDebug() << "ObjectActionSpring::updateAction no owner entity"; - return; - } - if (!tryLockForRead()) { - // don't risk hanging the thread running the physics simulation - return; - } - void* physicsInfo = _ownerEntity->getPhysicsInfo(); +void ObjectActionSpring::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, + ObjectMotionState* motionState, btRigidBody* rigidBody) { + // handle the linear part + if (_positionalTargetSet) { + glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); + float offsetLength = glm::length(offset); + float speed = offsetLength / _linearTimeScale; - if (_active && physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState->getRigidBody(); - if (rigidBody) { - // handle the linear part - if (_positionalTargetSet) { - glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); - float offsetLength = glm::length(offset); - float speed = offsetLength / _linearTimeScale; - - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * speed; - rigidBody->setLinearVelocity(glmToBullet(newVelocity)); - // void setAngularVelocity (const btVector3 &ang_vel); - rigidBody->activate(); - } else { - rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); - } - } - - // handle rotation - if (_rotationalTargetSet) { - glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); - // if qZero and qOne are too close to each other, we can get NaN for angle. - auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); - const float almostOne = 0.99999; - if (glm::abs(alignmentDot) < almostOne) { - glm::quat target = _rotationalTarget; - if (alignmentDot < 0) { - target = -target; - } - glm::quat qZeroInverse = glm::inverse(bodyRotation); - glm::quat deltaQ = target * qZeroInverse; - glm::vec3 axis = glm::axis(deltaQ); - float angle = glm::angle(deltaQ); - if (isNaN(angle)) { - qDebug() << "ObjectActionSpring::updateAction angle =" << angle - << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w - << "target-rotation =" - << target.x << target.y << target.z<< target.w; - } - assert(!isNaN(angle)); - glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); - rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); - rigidBody->activate(); - } else { - rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); - } - } + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + rigidBody->setLinearVelocity(glmToBullet(newVelocity)); + rigidBody->activate(); + } else { + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + } + } + + // handle rotation + if (_rotationalTargetSet) { + glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); + // if qZero and qOne are too close to each other, we can get NaN for angle. + auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); + const float almostOne = 0.99999; + if (glm::abs(alignmentDot) < almostOne) { + glm::quat target = _rotationalTarget; + if (alignmentDot < 0) { + target = -target; + } + glm::quat qZeroInverse = glm::inverse(bodyRotation); + glm::quat deltaQ = target * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + if (isNaN(angle)) { + qDebug() << "ObjectActionSpring::updateAction angle =" << angle + << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w + << "target-rotation =" + << target.x << target.y << target.z<< target.w; + } + assert(!isNaN(angle)); + glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); + rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); + rigidBody->activate(); + } else { + rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); } } - unlock(); } diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index b211259866..0ce06ab43e 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -23,7 +23,8 @@ public: virtual ~ObjectActionSpring(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); + virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, + ObjectMotionState* motionState, btRigidBody* rigidBody); private: From 3dcc6c9b8ce28722928c891feae1d21009842e21 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 10 Jun 2015 12:04:44 -0700 Subject: [PATCH 15/28] make action-factory interface and subclass it in Interface. this allows an action to use avatar data. the login the AvatarActionHold is still bogus. --- interface/src/Application.cpp | 5 +- interface/src/InterfaceActionFactory.cpp | 49 ++++++++ interface/src/InterfaceActionFactory.h | 28 +++++ interface/src/avatar/AvatarActionHold.cpp | 111 ++++++++++++++++++ interface/src/avatar/AvatarActionHold.h | 39 ++++++ .../src/EntityActionFactoryInterface.h | 33 ++++++ .../entities/src/EntityActionInterface.cpp | 2 + .../entities/src/EntityActionInterface.h | 14 ++- libraries/entities/src/EntityItem.h | 5 +- .../entities/src/EntityScriptingInterface.cpp | 5 +- libraries/entities/src/EntitySimulation.h | 5 +- libraries/physics/src/ObjectAction.cpp | 94 +++++++++++++-- libraries/physics/src/ObjectAction.h | 15 ++- .../physics/src/ObjectActionPullToPoint.cpp | 13 +- .../physics/src/ObjectActionPullToPoint.h | 3 +- libraries/physics/src/ObjectActionSpring.cpp | 13 +- libraries/physics/src/ObjectActionSpring.h | 3 +- .../physics/src/PhysicalEntitySimulation.cpp | 30 +---- .../physics/src/PhysicalEntitySimulation.h | 4 - 19 files changed, 413 insertions(+), 58 deletions(-) create mode 100644 interface/src/InterfaceActionFactory.cpp create mode 100644 interface/src/InterfaceActionFactory.h create mode 100644 interface/src/avatar/AvatarActionHold.cpp create mode 100644 interface/src/avatar/AvatarActionHold.h create mode 100644 libraries/entities/src/EntityActionFactoryInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 188ae6eaf9..0c90fdb9bd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -100,6 +100,7 @@ #include "ModelPackager.h" #include "Util.h" #include "InterfaceLogging.h" +#include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" @@ -257,6 +258,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); Setting::init(); @@ -293,7 +295,8 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); - auto pathUtils = DependencyManager::set(); + auto pathUtils = DependencyManager::set(); + auto actionFactory = DependencyManager::set(); return true; } diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp new file mode 100644 index 0000000000..08afc3581c --- /dev/null +++ b/interface/src/InterfaceActionFactory.cpp @@ -0,0 +1,49 @@ +// +// InterfaceActionFactory.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-2 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + + +#include +#include +#include + +#include "InterfaceActionFactory.h" + + +EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityActionPointer action = nullptr; + switch (type) { + case ACTION_TYPE_NONE: + return nullptr; + case ACTION_TYPE_PULL_TO_POINT: + action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); + break; + case ACTION_TYPE_SPRING: + action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); + break; + case ACTION_TYPE_HOLD: + action = (EntityActionPointer) new AvatarActionHold(id, ownerEntity); + break; + } + + bool ok = action->updateArguments(arguments); + if (ok) { + ownerEntity->addAction(simulation, action); + return action; + } + + action = nullptr; + return action; +} diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceActionFactory.h new file mode 100644 index 0000000000..5848df4635 --- /dev/null +++ b/interface/src/InterfaceActionFactory.h @@ -0,0 +1,28 @@ +// +// InterfaceActionFactory.cpp +// interface/src/ +// +// Created by Seth Alves on 2015-6-10 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_InterfaceActionFactory_h +#define hifi_InterfaceActionFactory_h + +#include "EntityActionFactoryInterface.h" + +class InterfaceActionFactory : public EntityActionFactoryInterface { +public: + InterfaceActionFactory() : EntityActionFactoryInterface() { } + virtual ~InterfaceActionFactory() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments); +}; + +#endif // hifi_InterfaceActionFactory_h diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp new file mode 100644 index 0000000000..6d4164367a --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -0,0 +1,111 @@ +// +// AvatarActionHold.cpp +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "avatar/MyAvatar.h" +#include "avatar/AvatarManager.h" + +#include "AvatarActionHold.h" + +AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) : + ObjectAction(id, ownerEntity) { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::AvatarActionHold"; + #endif +} + +AvatarActionHold::~AvatarActionHold() { + #if WANT_DEBUG + qDebug() << "AvatarActionHold::~AvatarActionHold"; + #endif +} + +void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + // handle the linear part + if (_linearOffsetSet) { + } + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + + glm::vec3 palmPosition = myAvatar->getRightPalmPosition(); + glm::vec3 position = getPosition(); + glm::vec3 positionalTarget = palmPosition + _linearOffset; + glm::vec3 offset = positionalTarget - position; + + float offsetLength = glm::length(offset); + float speed = offsetLength / _timeScale; + + if (offsetLength > IGNORE_POSITION_DELTA) { + glm::vec3 newVelocity = glm::normalize(offset) * speed; + setLinearVelocity(newVelocity); + } else { + setLinearVelocity(glm::vec3(0.0f)); + } + + + // handle rotation + if (_angularOffsetSet) { + glm::quat bodyRotation = getRotation(); + // if qZero and qOne are too close to each other, we can get NaN for angle. + auto alignmentDot = glm::dot(bodyRotation, _angularOffset); + const float almostOne = 0.99999; + if (glm::abs(alignmentDot) < almostOne) { + glm::quat target = _angularOffset; + if (alignmentDot < 0) { + target = -target; + } + glm::quat qZeroInverse = glm::inverse(bodyRotation); + glm::quat deltaQ = target * qZeroInverse; + glm::vec3 axis = glm::axis(deltaQ); + float angle = glm::angle(deltaQ); + if (isNaN(angle)) { + qDebug() << "AvatarActionHold::updateAction angle =" << angle + << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w + << "target-rotation =" + << target.x << target.y << target.z<< target.w; + } + assert(!isNaN(angle)); + glm::vec3 newAngularVelocity = (angle / _timeScale) * glm::normalize(axis); + setAngularVelocity(newAngularVelocity); + } else { + setAngularVelocity(glm::vec3(0.0f)); + } + } +} + + +bool AvatarActionHold::updateArguments(QVariantMap arguments) { + // targets are required, spring-constants are optional + bool ptOk = true; + glm::vec3 linearOffset = + EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false); + + bool rtOk = true; + glm::quat angularOffset = + EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false); + + lockForWrite(); + + _linearOffsetSet = _angularOffsetSet = false; + + if (ptOk) { + _linearOffset = linearOffset; + _linearOffsetSet = true; + } + + if (rtOk) { + _angularOffset = angularOffset; + _angularOffsetSet = true; + } + + _active = true; + unlock(); + return true; +} diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h new file mode 100644 index 0000000000..19e6e8df55 --- /dev/null +++ b/interface/src/avatar/AvatarActionHold.h @@ -0,0 +1,39 @@ +// +// AvatarActionHold.h +// interface/src/avatar/ +// +// Created by Seth Alves 2015-6-9 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarActionHold_h +#define hifi_AvatarActionHold_h + +#include + +#include +#include + +class AvatarActionHold : public ObjectAction { +public: + AvatarActionHold(QUuid id, EntityItemPointer ownerEntity); + virtual ~AvatarActionHold(); + + virtual bool updateArguments(QVariantMap arguments); + virtual void updateActionWorker(float deltaTimeStep); + +private: + + glm::vec3 _linearOffset; + bool _linearOffsetSet; + + glm::quat _angularOffset; + bool _angularOffsetSet; + + float _timeScale = 0.01; +}; + +#endif // hifi_AvatarActionHold_h diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h new file mode 100644 index 0000000000..2347eff525 --- /dev/null +++ b/libraries/entities/src/EntityActionFactoryInterface.h @@ -0,0 +1,33 @@ +// +// EntityActionFactoryInterface.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-2 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityActionFactoryInterface_h +#define hifi_EntityActionFactoryInterface_h + +#include + +#include "EntityActionInterface.h" + +class EntityActionFactoryInterface : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + + public: + EntityActionFactoryInterface() { } + virtual ~EntityActionFactoryInterface() { } + virtual EntityActionPointer factory(EntitySimulation* simulation, + EntityActionType type, + QUuid id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { assert(false); } +}; + +#endif // hifi_EntityActionFactoryInterface_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index 399c529aa2..09bb933488 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -38,6 +38,8 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "pullToPoint"; case ACTION_TYPE_SPRING: return "spring"; + case ACTION_TYPE_HOLD: + return "hold"; } assert(false); return "none"; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index a0a3db9b68..50f862c535 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -14,13 +14,16 @@ #include +#include "EntityItem.h" + class EntitySimulation; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString ACTION_TYPE_NONE, ACTION_TYPE_PULL_TO_POINT, - ACTION_TYPE_SPRING + ACTION_TYPE_SPRING, + ACTION_TYPE_HOLD }; @@ -40,6 +43,14 @@ public: static QString actionTypeToString(EntityActionType actionType); protected: + virtual glm::vec3 getPosition() = 0; + virtual void setPosition(glm::vec3 position) = 0; + virtual glm::quat getRotation() = 0; + virtual void setRotation(glm::quat rotation) = 0; + virtual glm::vec3 getLinearVelocity() = 0; + virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0; + virtual glm::vec3 getAngularVelocity() = 0; + virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0; static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); @@ -49,6 +60,7 @@ protected: QString argumentName, bool& ok, bool required = true); }; + typedef std::shared_ptr EntityActionPointer; #endif // hifi_EntityActionInterface_h diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 77a6627853..7e56fb8ec4 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -28,13 +28,16 @@ #include "EntityItemID.h" #include "EntityItemProperties.h" #include "EntityItemPropertiesDefaults.h" -#include "EntityActionInterface.h" #include "EntityTypes.h" class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; +class EntityActionInterface; +typedef std::shared_ptr EntityActionPointer; + + namespace render { class Scene; class PendingChanges; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 836e7f5225..6f6633ce0f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,6 +17,8 @@ #include "ZoneEntityItem.h" #include "EntitiesLogging.h" #include "EntitySimulation.h" +#include "EntityActionInterface.h" +#include "EntityActionFactoryInterface.h" #include "EntityScriptingInterface.h" @@ -491,12 +493,13 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments) { QUuid actionID = QUuid::createUuid(); + auto actionFactory = DependencyManager::get(); bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { return false; } - if (simulation->actionFactory(actionType, actionID, entity, arguments)) { + if (actionFactory->factory(simulation, actionType, actionID, entity, arguments)) { return true; } return false; diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index 0c9b3efee6..7d244086e5 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -18,6 +18,7 @@ #include +#include "EntityActionInterface.h" #include "EntityItem.h" #include "EntityTree.h" @@ -56,10 +57,6 @@ public: friend class EntityTree; - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { return nullptr; } virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; } virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; } virtual void removeActions(QList actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 5f67db4355..8f01a90410 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -35,15 +35,9 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta // don't risk hanging the thread running the physics simulation return; } - void* physicsInfo = _ownerEntity->getPhysicsInfo(); - if (physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState->getRigidBody(); - if (rigidBody) { - updateActionWorker(collisionWorld, deltaTimeStep, motionState, rigidBody); - } - } + updateActionWorker(deltaTimeStep); + unlock(); } @@ -53,3 +47,87 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const { simulation->removeAction(_id); } + +btRigidBody* ObjectAction::getRigidBody() { + if (!_ownerEntity) { + return nullptr; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return nullptr; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + return motionState->getRigidBody(); +} + +glm::vec3 ObjectAction::getPosition() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getCenterOfMassPosition()); +} + +void ObjectAction::setPosition(glm::vec3 position) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + // XXX + // void setWorldTransform (const btTransform &worldTrans) + assert(false); + rigidBody->activate(); +} + +glm::quat ObjectAction::getRotation() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::quat(0.0f, 0.0f, 0.0f, 1.0f); + } + return bulletToGLM(rigidBody->getOrientation()); +} + +void ObjectAction::setRotation(glm::quat rotation) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + // XXX + // void setWorldTransform (const btTransform &worldTrans) + assert(false); + rigidBody->activate(); +} + +glm::vec3 ObjectAction::getLinearVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getLinearVelocity()); +} + +void ObjectAction::setLinearVelocity(glm::vec3 linearVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + rigidBody->activate(); +} + +glm::vec3 ObjectAction::getAngularVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getAngularVelocity()); +} + +void ObjectAction::setAngularVelocity(glm::vec3 angularVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setAngularVelocity(glmToBullet(angularVelocity)); + rigidBody->activate(); +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 7ff11b8ba0..a834a54864 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -18,8 +18,10 @@ #include #include + #include "ObjectMotionState.h" #include "BulletUtil.h" +#include "EntityActionInterface.h" class ObjectAction : public btActionInterface, public EntityActionInterface { @@ -34,8 +36,7 @@ public: virtual bool updateArguments(QVariantMap arguments) { return false; } // this is called from updateAction and should be overridden by subclasses - virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, - ObjectMotionState* motionState, btRigidBody* rigidBody) {} + virtual void updateActionWorker(float deltaTimeStep) {} // these are from btActionInterface virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep); @@ -46,6 +47,16 @@ private: QReadWriteLock _lock; protected: + btRigidBody* getRigidBody(); + virtual glm::vec3 getPosition(); + virtual void setPosition(glm::vec3 position); + virtual glm::quat getRotation(); + virtual void setRotation(glm::quat rotation); + virtual glm::vec3 getLinearVelocity(); + virtual void setLinearVelocity(glm::vec3 linearVelocity); + virtual glm::vec3 getAngularVelocity(); + virtual void setAngularVelocity(glm::vec3 angularVelocity); + bool tryLockForRead() { return _lock.tryLockForRead(); } void lockForWrite() { _lock.lockForWrite(); } void unlock() { _lock.unlock(); } diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 2e66b948f5..1069a2b9cf 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -24,8 +24,17 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { #endif } -void ObjectActionPullToPoint::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, - ObjectMotionState* motionState, btRigidBody* rigidBody) { +void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) { + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + return; + } + glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition()); float offsetLength = glm::length(offset); if (offsetLength > IGNORE_POSITION_DELTA) { diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h index 55fd748921..2596f13515 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.h +++ b/libraries/physics/src/ObjectActionPullToPoint.h @@ -23,8 +23,7 @@ public: virtual ~ObjectActionPullToPoint(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, - ObjectMotionState* motionState, btRigidBody* rigidBody); + virtual void updateActionWorker(float deltaTimeStep); private: diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 101af665f7..aea4dafbb8 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -24,8 +24,17 @@ ObjectActionSpring::~ObjectActionSpring() { #endif } -void ObjectActionSpring::updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, - ObjectMotionState* motionState, btRigidBody* rigidBody) { +void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { + void* physicsInfo = _ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState->getRigidBody(); + if (!rigidBody) { + return; + } + // handle the linear part if (_positionalTargetSet) { glm::vec3 offset = _positionalTarget - bulletToGLM(rigidBody->getCenterOfMassPosition()); diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index 0ce06ab43e..c5cbbe6126 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -23,8 +23,7 @@ public: virtual ~ObjectActionSpring(); virtual bool updateArguments(QVariantMap arguments); - virtual void updateActionWorker(btCollisionWorld* collisionWorld, btScalar deltaTimeStep, - ObjectMotionState* motionState, btRigidBody* rigidBody); + virtual void updateActionWorker(float deltaTimeStep); private: diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index a4d2113be6..c68b993fe2 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -9,11 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + #include "PhysicsHelpers.h" #include "PhysicsLogging.h" #include "ShapeManager.h" -#include "ObjectActionPullToPoint.h" -#include "ObjectActionSpring.h" #include "PhysicalEntitySimulation.h" @@ -235,32 +235,6 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE } } -EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { - EntityActionPointer action = nullptr; - switch (type) { - case ACTION_TYPE_NONE: - return nullptr; - case ACTION_TYPE_PULL_TO_POINT: - action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity); - break; - case ACTION_TYPE_SPRING: - action = (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); - break; - } - - bool ok = action->updateArguments(arguments); - if (ok) { - ownerEntity->addAction(this, action); - return action; - } - - action = nullptr; - return action; -} - void PhysicalEntitySimulation::applyActionChanges() { if (_physicsEngine) { foreach (EntityActionPointer actionToAdd, _actionsToAdd) { diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 82b0f8ad51..81ab9f5cce 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -32,10 +32,6 @@ public: void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender); - virtual EntityActionPointer actionFactory(EntityActionType type, - QUuid id, - EntityItemPointer ownerEntity, - QVariantMap arguments); virtual void applyActionChanges(); protected: // only called by EntitySimulation From eccf4eb8a8eb8ab048d205a18f46429f8f2545da Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 10 Jun 2015 17:05:49 -0700 Subject: [PATCH 16/28] hold action works --- interface/src/avatar/AvatarActionHold.cpp | 100 +++++++----------- interface/src/avatar/AvatarActionHold.h | 14 +-- .../entities/src/EntityActionInterface.cpp | 3 + libraries/physics/src/ObjectAction.cpp | 7 -- libraries/physics/src/ObjectAction.h | 2 +- .../physics/src/ObjectActionPullToPoint.cpp | 9 ++ libraries/physics/src/ObjectActionSpring.cpp | 12 +++ libraries/physics/src/ObjectActionSpring.h | 2 +- 8 files changed, 67 insertions(+), 82 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 6d4164367a..74a583df58 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -15,7 +15,7 @@ #include "AvatarActionHold.h" AvatarActionHold::AvatarActionHold(QUuid id, EntityItemPointer ownerEntity) : - ObjectAction(id, ownerEntity) { + ObjectActionSpring(id, ownerEntity) { #if WANT_DEBUG qDebug() << "AvatarActionHold::AvatarActionHold"; #endif @@ -28,83 +28,57 @@ AvatarActionHold::~AvatarActionHold() { } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - // handle the linear part - if (_linearOffsetSet) { - } - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition = myAvatar->getRightPalmPosition(); - glm::vec3 position = getPosition(); - glm::vec3 positionalTarget = palmPosition + _linearOffset; - glm::vec3 offset = positionalTarget - position; - float offsetLength = glm::length(offset); - float speed = offsetLength / _timeScale; + auto rotation = myAvatar->getWorldAlignedOrientation(); + auto offset = rotation * _relativePosition; + auto position = palmPosition + offset; + rotation *= _relativeRotation; - if (offsetLength > IGNORE_POSITION_DELTA) { - glm::vec3 newVelocity = glm::normalize(offset) * speed; - setLinearVelocity(newVelocity); - } else { - setLinearVelocity(glm::vec3(0.0f)); - } + lockForWrite(); + _positionalTarget = position; + _rotationalTarget = rotation; + unlock(); - - // handle rotation - if (_angularOffsetSet) { - glm::quat bodyRotation = getRotation(); - // if qZero and qOne are too close to each other, we can get NaN for angle. - auto alignmentDot = glm::dot(bodyRotation, _angularOffset); - const float almostOne = 0.99999; - if (glm::abs(alignmentDot) < almostOne) { - glm::quat target = _angularOffset; - if (alignmentDot < 0) { - target = -target; - } - glm::quat qZeroInverse = glm::inverse(bodyRotation); - glm::quat deltaQ = target * qZeroInverse; - glm::vec3 axis = glm::axis(deltaQ); - float angle = glm::angle(deltaQ); - if (isNaN(angle)) { - qDebug() << "AvatarActionHold::updateAction angle =" << angle - << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w - << "target-rotation =" - << target.x << target.y << target.z<< target.w; - } - assert(!isNaN(angle)); - glm::vec3 newAngularVelocity = (angle / _timeScale) * glm::normalize(axis); - setAngularVelocity(newAngularVelocity); - } else { - setAngularVelocity(glm::vec3(0.0f)); - } - } + ObjectActionSpring::updateActionWorker(deltaTimeStep); } bool AvatarActionHold::updateArguments(QVariantMap arguments) { - // targets are required, spring-constants are optional - bool ptOk = true; - glm::vec3 linearOffset = - EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ptOk, false); - - bool rtOk = true; - glm::quat angularOffset = - EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", rtOk, false); + bool rPOk = true; + glm::vec3 relativePosition = + EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", rPOk, false); + bool rROk = true; + glm::quat relativeRotation = + EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", rROk, false); + bool tSOk = true; + float timeScale = + EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false); lockForWrite(); - - _linearOffsetSet = _angularOffsetSet = false; - - if (ptOk) { - _linearOffset = linearOffset; - _linearOffsetSet = true; + if (rPOk) { + _relativePosition = relativePosition; + } else { + _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f); } - if (rtOk) { - _angularOffset = angularOffset; - _angularOffsetSet = true; + if (rROk) { + _relativeRotation = relativeRotation; + } else { + _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f); } + if (tSOk) { + _linearTimeScale = timeScale; + _angularTimeScale = timeScale; + } else { + _linearTimeScale = 0.2; + _angularTimeScale = 0.2; + } + + _positionalTargetSet = true; + _rotationalTargetSet = true; _active = true; unlock(); return true; diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 19e6e8df55..f92ea94aaa 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -15,9 +15,9 @@ #include #include -#include +#include -class AvatarActionHold : public ObjectAction { +class AvatarActionHold : public ObjectActionSpring { public: AvatarActionHold(QUuid id, EntityItemPointer ownerEntity); virtual ~AvatarActionHold(); @@ -26,14 +26,8 @@ public: virtual void updateActionWorker(float deltaTimeStep); private: - - glm::vec3 _linearOffset; - bool _linearOffsetSet; - - glm::quat _angularOffset; - bool _angularOffsetSet; - - float _timeScale = 0.01; + glm::vec3 _relativePosition; + glm::quat _relativeRotation; }; #endif // hifi_AvatarActionHold_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index 09bb933488..b293d609c2 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -25,6 +25,9 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "spring") { return ACTION_TYPE_SPRING; } + if (normalizedActionTypeString == "hold") { + return ACTION_TYPE_HOLD; + } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 8f01a90410..ee7ba9ce7c 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -31,14 +31,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta qDebug() << "ObjectActionPullToPoint::updateAction no owner entity"; return; } - if (!tryLockForRead()) { - // don't risk hanging the thread running the physics simulation - return; - } - updateActionWorker(deltaTimeStep); - - unlock(); } void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index a834a54864..0fd7383e6f 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -47,7 +47,7 @@ private: QReadWriteLock _lock; protected: - btRigidBody* getRigidBody(); + virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); virtual void setPosition(glm::vec3 position); virtual glm::quat getRotation(); diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp index 1069a2b9cf..053bef6a03 100644 --- a/libraries/physics/src/ObjectActionPullToPoint.cpp +++ b/libraries/physics/src/ObjectActionPullToPoint.cpp @@ -25,13 +25,20 @@ ObjectActionPullToPoint::~ObjectActionPullToPoint() { } void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + return; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); if (!physicsInfo) { + unlock(); return; } ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { + unlock(); return; } @@ -44,6 +51,8 @@ void ObjectActionPullToPoint::updateActionWorker(btScalar deltaTimeStep) { } else { rigidBody->setLinearVelocity(glmToBullet(glm::vec3())); } + + unlock(); } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index aea4dafbb8..6883e73766 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -25,13 +25,23 @@ ObjectActionSpring::~ObjectActionSpring() { } void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { + if (!tryLockForRead()) { + // don't risk hanging the thread running the physics simulation + qDebug() << "ObjectActionSpring::updateActionWorker lock failed"; + return; + } + void* physicsInfo = _ownerEntity->getPhysicsInfo(); if (!physicsInfo) { + unlock(); + qDebug() << "ObjectActionSpring::updateActionWorker no physicsInfo"; return; } ObjectMotionState* motionState = static_cast(physicsInfo); btRigidBody* rigidBody = motionState->getRigidBody(); if (!rigidBody) { + unlock(); + qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; return; } @@ -79,6 +89,8 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { rigidBody->setAngularVelocity(glmToBullet(glm::vec3(0.0f))); } } + + unlock(); } diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index c5cbbe6126..9f3df0fdf8 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -25,7 +25,7 @@ public: virtual bool updateArguments(QVariantMap arguments); virtual void updateActionWorker(float deltaTimeStep); -private: +protected: glm::vec3 _positionalTarget; float _linearTimeScale; From 45c7cd4929681f09f3209a8a7f5dc074b0ed9b1f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 10 Jun 2015 18:48:51 -0700 Subject: [PATCH 17/28] respond to code review, add a simple hold-a-stick script --- examples/stick.js | 18 ++++++++++++++++++ libraries/entities/src/EntityActionInterface.h | 5 +++-- .../entities/src/EntityScriptingInterface.cpp | 6 ++++++ libraries/physics/src/ObjectActionSpring.cpp | 13 +++---------- libraries/render/src/render/Scene.h | 2 +- 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 examples/stick.js diff --git a/examples/stick.js b/examples/stick.js new file mode 100644 index 0000000000..02101f59e1 --- /dev/null +++ b/examples/stick.js @@ -0,0 +1,18 @@ +var stickID = null; +// sometimes if this is run immediately the stick doesn't get created? use a timer. +Script.setTimeout(function() { + var stickID = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", + compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", + dimensions: {x: .11, y: .11, z: .59}, + position: MyAvatar.getRightPalmPosition(), + rotation: MyAvatar.orientation, + damping: .1, + collisionsWillMove: true + }); + Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15}); +}, 3000); + +function cleanup() { Entities.deleteEntity(stickID); } +Script.scriptEnding.connect(cleanup); diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 50f862c535..457db8f5c3 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -36,8 +36,6 @@ public: virtual const EntityItemPointer& getOwnerEntity() const = 0; virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; virtual bool updateArguments(QVariantMap arguments) = 0; - // virtual QByteArray serialize() = 0; - // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data); static EntityActionType actionTypeFromString(QString actionTypeString); static QString actionTypeToString(EntityActionType actionType); @@ -52,6 +50,9 @@ protected: virtual glm::vec3 getAngularVelocity() = 0; virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0; + // these look in the arguments map for a named argument. if it's not found or isn't well formed, + // ok will be set to false (note that it's never set to true -- set it to true before calling these). + // if required is true, failure to extract an argument will cause a warning to be printed. static glm::vec3 extractVec3Argument (QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); static glm::quat extractQuatArgument (QString objectName, QVariantMap arguments, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 6f6633ce0f..a091c786b7 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -495,6 +495,12 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, QUuid actionID = QUuid::createUuid(); auto actionFactory = DependencyManager::get(); bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + // create this action even if the entity doesn't have physics info. it will often be the + // case that a script adds an action immediately after an object is created, and the physicsInfo + // is computed asynchronously. + // if (!entity->getPhysicsInfo()) { + // return false; + // } EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { return false; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 6883e73766..8eb4f7f652 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -34,7 +34,6 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { void* physicsInfo = _ownerEntity->getPhysicsInfo(); if (!physicsInfo) { unlock(); - qDebug() << "ObjectActionSpring::updateActionWorker no physicsInfo"; return; } ObjectMotionState* motionState = static_cast(physicsInfo); @@ -65,7 +64,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { glm::quat bodyRotation = bulletToGLM(rigidBody->getOrientation()); // if qZero and qOne are too close to each other, we can get NaN for angle. auto alignmentDot = glm::dot(bodyRotation, _rotationalTarget); - const float almostOne = 0.99999; + const float almostOne = 0.99999f; if (glm::abs(alignmentDot) < almostOne) { glm::quat target = _rotationalTarget; if (alignmentDot < 0) { @@ -75,12 +74,6 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { glm::quat deltaQ = target * qZeroInverse; glm::vec3 axis = glm::axis(deltaQ); float angle = glm::angle(deltaQ); - if (isNaN(angle)) { - qDebug() << "ObjectActionSpring::updateAction angle =" << angle - << "body-rotation =" << bodyRotation.x << bodyRotation.y << bodyRotation.z << bodyRotation.w - << "target-rotation =" - << target.x << target.y << target.z<< target.w; - } assert(!isNaN(angle)); glm::vec3 newAngularVelocity = (angle / _angularTimeScale) * glm::normalize(axis); rigidBody->setAngularVelocity(glmToBullet(newAngularVelocity)); @@ -130,7 +123,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { if (pscOk) { _linearTimeScale = linearTimeScale; } else { - _linearTimeScale = 0.1; + _linearTimeScale = 0.1f; } } @@ -141,7 +134,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { if (rscOk) { _angularTimeScale = angularTimeScale; } else { - _angularTimeScale = 0.1; + _angularTimeScale = 0.1f; } } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 8cb29609ba..b9481d367e 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -245,7 +245,7 @@ public: void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Shape Type Interface - const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); } + const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } protected: PayloadPointer _payload; From 93c0bd63e962bc6673371e00bc9a1555570617e6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 10 Jun 2015 18:54:07 -0700 Subject: [PATCH 18/28] fix debug rendering transforms --- .../entities-renderer/src/RenderableBoxEntityItem.cpp | 2 +- .../src/RenderableDebugableEntityItem.cpp | 9 +++------ .../entities-renderer/src/RenderableLightEntityItem.cpp | 2 +- .../entities-renderer/src/RenderableSphereEntityItem.cpp | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 6ddf44b82d..066aaa8b31 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -33,7 +33,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); + batch.setModelTransform(getTransform()); // we want to include the scale as well DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor); RenderableDebugableEntityItem::render(this, args); diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp index fecc574af2..32e30c7e96 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp @@ -24,9 +24,7 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render float puffedOut, glm::vec4& color) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - Transform transform = entity->getTransformToCenter(); - //transform.postScale(entity->getDimensions()); - batch.setModelTransform(transform); + batch.setModelTransform(entity->getTransform()); // we want to include the scale as well DependencyManager::get()->renderWireCube(batch, 1.0f + puffedOut, color); } @@ -34,10 +32,9 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - Transform transform = entity->getTransformToCenter(); - transform.postScale(entity->getDimensions()); - batch.setModelTransform(transform); + batch.setModelTransform(entity->getTransform()); // we want to include the scale as well + auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID); diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 819989d5ec..8a84c167c5 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -49,7 +49,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) { #ifdef WANT_DEBUG Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); + batch.setModelTransform(getTransform()); DependencyManager::get()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f)); #endif }; diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index b0aaebb2c8..5ba9003d43 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -39,7 +39,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransformToCenter()); + batch.setModelTransform(getTransform()); // use a transform with scale, rotation, registration point and translation DependencyManager::get()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); RenderableDebugableEntityItem::render(this, args); From ba4fd7adf6b0b3238b2f82e043ddbf7eb49e5d30 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 10 Jun 2015 19:23:01 -0700 Subject: [PATCH 19/28] windows didn't like EntityActionFactoryInterface::factory not returning an error --- libraries/entities/src/EntityActionFactoryInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityActionFactoryInterface.h index 2347eff525..9820313aae 100644 --- a/libraries/entities/src/EntityActionFactoryInterface.h +++ b/libraries/entities/src/EntityActionFactoryInterface.h @@ -27,7 +27,7 @@ class EntityActionFactoryInterface : public QObject, public Dependency { EntityActionType type, QUuid id, EntityItemPointer ownerEntity, - QVariantMap arguments) { assert(false); } + QVariantMap arguments) { assert(false); return nullptr; } }; #endif // hifi_EntityActionFactoryInterface_h From 8bd80c511ee8df21053f7ab47c93105484317dca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 10 Jun 2015 21:08:00 -0700 Subject: [PATCH 20/28] make stick follow mouse motion --- examples/stick.js | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/examples/stick.js b/examples/stick.js index 02101f59e1..5631f3aa3a 100644 --- a/examples/stick.js +++ b/examples/stick.js @@ -1,7 +1,20 @@ +// stick.js +// examples +// +// Created by Seth Alves on 2015-6-10 +// Copyright 2015 High Fidelity, Inc. +// +// Allow avatar to hold a stick +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + var stickID = null; +var actionID = "00000000-0000-0000-0000-000000000000"; // sometimes if this is run immediately the stick doesn't get created? use a timer. Script.setTimeout(function() { - var stickID = Entities.addEntity({ + stickID = Entities.addEntity({ type: "Model", modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", @@ -11,8 +24,33 @@ Script.setTimeout(function() { damping: .1, collisionsWillMove: true }); - Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15}); + actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15}); }, 3000); -function cleanup() { Entities.deleteEntity(stickID); } -Script.scriptEnding.connect(cleanup); + +function cleanUp() { + Entities.deleteEntity(stickID); +} + +function mouseMoveEvent(event) { + if (!stickID || actionID == "00000000-0000-0000-0000-000000000000") { + return; + } + var windowCenterX = Window.innerWidth/ 2; + var windowCenterY = Window.innerHeight / 2; + var mouseXCenterOffset = event.x - windowCenterX; + var mouseYCenterOffset = event.y - windowCenterY; + var mouseXRatio = mouseXCenterOffset / windowCenterX; + var mouseYRatio = mouseYCenterOffset / windowCenterY; + + var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0); + var baseOffset = {x: 0.0, y: 0.0, z: -0.9}; + var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset); + + Entities.updateAction(stickID, actionID, {relativePosition: offset, + relativeRotation: stickOrientation, + timeScale: 0.15}); +} + +Script.scriptEnding.connect(cleanUp); +Controller.mouseMoveEvent.connect(mouseMoveEvent); From 7685fe2229ff876fa0a3cfe296f45a38f1d1d704 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 11 Jun 2015 06:40:21 -0700 Subject: [PATCH 21/28] Clean up on the item interface and introduction of the Layered concept, fixing the highliting box of the edit tool --- examples/libraries/entitySelectionTool.js | 3 +- interface/src/ui/overlays/Overlay.h | 1 + interface/src/ui/overlays/OverlaysPayload.cpp | 13 +- .../render-utils/src/RenderDeferredTask.cpp | 5 +- libraries/render/src/render/DrawTask.cpp | 43 ++++++- libraries/render/src/render/DrawTask.h | 8 ++ libraries/render/src/render/Scene.cpp | 35 +----- libraries/render/src/render/Scene.h | 113 +++++++----------- 8 files changed, 115 insertions(+), 106 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d12c9dae3c..d5e2b24f36 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -304,7 +304,8 @@ SelectionDisplay = (function () { visible: false, dashed: true, lineWidth: 2.0, - ignoreRayIntersection: true // this never ray intersects + ignoreRayIntersection: true, // this never ray intersects + drawInFront: true }); var selectionBox = Overlays.addOverlay("cube", { diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 1a808bc15c..0264c6e3c0 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -123,6 +123,7 @@ protected: namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay); template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay); + template <> int payloadGetLayer(const Overlay::Pointer& overlay); template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args); } diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index cf3262d05c..d5e4b34f6b 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -37,7 +37,7 @@ namespace render { template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay) { if (overlay->is3D() && !static_cast(overlay.get())->getDrawOnHUD()) { if (static_cast(overlay.get())->getDrawInFront()) { - return ItemKey::Builder().withTypeShape().withNoDepthSort().build(); + return ItemKey::Builder().withTypeShape().withLayered().build(); } else { return ItemKey::Builder::opaqueShape(); } @@ -53,6 +53,17 @@ namespace render { return AABox(glm::vec3(bounds.x(), bounds.y(), 0.0f), glm::vec3(bounds.width(), bounds.height(), 0.1f)); } } + template <> int payloadGetLayer(const Overlay::Pointer& overlay) { + // MAgic number while we are defining the layering mechanism: + const int LAYER_2D = 2; + const int LAYER_3D_FRONT = 1; + const int LAYER_3D = 0; + if (overlay->is3D()) { + return (static_cast(overlay.get())->getDrawInFront() ? LAYER_3D_FRONT : LAYER_3D); + } else { + return LAYER_2D; + } + } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { glPushMatrix(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 777d9466a5..3fd7d05666 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -50,6 +50,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); + _jobs.push_back(Job(DrawPostLayered())); _jobs.push_back(Job(ResetGLState())); } @@ -85,7 +86,7 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex // render opaques auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; @@ -163,7 +164,7 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC // render transparents auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape()); + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::transparentShape().withoutLayered()); auto& renderDetails = renderContext->args->_details; ItemIDsBounds inItems; diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index bfc888ea8a..53964ac1db 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -192,7 +192,6 @@ void render::renderItems(const SceneContextPointer& sceneContext, const RenderCo } } - void addClearStateCommands(gpu::Batch& batch) { batch._glDepthMask(true); batch._glDepthFunc(GL_LESS); @@ -446,6 +445,48 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi args->_context->syncCache(); } +template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawPostLayered"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() > 0)) { + inItems.emplace_back(id); + } + } + if (inItems.empty()) { + return; + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + + renderItems(sceneContext, renderContext, inItems); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + + // Force the context sync + args->_context->syncCache(); +} void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 1f260583f2..0052c314c0 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -87,6 +87,14 @@ public: }; template <> void jobRun(const DrawBackground& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + +class DrawPostLayered { +public: +}; +template <> void jobRun(const DrawPostLayered& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + + class ResetGLState { public: }; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 8615f7cf7a..1d2e54541b 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -45,10 +45,12 @@ void ItemBucketMap::reset(const ItemID& id, const ItemKey& oldKey, const ItemKey } void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { - (*this)[ItemFilter::Builder::opaqueShape()]; - (*this)[ItemFilter::Builder::transparentShape()]; + (*this)[ItemFilter::Builder::opaqueShape().withoutLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withoutLayered()]; (*this)[ItemFilter::Builder::light()]; (*this)[ItemFilter::Builder::background()]; + (*this)[ItemFilter::Builder::opaqueShape().withLayered()]; + (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } @@ -61,11 +63,6 @@ void Item::resetPayload(const PayloadPointer& payload) { } } -void Item::kill() { - _payload.reset(); - _key._flags.reset(); -} - void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); @@ -164,27 +161,3 @@ void Scene::updateItems(const ItemIDs& ids, UpdateFunctors& functors) { _items[(*updateID)].update((*updateFunctor)); } } - -void Scene::registerObserver(ObserverPointer& observer) { - // make sure it's a valid observer - if (observer && (observer->getScene() == nullptr)) { - // Then register the observer - _observers.push_back(observer); - - // And let it do what it wants to do - observer->registerScene(this); - } -} - -void Scene::unregisterObserver(ObserverPointer& observer) { - // make sure it's a valid observer currently registered - if (observer && (observer->getScene() == this)) { - // let it do what it wants to do - observer->unregisterScene(); - - // Then unregister the observer - auto it = std::find(_observers.begin(), _observers.end(), observer); - _observers.erase(it); - } -} - diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 8cb29609ba..5568312dfe 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -36,7 +36,6 @@ public: enum FlagBit { TYPE_SHAPE = 0, // Item is a Shape TYPE_LIGHT, // Item is a Light - TYPE_BACKGROUND, // Item is a Background TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work... VIEW_SPACE, // Transformed in view space, and not in world space DYNAMIC, // Dynamic and bound will change unlike static item @@ -44,7 +43,7 @@ public: INVISIBLE, // Visible or not? could be just here to cast shadow SHADOW_CASTER, // Item cast shadows PICKABLE, // Item can be picked/selected - NO_DEPTH_SORT, // Item should not be depth sorted + LAYERED, // Item belongs to one of the layers different from the default layer NUM_FLAGS, // Not a valid flag }; @@ -57,6 +56,7 @@ public: ItemKey(const Flags& flags) : _flags(flags) {} class Builder { + friend class ItemKey; Flags _flags{ 0 }; public: Builder() {} @@ -65,7 +65,6 @@ public: Builder& withTypeShape() { _flags.set(TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _flags.set(TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _flags.set(TYPE_BACKGROUND); return (*this); } Builder& withTransparent() { _flags.set(TRANSLUCENT); return (*this); } Builder& withViewSpace() { _flags.set(VIEW_SPACE); return (*this); } Builder& withDynamic() { _flags.set(DYNAMIC); return (*this); } @@ -73,14 +72,15 @@ public: Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); } Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); } Builder& withPickable() { _flags.set(PICKABLE); return (*this); } - Builder& withNoDepthSort() { _flags.set(NO_DEPTH_SORT); return (*this); } + Builder& withLayered() { _flags.set(LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemKey opaqueShape() { return Builder().withTypeShape().build(); } - static ItemKey transparentShape() { return Builder().withTypeShape().withTransparent().build(); } - static ItemKey light() { return Builder().withTypeLight().build(); } - static ItemKey background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemKey(const Builder& builder) : ItemKey(builder._flags) {} bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; } @@ -101,8 +101,7 @@ public: bool isPickable() const { return _flags[PICKABLE]; } - bool isDepthSort() const { return !_flags[NO_DEPTH_SORT]; } - bool isNoDepthSort() const { return _flags[NO_DEPTH_SORT]; } + bool isLayered() const { return _flags[LAYERED]; } }; inline QDebug operator<<(QDebug debug, const ItemKey& itemKey) { @@ -122,6 +121,7 @@ public: ItemFilter(const ItemKey::Flags& value = ItemKey::Flags(0), const ItemKey::Flags& mask = ItemKey::Flags(0)) : _value(value), _mask(mask) {} class Builder { + friend class ItemFilter; ItemKey::Flags _value{ 0 }; ItemKey::Flags _mask{ 0 }; public: @@ -131,7 +131,6 @@ public: Builder& withTypeShape() { _value.set(ItemKey::TYPE_SHAPE); _mask.set(ItemKey::TYPE_SHAPE); return (*this); } Builder& withTypeLight() { _value.set(ItemKey::TYPE_LIGHT); _mask.set(ItemKey::TYPE_LIGHT); return (*this); } - Builder& withTypeBackground() { _value.set(ItemKey::TYPE_BACKGROUND); _mask.set(ItemKey::TYPE_BACKGROUND); return (*this); } Builder& withOpaque() { _value.reset(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } Builder& withTransparent() { _value.set(ItemKey::TRANSLUCENT); _mask.set(ItemKey::TRANSLUCENT); return (*this); } @@ -153,16 +152,18 @@ public: Builder& withPickable() { _value.set(ItemKey::PICKABLE); _mask.set(ItemKey::PICKABLE); return (*this); } - Builder& withDepthSort() { _value.reset(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } - Builder& withNotDepthSort() { _value.set(ItemKey::NO_DEPTH_SORT); _mask.set(ItemKey::NO_DEPTH_SORT); return (*this); } + Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } // Convenient standard keys that we will keep on using all over the place - static ItemFilter opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace().build(); } - static ItemFilter transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace().build(); } - static ItemFilter light() { return Builder().withTypeLight().build(); } - static ItemFilter background() { return Builder().withTypeBackground().build(); } + static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } + static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); } + static Builder light() { return Builder().withTypeLight(); } + static Builder background() { return Builder().withViewSpace().withLayered(); } }; + ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} + // Item Filter operator testing if a key pass the filter bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } @@ -179,7 +180,7 @@ public: }; inline QDebug operator<<(QDebug debug, const ItemFilter& me) { - debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape()) + debug << "[ItemFilter: opaqueShape:" << me.test(ItemKey::Builder::opaqueShape().build()) << "]"; return debug; } @@ -214,35 +215,40 @@ public: public: virtual const ItemKey getKey() const = 0; virtual const Bound getBound() const = 0; - virtual void render(RenderArgs* args) = 0; + virtual int getLayer() const = 0; - virtual void update(const UpdateFunctorPointer& functor) = 0; + virtual void render(RenderArgs* args) = 0; virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} protected: + friend class Item; + virtual void update(const UpdateFunctorPointer& functor) = 0; }; - - - typedef std::shared_ptr PayloadPointer; - - Item() {} ~Item() {} + // Main scene / item managment interface Reset/Update/Kill void resetPayload(const PayloadPointer& payload); - void kill(); + void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Communicate update to the payload + void kill() { _payload.reset(); _key._flags.reset(); } // Kill means forget the payload and key // Check heuristic key const ItemKey& getKey() const { return _key; } // Payload Interface + + // Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace()) const Bound getBound() const { return _payload->getBound(); } + + // Get the layer where the item belongs. 0 by default meaning NOT LAYERED + int getLayer() const { return _payload->getLayer(); } + + // Render call for the item void render(RenderArgs* args) { _payload->render(args); } - void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Shape Type Interface const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); } @@ -280,6 +286,7 @@ inline QDebug operator<<(QDebug debug, const Item& item) { // of the Payload interface template const ItemKey payloadGetKey(const std::shared_ptr& payloadData) { return ItemKey(); } template const Item::Bound payloadGetBound(const std::shared_ptr& payloadData) { return Item::Bound(); } +template int payloadGetLayer(const std::shared_ptr& payloadData) { return 0; } template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } // Shape type interface @@ -290,19 +297,25 @@ public: typedef std::shared_ptr DataPointer; typedef UpdateFunctor Updater; - virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + Payload(const DataPointer& data) : _data(data) {} // Payload general interface virtual const ItemKey getKey() const { return payloadGetKey(_data); } virtual const Item::Bound getBound() const { return payloadGetBound(_data); } + virtual int getLayer() const { return payloadGetLayer(_data); } + + virtual void render(RenderArgs* args) { payloadRender(_data, args); } // Shape Type interface virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey(_data); } - Payload(const DataPointer& data) : _data(data) {} protected: DataPointer _data; + + // Update mechanics + virtual void update(const UpdateFunctorPointer& functor) { static_cast(functor.get())->_func((*_data)); } + friend class Item; }; // Let's show how to make a simple FooPayload example: @@ -358,6 +371,7 @@ public: typedef std::vector< ItemIDAndBounds > ItemIDsBounds; + // A map of ItemIDSets allowing to create bucket lists of items which are filtering correctly class ItemBucketMap : public std::map { public: @@ -374,8 +388,6 @@ public: }; class Engine; -class Observer; - class PendingChanges { public: @@ -411,36 +423,6 @@ typedef std::queue PendingChangesQueue; // Items are notified accordingly on any update message happening class Scene { public: - - class Observer { - public: - Observer(Scene* scene) { - - } - ~Observer() {} - - const Scene* getScene() const { return _scene; } - Scene* editScene() { return _scene; } - - protected: - Scene* _scene = nullptr; - virtual void onRegisterScene() {} - virtual void onUnregisterScene() {} - - friend class Scene; - void registerScene(Scene* scene) { - _scene = scene; - onRegisterScene(); - } - - void unregisterScene() { - onUnregisterScene(); - _scene = 0; - } - }; - typedef std::shared_ptr< Observer > ObserverPointer; - typedef std::vector< ObserverPointer > Observers; - Scene(); ~Scene() {} @@ -450,11 +432,6 @@ public: /// Enqueue change batch to the scene void enqueuePendingChanges(const PendingChanges& pendingChanges); - /// Scene Observer listen to any change and get notified - void registerObserver(ObserverPointer& observer); - void unregisterObserver(ObserverPointer& observer); - - /// Access the main bucketmap of items const ItemBucketMap& getMasterBucket() const { return _masterBucketMap; } @@ -483,10 +460,6 @@ protected: void removeItems(const ItemIDs& ids); void updateItems(const ItemIDs& ids, UpdateFunctors& functors); - - // The scene context listening for any change to the database - Observers _observers; - friend class Engine; }; From 7d0000c5377febd3fc3307b840f5fe020f3d7bfa Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 11 Jun 2015 08:38:03 -0700 Subject: [PATCH 22/28] fix warning about returning ref to temp variable --- libraries/render/src/render/Scene.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 8cb29609ba..b9481d367e 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -245,7 +245,7 @@ public: void update(const UpdateFunctorPointer& updateFunctor) { _payload->update(updateFunctor); } // Shape Type Interface - const model::MaterialKey& getMaterialKey() const { return _payload->getMaterialKey(); } + const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } protected: PayloadPointer _payload; From f77f3f1e1e46eea210f9bbefad2befe182177359 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 11 Jun 2015 08:43:19 -0700 Subject: [PATCH 23/28] fix bug where grab.js moves mouse to screen corner when CTRL is pressed --- examples/grab.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/grab.js b/examples/grab.js index 306af86c68..c6b1b884b7 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -140,6 +140,10 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) { } function computeNewGrabPlane() { + if (!gIsGrabbing) { + return; + } + var maybeResetMousePosition = false; if (gGrabMode !== "rotate") { gMouseAtRotateStart = gMouseCursorLocation; From f4f5f167588206987836c3e1b1a2757351a2a9cb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 11 Jun 2015 18:34:39 +0200 Subject: [PATCH 24/28] Fix zones wireframe rendering --- .../src/RenderableZoneEntityItem.cpp | 73 ++++++++++++++++++- .../src/RenderableZoneEntityItem.h | 5 ++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 9c4f8ae0bb..8c147cac05 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -100,10 +101,17 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { case SHAPE_TYPE_COMPOUND: { PerformanceTimer perfTimer("zone->renderCompound"); updateGeometry(); - - if (_model && _model->isActive()) { - // FIX ME: this is no longer available... we need to switch to payloads - //_model->renderInScene(getLocalRenderAlpha(), args); + if (_model && _model->needsFixupInScene()) { + // check to see if when we added our models to the scene they were ready, if they were not ready, then + // fix them up in the scene + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); + _model->addToScene(scene, pendingChanges); + + scene->enqueuePendingChanges(pendingChanges); + + _model->setVisibleInScene(getVisible(), scene); } break; } @@ -131,6 +139,15 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { break; } } + + if ((!_drawZoneBoundaries || getShapeType() != SHAPE_TYPE_COMPOUND) && + _model && !_model->needsFixupInScene()) { + // If the model is in the scene but doesn't need to be, remove it. + render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + render::PendingChanges pendingChanges; + _model->removeFromScene(scene, pendingChanges); + scene->enqueuePendingChanges(pendingChanges); + } } bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { @@ -145,3 +162,51 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { return false; } + +class RenderableZoneEntityItemMeta { +public: + RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ } + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + EntityItemPointer entity; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) { + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) { + if (payload && payload->entity) { + return payload->entity->getAABox(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) { + if (args) { + if (payload && payload->entity) { + payload->entity->render(args); + } + } + } +} + +bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + _myMetaItem = scene->allocateID(); + + auto renderData = RenderableZoneEntityItemMeta::Pointer(new RenderableZoneEntityItemMeta(self)); + auto renderPayload = render::PayloadPointer(new RenderableZoneEntityItemMeta::Payload(renderData)); + + pendingChanges.resetItem(_myMetaItem, renderPayload); + return true; +} + +void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, + render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myMetaItem); + if (_model) { + _model->removeFromScene(scene, pendingChanges); + } +} diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index b2a9791d44..f455ea34de 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -35,6 +35,9 @@ public: virtual void render(RenderArgs* args); virtual bool contains(const glm::vec3& point) const; + virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); + virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); + private: Model* getModel(); void initialSimulation(); @@ -45,6 +48,8 @@ private: Model* _model; bool _needsInitialSimulation; + + render::ItemID _myMetaItem; }; #endif // hifi_RenderableZoneEntityItem_h From 47888b46716d6808645eeb1ae3a3a96735ff82f7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 11 Jun 2015 09:34:56 -0700 Subject: [PATCH 25/28] fix transforms for items with non-default registration point --- libraries/entities-renderer/src/RenderableBoxEntityItem.cpp | 2 +- .../entities-renderer/src/RenderableDebugableEntityItem.cpp | 4 ++-- .../entities-renderer/src/RenderableSphereEntityItem.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 066aaa8b31..23b93250bc 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -33,7 +33,7 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransform()); // we want to include the scale as well + batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well DependencyManager::get()->renderSolidCube(batch, 1.0f, cubeColor); RenderableDebugableEntityItem::render(this, args); diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp index 32e30c7e96..6a511e0d30 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp @@ -24,7 +24,7 @@ void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, Render float puffedOut, glm::vec4& color) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(entity->getTransform()); // we want to include the scale as well + batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well DependencyManager::get()->renderWireCube(batch, 1.0f + puffedOut, color); } @@ -33,7 +33,7 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(entity->getTransform()); // we want to include the scale as well + batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 5ba9003d43..6d9cb525d6 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -39,7 +39,7 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransform()); // use a transform with scale, rotation, registration point and translation + batch.setModelTransform(getTransformToCenter()); // use a transform with scale, rotation, registration point and translation DependencyManager::get()->renderSolidSphere(batch, 0.5f, SLICES, STACKS, sphereColor); RenderableDebugableEntityItem::render(this, args); From a878559e0c4a88126c924abc805f236f0f7bb765 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 11 Jun 2015 09:38:45 -0700 Subject: [PATCH 26/28] fix transforms for items with non-default registration point --- libraries/entities-renderer/src/RenderableLightEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 8a84c167c5..819989d5ec 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -49,7 +49,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) { #ifdef WANT_DEBUG Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getTransform()); + batch.setModelTransform(getTransformToCenter()); DependencyManager::get()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f)); #endif }; From 0e8ec81837f56f4a211fc843c3a51c4aa168308d Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 11 Jun 2015 09:41:59 -0700 Subject: [PATCH 27/28] fixed lambda capture --- interface/src/devices/KeyboardMouseDevice.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/devices/KeyboardMouseDevice.cpp b/interface/src/devices/KeyboardMouseDevice.cpp index d0f04e5636..8a336064e5 100755 --- a/interface/src/devices/KeyboardMouseDevice.cpp +++ b/interface/src/devices/KeyboardMouseDevice.cpp @@ -173,9 +173,9 @@ void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); return availableInputs; }; - proxy->resetDeviceBindings = [this, &_mapper = mapper, &device = _deviceID] () -> bool { - _mapper.removeAllInputChannelsForDevice(device); - this->assignDefaultInputMapping(_mapper); + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); return true; }; mapper.registerDevice(_deviceID, proxy); From 8ed9a3ca02e94bb064a42cb216b9b5c863384cc2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 11 Jun 2015 11:04:19 -0700 Subject: [PATCH 28/28] add handedness setting to avatar-hold action. update stick.js to allow use of hydra --- examples/stick.js | 58 +++++++++++++++---- interface/src/avatar/AvatarActionHold.cpp | 31 ++++++++-- interface/src/avatar/AvatarActionHold.h | 2 + .../entities/src/EntityActionInterface.cpp | 18 +++++- .../entities/src/EntityActionInterface.h | 3 + 5 files changed, 94 insertions(+), 18 deletions(-) diff --git a/examples/stick.js b/examples/stick.js index 5631f3aa3a..ea1f3439a9 100644 --- a/examples/stick.js +++ b/examples/stick.js @@ -10,8 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var hand = "left"; +var nullActionID = "00000000-0000-0000-0000-000000000000"; +var controllerID; +var controllerActive; var stickID = null; -var actionID = "00000000-0000-0000-0000-000000000000"; +var actionID = nullActionID; // sometimes if this is run immediately the stick doesn't get created? use a timer. Script.setTimeout(function() { stickID = Entities.addEntity({ @@ -19,12 +23,14 @@ Script.setTimeout(function() { modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", dimensions: {x: .11, y: .11, z: .59}, - position: MyAvatar.getRightPalmPosition(), + position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close rotation: MyAvatar.orientation, damping: .1, collisionsWillMove: true }); - actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, timeScale: 0.15}); + actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -0.9}, + hand: hand, + timeScale: 0.15}); }, 3000); @@ -32,11 +38,20 @@ function cleanUp() { Entities.deleteEntity(stickID); } + +function positionStick(stickOrientation) { + var baseOffset = {x: 0.0, y: 0.0, z: -0.9}; + var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset); + Entities.updateAction(stickID, actionID, {relativePosition: offset, + relativeRotation: stickOrientation}); +} + + function mouseMoveEvent(event) { - if (!stickID || actionID == "00000000-0000-0000-0000-000000000000") { + if (!stickID || actionID == nullActionID) { return; } - var windowCenterX = Window.innerWidth/ 2; + var windowCenterX = Window.innerWidth / 2; var windowCenterY = Window.innerHeight / 2; var mouseXCenterOffset = event.x - windowCenterX; var mouseYCenterOffset = event.y - windowCenterY; @@ -44,13 +59,34 @@ function mouseMoveEvent(event) { var mouseYRatio = mouseYCenterOffset / windowCenterY; var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0); - var baseOffset = {x: 0.0, y: 0.0, z: -0.9}; - var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset); - - Entities.updateAction(stickID, actionID, {relativePosition: offset, - relativeRotation: stickOrientation, - timeScale: 0.15}); + positionStick(stickOrientation); } + +function initControls(){ + if (hand == "right") { + controllerID = 3; // right handed + } else { + controllerID = 4; // left handed + } +} + + +function update(deltaTime){ + var palmPosition = Controller.getSpatialControlPosition(controllerID); + controllerActive = (Vec3.length(palmPosition) > 0); + if(!controllerActive){ + return; + } + + stickOrientation = Controller.getSpatialControlRawRotation(controllerID); + var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); + stickOrientation = Quat.multiply(stickOrientation, adjustment); + + positionStick(stickOrientation); +} + + Script.scriptEnding.connect(cleanUp); Controller.mouseMoveEvent.connect(mouseMoveEvent); +Script.update.connect(update); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 74a583df58..1fbb01beb3 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -29,7 +29,12 @@ AvatarActionHold::~AvatarActionHold() { void AvatarActionHold::updateActionWorker(float deltaTimeStep) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition = myAvatar->getRightPalmPosition(); + glm::vec3 palmPosition; + if (_hand == "right") { + palmPosition = myAvatar->getRightPalmPosition(); + } else { + palmPosition = myAvatar->getLeftPalmPosition(); + } auto rotation = myAvatar->getWorldAlignedOrientation(); auto offset = rotation * _relativePosition; @@ -55,28 +60,46 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { bool tSOk = true; float timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", tSOk, false); + bool hOk = true; + QString hand = + EntityActionInterface::extractStringArgument("hold", arguments, "hand", hOk, false); lockForWrite(); if (rPOk) { _relativePosition = relativePosition; - } else { + } else if (!_parametersSet) { _relativePosition = glm::vec3(0.0f, 0.0f, 1.0f); } if (rROk) { _relativeRotation = relativeRotation; - } else { + } else if (!_parametersSet) { _relativeRotation = glm::quat(0.0f, 0.0f, 0.0f, 1.0f); } if (tSOk) { _linearTimeScale = timeScale; _angularTimeScale = timeScale; - } else { + } else if (!_parametersSet) { _linearTimeScale = 0.2; _angularTimeScale = 0.2; } + if (hOk) { + hand = hand.toLower(); + if (hand == "left") { + _hand = "left"; + } else if (hand == "right") { + _hand = "right"; + } else { + qDebug() << "hold action -- invalid hand argument:" << hand; + _hand = "right"; + } + } else if (!_parametersSet) { + _hand = "right"; + } + + _parametersSet = true; _positionalTargetSet = true; _rotationalTargetSet = true; _active = true; diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index f92ea94aaa..705c751029 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -28,6 +28,8 @@ public: private: glm::vec3 _relativePosition; glm::quat _relativeRotation; + QString _hand; + bool _parametersSet = false; }; #endif // hifi_AvatarActionHold_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index b293d609c2..e97669686c 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -91,7 +91,6 @@ glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVarian return glm::vec3(x, y, z); } - glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { @@ -139,8 +138,6 @@ glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVarian return glm::quat(w, x, y, z); } - - float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required) { if (!arguments.contains(argumentName)) { @@ -162,3 +159,18 @@ float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMa return v; } + +QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return ""; + } + + QVariant vV = arguments[argumentName]; + QString v = vV.toString(); + return v; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 457db8f5c3..486d3f5948 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -59,6 +59,9 @@ protected: QString argumentName, bool& ok, bool required = true); static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); + static QString extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); + };