diff --git a/assignment-client/src/AssignmentAction.cpp b/assignment-client/src/AssignmentAction.cpp deleted file mode 100644 index 8d296cd6ab..0000000000 --- a/assignment-client/src/AssignmentAction.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// AssignmentAction.cpp -// assignment-client/src/ -// -// Created by Seth Alves 2015-6-19 -// 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 "EntitySimulation.h" - -#include "AssignmentAction.h" - -AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) : - EntityActionInterface(type, id), - _data(QByteArray()), - _active(false), - _ownerEntity(ownerEntity) { -} - -AssignmentAction::~AssignmentAction() { -} - -void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const { - withReadLock([&]{ - simulation->removeAction(_id); - simulation->applyActionChanges(); - }); -} - -QByteArray AssignmentAction::serialize() const { - QByteArray result; - withReadLock([&]{ - result = _data; - }); - return result; -} - -void AssignmentAction::deserialize(QByteArray serializedArguments) { - withWriteLock([&]{ - _data = serializedArguments; - }); -} - -bool AssignmentAction::updateArguments(QVariantMap arguments) { - qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client."; - return false; -} - -QVariantMap AssignmentAction::getArguments() { - qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client."; - return QVariantMap(); -} - -glm::vec3 AssignmentAction::getPosition() { - qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client."; - return glm::vec3(0.0f); -} - -void AssignmentAction::setPosition(glm::vec3 position) { - qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client."; -} - -glm::quat AssignmentAction::getRotation() { - qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client."; - return glm::quat(); -} - -void AssignmentAction::setRotation(glm::quat rotation) { - qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client."; -} - -glm::vec3 AssignmentAction::getLinearVelocity() { - qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client."; - return glm::vec3(0.0f); -} - -void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) { - qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client."; -} - -glm::vec3 AssignmentAction::getAngularVelocity() { - qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client."; - return glm::vec3(0.0f); -} - -void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) { - qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client."; -} diff --git a/assignment-client/src/AssignmentActionFactory.cpp b/assignment-client/src/AssignmentActionFactory.cpp deleted file mode 100644 index f99e712b72..0000000000 --- a/assignment-client/src/AssignmentActionFactory.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// AssignmentActionFactory.cpp -// assignment-client/src/ -// -// Created by Seth Alves on 2015-6-19 -// 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 "AssignmentActionFactory.h" - - -EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) { - return EntityActionPointer(new AssignmentAction(type, id, ownerEntity)); -} - -EntityActionPointer AssignmentActionFactory::factory(EntityActionType type, - const QUuid& id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { - EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity); - if (action) { - bool ok = action->updateArguments(arguments); - if (ok) { - return action; - } - } - return nullptr; -} - - -EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) { - QDataStream serializedActionDataStream(data); - EntityActionType type; - QUuid id; - - serializedActionDataStream >> type; - serializedActionDataStream >> id; - - EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity); - - if (action) { - action->deserialize(data); - } - return action; -} diff --git a/assignment-client/src/AssignmentActionFactory.h b/assignment-client/src/AssignmentActionFactory.h deleted file mode 100644 index 87970c9431..0000000000 --- a/assignment-client/src/AssignmentActionFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// AssignmentActionFactory.cpp -// assignment-client/src/ -// -// Created by Seth Alves on 2015-6-19 -// 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_AssignmentActionFactory_h -#define hifi_AssignmentActionFactory_h - -#include "EntityActionFactoryInterface.h" -#include "AssignmentAction.h" - -class AssignmentActionFactory : public EntityActionFactoryInterface { -public: - AssignmentActionFactory() : EntityActionFactoryInterface() { } - virtual ~AssignmentActionFactory() { } - virtual EntityActionPointer factory(EntityActionType type, - const QUuid& id, - EntityItemPointer ownerEntity, - QVariantMap arguments) override; - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override; -}; - -#endif // hifi_AssignmentActionFactory_h diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index fe565b62f4..eb0ffefe47 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -32,7 +32,7 @@ #include #include "AssignmentFactory.h" -#include "AssignmentActionFactory.h" +#include "AssignmentDynamicFactory.h" #include "AssignmentClient.h" #include "AssignmentClientLogging.h" @@ -63,8 +63,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto animationCache = DependencyManager::set(); auto entityScriptingInterface = DependencyManager::set(false); - DependencyManager::registerInheritance(); - auto actionFactory = DependencyManager::set(); + DependencyManager::registerInheritance(); + auto dynamicFactory = DependencyManager::set(); DependencyManager::set(); // setup a thread for the NodeList and its PacketReceiver diff --git a/assignment-client/src/AssignmentDynamic.cpp b/assignment-client/src/AssignmentDynamic.cpp new file mode 100644 index 0000000000..026bc120bb --- /dev/null +++ b/assignment-client/src/AssignmentDynamic.cpp @@ -0,0 +1,83 @@ +// +// AssignmentDynamic.cpp +// assignment-client/src/ +// +// Created by Seth Alves 2015-6-19 +// 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 "EntitySimulation.h" + +#include "AssignmentDynamic.h" + +AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) : + EntityDynamicInterface(type, id), + _data(QByteArray()), + _active(false), + _ownerEntity(ownerEntity) { +} + +AssignmentDynamic::~AssignmentDynamic() { +} + +void AssignmentDynamic::removeFromSimulation(EntitySimulationPointer simulation) const { + withReadLock([&]{ + simulation->removeDynamic(_id); + simulation->applyDynamicChanges(); + }); +} + +QByteArray AssignmentDynamic::serialize() const { + QByteArray result; + withReadLock([&]{ + result = _data; + }); + return result; +} + +void AssignmentDynamic::deserialize(QByteArray serializedArguments) { + withWriteLock([&]{ + _data = serializedArguments; + }); +} + +bool AssignmentDynamic::updateArguments(QVariantMap arguments) { + qDebug() << "UNEXPECTED -- AssignmentDynamic::updateArguments called in assignment-client."; + return false; +} + +QVariantMap AssignmentDynamic::getArguments() { + qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client."; + return QVariantMap(); +} + +glm::vec3 AssignmentDynamic::getPosition() { + qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client."; + return glm::vec3(0.0f); +} + +glm::quat AssignmentDynamic::getRotation() { + qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client."; + return glm::quat(); +} + +glm::vec3 AssignmentDynamic::getLinearVelocity() { + qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client."; + return glm::vec3(0.0f); +} + +void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) { + qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client."; +} + +glm::vec3 AssignmentDynamic::getAngularVelocity() { + qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client."; + return glm::vec3(0.0f); +} + +void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) { + qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client."; +} diff --git a/assignment-client/src/AssignmentAction.h b/assignment-client/src/AssignmentDynamic.h similarity index 69% rename from assignment-client/src/AssignmentAction.h rename to assignment-client/src/AssignmentDynamic.h index 98504b3545..35db8b1524 100644 --- a/assignment-client/src/AssignmentAction.h +++ b/assignment-client/src/AssignmentDynamic.h @@ -1,5 +1,5 @@ // -// AssignmentAction.h +// AssignmentDynamic.h // assignment-client/src/ // // Created by Seth Alves 2015-6-19 @@ -8,21 +8,21 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html +// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html -#ifndef hifi_AssignmentAction_h -#define hifi_AssignmentAction_h +#ifndef hifi_AssignmentDynamic_h +#define hifi_AssignmentDynamic_h #include #include -#include "EntityActionInterface.h" +#include "EntityDynamicInterface.h" -class AssignmentAction : public EntityActionInterface, public ReadWriteLockable { +class AssignmentDynamic : public EntityDynamicInterface, public ReadWriteLockable { public: - AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); - virtual ~AssignmentAction(); + AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity); + virtual ~AssignmentDynamic(); virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } @@ -38,9 +38,7 @@ private: protected: virtual glm::vec3 getPosition() override; - virtual void setPosition(glm::vec3 position) override; virtual glm::quat getRotation() override; - virtual void setRotation(glm::quat rotation) override; virtual glm::vec3 getLinearVelocity() override; virtual void setLinearVelocity(glm::vec3 linearVelocity) override; virtual glm::vec3 getAngularVelocity() override; @@ -50,4 +48,4 @@ protected: EntityItemWeakPointer _ownerEntity; }; -#endif // hifi_AssignmentAction_h +#endif // hifi_AssignmentDynamic_h diff --git a/assignment-client/src/AssignmentDynamicFactory.cpp b/assignment-client/src/AssignmentDynamicFactory.cpp new file mode 100644 index 0000000000..88c7f6e06c --- /dev/null +++ b/assignment-client/src/AssignmentDynamicFactory.cpp @@ -0,0 +1,48 @@ +// +// AssignmentDynamcFactory.cpp +// assignment-client/src/ +// +// Created by Seth Alves on 2015-6-19 +// 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 "AssignmentDynamicFactory.h" + + +EntityDynamicPointer assignmentDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) { + return EntityDynamicPointer(new AssignmentDynamic(type, id, ownerEntity)); +} + +EntityDynamicPointer AssignmentDynamicFactory::factory(EntityDynamicType type, + const QUuid& id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity); + if (dynamic) { + bool ok = dynamic->updateArguments(arguments); + if (ok) { + return dynamic; + } + } + return nullptr; +} + + +EntityDynamicPointer AssignmentDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) { + QDataStream serializedDynamicDataStream(data); + EntityDynamicType type; + QUuid id; + + serializedDynamicDataStream >> type; + serializedDynamicDataStream >> id; + + EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity); + + if (dynamic) { + dynamic->deserialize(data); + } + return dynamic; +} diff --git a/assignment-client/src/AssignmentDynamicFactory.h b/assignment-client/src/AssignmentDynamicFactory.h new file mode 100644 index 0000000000..cdb9b6ae71 --- /dev/null +++ b/assignment-client/src/AssignmentDynamicFactory.h @@ -0,0 +1,29 @@ +// +// AssignmentDynamicFactory.cpp +// assignment-client/src/ +// +// Created by Seth Alves on 2015-6-19 +// 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_AssignmentDynamicFactory_h +#define hifi_AssignmentDynamicFactory_h + +#include "EntityDynamicFactoryInterface.h" +#include "AssignmentDynamic.h" + +class AssignmentDynamicFactory : public EntityDynamicFactoryInterface { +public: + AssignmentDynamicFactory() : EntityDynamicFactoryInterface() { } + virtual ~AssignmentDynamicFactory() { } + virtual EntityDynamicPointer factory(EntityDynamicType type, + const QUuid& id, + EntityItemPointer ownerEntity, + QVariantMap arguments) override; + virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override; +}; + +#endif // hifi_AssignmentDynamicFactory_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75535ebaf1..2d3fbf0f93 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -140,7 +140,7 @@ #include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" -#include "InterfaceActionFactory.h" +#include "InterfaceDynamicFactory.h" #include "InterfaceLogging.h" #include "LODManager.h" #include "ModelPackager.h" @@ -462,7 +462,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); - DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); // Set dependencies @@ -516,7 +516,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, @@ -4440,7 +4440,7 @@ void Application::update(float deltaTime) { _entitySimulation->setObjectsToChange(stillNeedChange); }); - _entitySimulation->applyActionChanges(); + _entitySimulation->applyDynamicChanges(); avatarManager->getObjectsToRemoveFromPhysics(motionStates); _physicsEngine->removeObjects(motionStates); @@ -4450,8 +4450,8 @@ void Application::update(float deltaTime) { _physicsEngine->changeObjects(motionStates); myAvatar->prepareForPhysicsSimulation(); - _physicsEngine->forEachAction([&](EntityActionPointer action) { - action->prepareForPhysicsSimulation(); + _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { + dynamic->prepareForPhysicsSimulation(); }); } { diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp deleted file mode 100644 index 2bc4608e86..0000000000 --- a/interface/src/InterfaceActionFactory.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// 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 -#include - -#include "InterfaceActionFactory.h" - - -EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) { - switch (type) { - case ACTION_TYPE_NONE: - return EntityActionPointer(); - case ACTION_TYPE_OFFSET: - return std::make_shared(id, ownerEntity); - case ACTION_TYPE_SPRING: - return std::make_shared(id, ownerEntity); - case ACTION_TYPE_HOLD: - return std::make_shared(id, ownerEntity); - case ACTION_TYPE_TRAVEL_ORIENTED: - return std::make_shared(id, ownerEntity); - } - - Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type"); - return EntityActionPointer(); -} - - -EntityActionPointer InterfaceActionFactory::factory(EntityActionType type, - const QUuid& id, - EntityItemPointer ownerEntity, - QVariantMap arguments) { - EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity); - if (action) { - bool ok = action->updateArguments(arguments); - if (ok) { - if (action->lifetimeIsOver()) { - return nullptr; - } - return action; - } - } - return nullptr; -} - - -EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) { - QDataStream serializedArgumentStream(data); - EntityActionType type; - QUuid id; - - serializedArgumentStream >> type; - serializedArgumentStream >> id; - - EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity); - - if (action) { - action->deserialize(data); - if (action->lifetimeIsOver()) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during action creation.*"); - qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --" - << action->getExpires() << "<" << usecTimestampNow(); - return nullptr; - } - } - - return action; -} diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp new file mode 100644 index 0000000000..5951ccef9e --- /dev/null +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -0,0 +1,88 @@ +// +// InterfaceDynamicFactory.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 +#include +#include +#include + +#include "InterfaceDynamicFactory.h" + + +EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) { + switch (type) { + case DYNAMIC_TYPE_NONE: + return EntityDynamicPointer(); + case DYNAMIC_TYPE_OFFSET: + return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_SPRING: + return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_HOLD: + return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_TRAVEL_ORIENTED: + return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_HINGE: + return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_FAR_GRAB: + return std::make_shared(id, ownerEntity); + } + + Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type"); + return EntityDynamicPointer(); +} + + +EntityDynamicPointer InterfaceDynamicFactory::factory(EntityDynamicType type, + const QUuid& id, + EntityItemPointer ownerEntity, + QVariantMap arguments) { + EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity); + if (dynamic) { + bool ok = dynamic->updateArguments(arguments); + if (ok) { + if (dynamic->lifetimeIsOver()) { + return nullptr; + } + return dynamic; + } + } + return nullptr; +} + + +EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) { + QDataStream serializedArgumentStream(data); + EntityDynamicType type; + QUuid id; + + serializedArgumentStream >> type; + serializedArgumentStream >> id; + + EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity); + + if (dynamic) { + dynamic->deserialize(data); + if (dynamic->lifetimeIsOver()) { + static QString repeatedMessage = + LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*"); + qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --" + << dynamic->getExpires() << "<" << usecTimestampNow(); + return nullptr; + } + } + + return dynamic; +} diff --git a/interface/src/InterfaceActionFactory.h b/interface/src/InterfaceDynamicFactory.h similarity index 51% rename from interface/src/InterfaceActionFactory.h rename to interface/src/InterfaceDynamicFactory.h index 3e8a17d871..b0696442cb 100644 --- a/interface/src/InterfaceActionFactory.h +++ b/interface/src/InterfaceDynamicFactory.h @@ -1,5 +1,5 @@ // -// InterfaceActionFactory.cpp +// InterfaceDynamicFactory.cpp // interface/src/ // // Created by Seth Alves on 2015-6-10 @@ -9,21 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_InterfaceActionFactory_h -#define hifi_InterfaceActionFactory_h +#ifndef hifi_InterfaceDynamicFactory_h +#define hifi_InterfaceDynamicFactory_h -#include "EntityActionFactoryInterface.h" +#include "EntityDynamicFactoryInterface.h" -class InterfaceActionFactory : public EntityActionFactoryInterface { +class InterfaceDynamicFactory : public EntityDynamicFactoryInterface { public: - InterfaceActionFactory() : EntityActionFactoryInterface() { } - virtual ~InterfaceActionFactory() { } - virtual EntityActionPointer factory(EntityActionType type, + InterfaceDynamicFactory() : EntityDynamicFactoryInterface() { } + virtual ~InterfaceDynamicFactory() { } + virtual EntityDynamicPointer factory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity, QVariantMap arguments) override; - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, + virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override; }; -#endif // hifi_InterfaceActionFactory_h +#endif // hifi_InterfaceDynamicFactory_h diff --git a/interface/src/avatar/AvatarActionFarGrab.cpp b/interface/src/avatar/AvatarActionFarGrab.cpp new file mode 100644 index 0000000000..afa21e58d7 --- /dev/null +++ b/interface/src/avatar/AvatarActionFarGrab.cpp @@ -0,0 +1,64 @@ +// +// AvatarActionFarGrab.cpp +// interface/src/avatar/ +// +// Created by Seth Alves 2017-4-14 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AvatarActionFarGrab.h" + +AvatarActionFarGrab::AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectActionSpring(id, ownerEntity) { + _type = DYNAMIC_TYPE_FAR_GRAB; +#if WANT_DEBUG + qDebug() << "AvatarActionFarGrab::AvatarActionFarGrab"; +#endif +} + +AvatarActionFarGrab::~AvatarActionFarGrab() { +#if WANT_DEBUG + qDebug() << "AvatarActionFarGrab::~AvatarActionFarGrab"; +#endif +} + + +QByteArray AvatarActionFarGrab::serialize() const { + QByteArray serializedActionArguments; + QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_FAR_GRAB; + dataStream << getID(); + dataStream << ObjectActionSpring::springVersion; + + serializeParameters(dataStream); + + return serializedActionArguments; +} + +void AvatarActionFarGrab::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + + QUuid id; + dataStream >> id; + + if (type != getType() || id != getID()) { + qDebug() << "AvatarActionFarGrab::deserialize type or ID don't match." << type << id << getID(); + return; + } + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectActionSpring::springVersion) { + assert(false); + return; + } + + deserializeParameters(serializedArguments, dataStream); +} diff --git a/interface/src/avatar/AvatarActionFarGrab.h b/interface/src/avatar/AvatarActionFarGrab.h new file mode 100644 index 0000000000..46c9f65dcf --- /dev/null +++ b/interface/src/avatar/AvatarActionFarGrab.h @@ -0,0 +1,27 @@ +// +// AvatarActionFarGrab.h +// interface/src/avatar/ +// +// Created by Seth Alves 2017-4-14 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarActionFarGrab_h +#define hifi_AvatarActionFarGrab_h + +#include +#include + +class AvatarActionFarGrab : public ObjectActionSpring { +public: + AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~AvatarActionFarGrab(); + + QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; +}; + +#endif // hifi_AvatarActionFarGrab_h diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 7f58c86aec..98ff687eb3 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -23,7 +23,7 @@ const int AvatarActionHold::velocitySmoothFrames = 6; AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) : ObjectActionSpring(id, ownerEntity) { - _type = ACTION_TYPE_HOLD; + _type = DYNAMIC_TYPE_HOLD; _measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames); auto myAvatar = DependencyManager::get()->getMyAvatar(); @@ -323,28 +323,28 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { bool ignoreIK; bool needUpdate = false; - bool somethingChanged = ObjectAction::updateArguments(arguments); + bool somethingChanged = ObjectDynamic::updateArguments(arguments); withReadLock([&]{ bool ok = true; - relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); + relativePosition = EntityDynamicInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); if (!ok) { relativePosition = _relativePosition; } ok = true; - relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); + relativeRotation = EntityDynamicInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); if (!ok) { relativeRotation = _relativeRotation; } ok = true; - timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); + timeScale = EntityDynamicInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); if (!ok) { timeScale = _linearTimeScale; } ok = true; - hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + hand = EntityDynamicInterface::extractStringArgument("hold", arguments, "hand", ok, false); if (!ok || !(hand == "left" || hand == "right")) { hand = _hand; } @@ -353,20 +353,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { holderID = myAvatar->getSessionUUID(); ok = true; - kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false); + kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false); if (!ok) { kinematic = _kinematic; } ok = true; - kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments, + kinematicSetVelocity = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematicSetVelocity", ok, false); if (!ok) { kinematicSetVelocity = _kinematicSetVelocity; } ok = true; - ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false); + ignoreIK = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false); if (!ok) { ignoreIK = _ignoreIK; } @@ -400,8 +400,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - ownerEntity->setActionDataNeedsTransmit(true); + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); } }); } @@ -410,7 +410,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { } QVariantMap AvatarActionHold::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); + QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&]{ arguments["holderID"] = _holderID; arguments["relativePosition"] = glmToQMap(_relativePosition); @@ -429,7 +429,7 @@ QByteArray AvatarActionHold::serialize() const { QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); withReadLock([&]{ - dataStream << ACTION_TYPE_HOLD; + dataStream << DYNAMIC_TYPE_HOLD; dataStream << getID(); dataStream << AvatarActionHold::holdVersion; @@ -451,7 +451,7 @@ QByteArray AvatarActionHold::serialize() const { void AvatarActionHold::deserialize(QByteArray serializedArguments) { QDataStream dataStream(serializedArguments); - EntityActionType type; + EntityDynamicType type; dataStream >> type; assert(type == getType()); diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp deleted file mode 100644 index 23e6fc0202..0000000000 --- a/libraries/entities/src/EntityActionInterface.cpp +++ /dev/null @@ -1,332 +0,0 @@ -// -// EntityActionInterface.cpp -// libraries/entities/src -// -// Created by Seth Alves on 2015-6-4 -// 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 -// - - - -/* - - - - - +-----------------------+ +-------------------+ +------------------------------+ - | | | | | | - | EntityActionInterface | | btActionInterface | | EntityActionFactoryInterface | - | (entities) | | (bullet) | | (entities) | - +-----------------------+ +-------------------+ +------------------------------+ - | | | | | - +----+ +--+ +----------+ | | - | | | | | - +-------------------+ +--------------+ +------------------------+ +-------------------------+ - | | | | | | | | - | AssignmentAction | | ObjectAction | | InterfaceActionFactory | | AssignmentActionFactory | - |(assignment client)| | (physics) | | (interface) | | (assignment client) | - +-------------------+ +--------------+ +------------------------+ +-------------------------+ - | - | - | - +--------------------+ - | | - | ObjectActionSpring | - | (physics) | - +--------------------+ - - - - -An action is a callback which is registered with bullet. An action is called-back every physics -simulation step and can do whatever it wants with the various datastructures it has available. An -action, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that -point by a spring. - -In this system, an action is a property of an EntityItem (rather, an EntityItem has a property which -encodes a list of actions). Each action has a type and some arguments. Actions can be created by a -script or when receiving information via an EntityTree data-stream (either over the network or from an -svo file). - -In the interface, if an EntityItem has actions, this EntityItem will have pointers to ObjectAction -subclass (like ObjectActionSpring) instantiations. Code in the entities library affects an action-object -via the EntityActionInterface (which knows nothing about bullet). When the ObjectAction subclass -instance is created, it is registered as an action with bullet. Bullet will call into code in this -instance with the btActionInterface every physics-simulation step. - -Because the action can exist next to the interface's EntityTree or the entity-server's EntityTree, -parallel versions of the factories and actions are needed. - -In an entity-server, any type of action is instantiated as an AssignmentAction. This action isn't called -by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its -arguments. This may change as we try to make the entity-server's simple physics simulation better, but -right now the AssignmentAction class is a place-holder. - -The action-objects are instantiated by singleton (dependecy) subclasses of EntityActionFactoryInterface. -In the interface, the subclass is an InterfaceActionFactory and it will produce things like -ObjectActionSpring. In an entity-server the subclass is an AssignmentActionFactory and it always -produces AssignmentActions. - -Depending on the action's type, it will have various arguments. When a script changes an argument of an -action, the argument-holding member-variables of ObjectActionSpring (in this example) are updated and -also serialized into _actionData in the EntityItem. Each subclass of ObjectAction knows how to serialize -and deserialize its own arguments. _actionData is what gets sent over the wire or saved in an svo file. -When a packet-reader receives data for _actionData, it will save it in the EntityItem; this causes the -deserializer in the ObjectAction subclass to be called with the new data, thereby updating its argument -variables. These argument variables are used by the code which is run when bullet does a callback. - - - */ - -#include "EntityItem.h" - -#include "EntityActionInterface.h" - - -EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) { - QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_'); - if (normalizedActionTypeString == "none") { - return ACTION_TYPE_NONE; - } - if (normalizedActionTypeString == "offset") { - return ACTION_TYPE_OFFSET; - } - if (normalizedActionTypeString == "spring") { - return ACTION_TYPE_SPRING; - } - if (normalizedActionTypeString == "hold") { - return ACTION_TYPE_HOLD; - } - if (normalizedActionTypeString == "traveloriented") { - return ACTION_TYPE_TRAVEL_ORIENTED; - } - - qCDebug(entities) << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; - return ACTION_TYPE_NONE; -} - -QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { - switch(actionType) { - case ACTION_TYPE_NONE: - return "none"; - case ACTION_TYPE_OFFSET: - return "offset"; - case ACTION_TYPE_SPRING: - return "spring"; - case ACTION_TYPE_HOLD: - return "hold"; - case ACTION_TYPE_TRAVEL_ORIENTED: - return "travel-oriented"; - } - assert(false); - return "none"; -} - -glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return glm::vec3(0.0f); - } - - QVariant resultV = arguments[argumentName]; - if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { - qCDebug(entities) << objectName << "argument" << argumentName << "must be a map"; - ok = false; - return glm::vec3(0.0f); - } - - QVariantMap resultVM = resultV.toMap(); - if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { - qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z"; - ok = false; - return glm::vec3(0.0f); - } - - QVariant xV = resultVM["x"]; - QVariant yV = resultVM["y"]; - QVariant zV = resultVM["z"]; - - bool xOk = true; - bool yOk = true; - bool zOk = true; - float x = xV.toFloat(&xOk); - float y = yV.toFloat(&yOk); - float z = zV.toFloat(&zOk); - if (!xOk || !yOk || !zOk) { - qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float."; - ok = false; - return glm::vec3(0.0f); - } - - if (x != x || y != y || z != z) { - // at least one of the values is NaN - ok = false; - return glm::vec3(0.0f); - } - - return glm::vec3(x, y, z); -} - -glm::quat EntityActionInterface::extractQuatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return glm::quat(); - } - - QVariant resultV = arguments[argumentName]; - if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { - qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); - ok = false; - return glm::quat(); - } - - QVariantMap resultVM = resultV.toMap(); - if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) { - qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w"; - 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) { - qCDebug(entities) << objectName << "argument" << argumentName - << "must be a map with keys: x, y, z, and w of type float."; - ok = false; - return glm::quat(); - } - - if (x != x || y != y || z != z || w != w) { - // at least one of the components is NaN! - ok = false; - return glm::quat(); - } - - return glm::normalize(glm::quat(w, x, y, z)); -} - -float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return 0.0f; - } - - QVariant variant = arguments[argumentName]; - bool variantOk = true; - float value = variant.toFloat(&variantOk); - - if (!variantOk || std::isnan(value)) { - ok = false; - return 0.0f; - } - - return value; -} - -int EntityActionInterface::extractIntegerArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return 0.0f; - } - - QVariant variant = arguments[argumentName]; - bool variantOk = true; - int value = variant.toInt(&variantOk); - - if (!variantOk) { - ok = false; - return 0; - } - - return value; -} - -QString EntityActionInterface::extractStringArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return ""; - } - return arguments[argumentName].toString(); -} - -bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments, - QString argumentName, bool& ok, bool required) { - if (!arguments.contains(argumentName)) { - if (required) { - qCDebug(entities) << objectName << "requires argument:" << argumentName; - } - ok = false; - return false; - } - return arguments[argumentName].toBool(); -} - - - -QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType) -{ - return stream << (quint16)entityActionType; -} - -QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType) -{ - quint16 actionTypeAsInt; - stream >> actionTypeAsInt; - entityActionType = (EntityActionType)actionTypeAsInt; - return stream; -} - -QString serializedActionsToDebugString(QByteArray data) { - if (data.size() == 0) { - return QString(); - } - QVector serializedActions; - QDataStream serializedActionsStream(data); - serializedActionsStream >> serializedActions; - - QString result; - foreach(QByteArray serializedAction, serializedActions) { - QDataStream serializedActionStream(serializedAction); - EntityActionType actionType; - QUuid actionID; - serializedActionStream >> actionType; - serializedActionStream >> actionID; - result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " "; - } - - return result; -} diff --git a/libraries/entities/src/EntityActionFactoryInterface.h b/libraries/entities/src/EntityDynamicFactoryInterface.h similarity index 56% rename from libraries/entities/src/EntityActionFactoryInterface.h rename to libraries/entities/src/EntityDynamicFactoryInterface.h index adff1a53ba..7d44b0a5e9 100644 --- a/libraries/entities/src/EntityActionFactoryInterface.h +++ b/libraries/entities/src/EntityDynamicFactoryInterface.h @@ -1,5 +1,5 @@ // -// EntityActionFactoryInterface.cpp +// EntityDynamicFactoryInterface.cpp // libraries/entities/src // // Created by Seth Alves on 2015-6-2 @@ -9,26 +9,26 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_EntityActionFactoryInterface_h -#define hifi_EntityActionFactoryInterface_h +#ifndef hifi_EntityDynamicFactoryInterface_h +#define hifi_EntityDynamicFactoryInterface_h #include -#include "EntityActionInterface.h" +#include "EntityDynamicInterface.h" -class EntityActionFactoryInterface : public QObject, public Dependency { +class EntityDynamicFactoryInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: - EntityActionFactoryInterface() { } - virtual ~EntityActionFactoryInterface() { } - virtual EntityActionPointer factory(EntityActionType type, + EntityDynamicFactoryInterface() { } + virtual ~EntityDynamicFactoryInterface() { } + virtual EntityDynamicPointer factory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity, QVariantMap arguments) { assert(false); return nullptr; } - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, + virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) { assert(false); return nullptr; } }; -#endif // hifi_EntityActionFactoryInterface_h +#endif // hifi_EntityDynamicFactoryInterface_h diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp new file mode 100644 index 0000000000..bed3185b8f --- /dev/null +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -0,0 +1,347 @@ +// +// EntityDynamicInterface.cpp +// libraries/entities/src +// +// Created by Seth Alves on 2015-6-4 +// 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 +// + + + +/* + + +-------------------------+ +--------------------------------+ + | | | | + | EntityDynamicsInterface | | EntityDynamicsFactoryInterface | + | (entities) | | (entities) | + +-------------------------+ +--------------------------------+ + | | | | + +----+ +--+ | | + | | | | + +---------------------+ +----------------+ +--------------------------+ +---------------------------+ + | | | | | | | | + | AssignmentDynamics | | ObjectDynamics | | InterfaceDynamicsFactory | | AssignmentDynamicsFactory | + |(assignment client) | | (physics) | | (interface) | | (assignment client) | + +---------------------+ +----------------+ +--------------------------+ +---------------------------+ + | | + | | + +---------------------+ | | + | | | | + | btActionInterface | | | + | (bullet) | | | + +---------------------+ | | + | | | + +--------------+ +------------------+ +-------------------+ + | | | | | | + | ObjectAction | | ObjectConstraint | | btTypedConstraint | + | (physics) | | (physics) --|--->| (bullet) | + +--------------+ +------------------+ +-------------------+ + | | + | | + +--------------------+ +-----------------------+ + | | | | + | ObjectActionSpring | | ObjectConstraintHinge | + | (physics) | | (physics) | + +--------------------+ +-----------------------+ + + + +An dynamic is a callback which is registered with bullet. An dynamic is called-back every physics +simulation step and can do whatever it wants with the various datastructures it has available. An +dynamic, for example, can pull an EntityItem toward a point as if that EntityItem were connected to that +point by a spring. + +In this system, an dynamic is a property of an EntityItem (rather, an EntityItem has a property which +encodes a list of dynamics). Each dynamic has a type and some arguments. Dynamics can be created by a +script or when receiving information via an EntityTree data-stream (either over the network or from an +svo file). + +In the interface, if an EntityItem has dynamics, this EntityItem will have pointers to ObjectDynamic +subclass (like ObjectDynamicSpring) instantiations. Code in the entities library affects an dynamic-object +via the EntityDynamicInterface (which knows nothing about bullet). When the ObjectDynamic subclass +instance is created, it is registered as an dynamic with bullet. Bullet will call into code in this +instance with the btDynamicInterface every physics-simulation step. + +Because the dynamic can exist next to the interface's EntityTree or the entity-server's EntityTree, +parallel versions of the factories and dynamics are needed. + +In an entity-server, any type of dynamic is instantiated as an AssignmentDynamic. This dynamic isn't called +by bullet (which isn't part of an assignment-client). It does nothing but remember its type and its +arguments. This may change as we try to make the entity-server's simple physics simulation better, but +right now the AssignmentDynamic class is a place-holder. + +The dynamic-objects are instantiated by singleton (dependecy) subclasses of EntityDynamicFactoryInterface. +In the interface, the subclass is an InterfaceDynamicFactory and it will produce things like +ObjectDynamicSpring. In an entity-server the subclass is an AssignmentDynamicFactory and it always +produces AssignmentDynamics. + +Depending on the dynamic's type, it will have various arguments. When a script changes an argument of an +dynamic, the argument-holding member-variables of ObjectDynamicSpring (in this example) are updated and +also serialized into _dynamicData in the EntityItem. Each subclass of ObjectDynamic knows how to serialize +and deserialize its own arguments. _dynamicData is what gets sent over the wire or saved in an svo file. +When a packet-reader receives data for _dynamicData, it will save it in the EntityItem; this causes the +deserializer in the ObjectDynamic subclass to be called with the new data, thereby updating its argument +variables. These argument variables are used by the code which is run when bullet does a callback. + + +*/ + +#include "EntityItem.h" + +#include "EntityDynamicInterface.h" + + +EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicTypeString) { + QString normalizedDynamicTypeString = dynamicTypeString.toLower().remove('-').remove('_'); + if (normalizedDynamicTypeString == "none") { + return DYNAMIC_TYPE_NONE; + } + if (normalizedDynamicTypeString == "offset") { + return DYNAMIC_TYPE_OFFSET; + } + if (normalizedDynamicTypeString == "spring") { + return DYNAMIC_TYPE_SPRING; + } + if (normalizedDynamicTypeString == "hold") { + return DYNAMIC_TYPE_HOLD; + } + if (normalizedDynamicTypeString == "traveloriented") { + return DYNAMIC_TYPE_TRAVEL_ORIENTED; + } + if (normalizedDynamicTypeString == "hinge") { + return DYNAMIC_TYPE_HINGE; + } + if (normalizedDynamicTypeString == "fargrab") { + return DYNAMIC_TYPE_FAR_GRAB; + } + + qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name" + << dynamicTypeString; + return DYNAMIC_TYPE_NONE; +} + +QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicType) { + switch(dynamicType) { + case DYNAMIC_TYPE_NONE: + return "none"; + case DYNAMIC_TYPE_OFFSET: + return "offset"; + case DYNAMIC_TYPE_SPRING: + return "spring"; + case DYNAMIC_TYPE_HOLD: + return "hold"; + case DYNAMIC_TYPE_TRAVEL_ORIENTED: + return "travel-oriented"; + case DYNAMIC_TYPE_HINGE: + return "hinge"; + case DYNAMIC_TYPE_FAR_GRAB: + return "far-grab"; + } + assert(false); + return "none"; +} + +glm::vec3 EntityDynamicInterface::extractVec3Argument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return glm::vec3(0.0f); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map"; + ok = false; + return glm::vec3(0.0f); + } + + QVariantMap resultVM = resultV.toMap(); + if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) { + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z"; + ok = false; + return glm::vec3(0.0f); + } + + QVariant xV = resultVM["x"]; + QVariant yV = resultVM["y"]; + QVariant zV = resultVM["z"]; + + bool xOk = true; + bool yOk = true; + bool zOk = true; + float x = xV.toFloat(&xOk); + float y = yV.toFloat(&yOk); + float z = zV.toFloat(&zOk); + if (!xOk || !yOk || !zOk) { + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, and z of type float."; + ok = false; + return glm::vec3(0.0f); + } + + if (x != x || y != y || z != z) { + // at least one of the values is NaN + ok = false; + return glm::vec3(0.0f); + } + + return glm::vec3(x, y, z); +} + +glm::quat EntityDynamicInterface::extractQuatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return glm::quat(); + } + + QVariant resultV = arguments[argumentName]; + if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) { + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map, not" << resultV.typeName(); + ok = false; + return glm::quat(); + } + + QVariantMap resultVM = resultV.toMap(); + if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z") || !resultVM.contains("w")) { + qCDebug(entities) << objectName << "argument" << argumentName << "must be a map with keys: x, y, z, and w"; + 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) { + qCDebug(entities) << objectName << "argument" << argumentName + << "must be a map with keys: x, y, z, and w of type float."; + ok = false; + return glm::quat(); + } + + if (x != x || y != y || z != z || w != w) { + // at least one of the components is NaN! + ok = false; + return glm::quat(); + } + + return glm::normalize(glm::quat(w, x, y, z)); +} + +float EntityDynamicInterface::extractFloatArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return 0.0f; + } + + QVariant variant = arguments[argumentName]; + bool variantOk = true; + float value = variant.toFloat(&variantOk); + + if (!variantOk || std::isnan(value)) { + ok = false; + return 0.0f; + } + + return value; +} + +int EntityDynamicInterface::extractIntegerArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return 0.0f; + } + + QVariant variant = arguments[argumentName]; + bool variantOk = true; + int value = variant.toInt(&variantOk); + + if (!variantOk) { + ok = false; + return 0; + } + + return value; +} + +QString EntityDynamicInterface::extractStringArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return ""; + } + return arguments[argumentName].toString(); +} + +bool EntityDynamicInterface::extractBooleanArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qCDebug(entities) << objectName << "requires argument:" << argumentName; + } + ok = false; + return false; + } + return arguments[argumentName].toBool(); +} + +QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType) { + return stream << (quint16)entityDynamicType; +} + +QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType) { + quint16 dynamicTypeAsInt; + stream >> dynamicTypeAsInt; + entityDynamicType = (EntityDynamicType)dynamicTypeAsInt; + return stream; +} + +QString serializedDynamicsToDebugString(QByteArray data) { + if (data.size() == 0) { + return QString(); + } + QVector serializedDynamics; + QDataStream serializedDynamicsStream(data); + serializedDynamicsStream >> serializedDynamics; + + QString result; + foreach(QByteArray serializedDynamic, serializedDynamics) { + QDataStream serializedDynamicStream(serializedDynamic); + EntityDynamicType dynamicType; + QUuid dynamicID; + serializedDynamicStream >> dynamicType; + serializedDynamicStream >> dynamicID; + result += EntityDynamicInterface::dynamicTypeToString(dynamicType) + "-" + dynamicID.toString() + " "; + } + + return result; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityDynamicInterface.h similarity index 68% rename from libraries/entities/src/EntityActionInterface.h rename to libraries/entities/src/EntityDynamicInterface.h index d9a901f1f6..93d9ffa43e 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -1,5 +1,5 @@ // -// EntityActionInterface.h +// EntityDynamicInterface.h // libraries/entities/src // // Created by Seth Alves on 2015-6-2 @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_EntityActionInterface_h -#define hifi_EntityActionInterface_h +#ifndef hifi_EntityDynamicInterface_h +#define hifi_EntityDynamicInterface_h #include #include @@ -23,22 +23,27 @@ using EntityItemWeakPointer = std::weak_ptr; class EntitySimulation; using EntitySimulationPointer = std::shared_ptr; -enum EntityActionType { - // keep these synchronized with actionTypeFromString and actionTypeToString - ACTION_TYPE_NONE = 0, - ACTION_TYPE_OFFSET = 1000, - ACTION_TYPE_SPRING = 2000, - ACTION_TYPE_HOLD = 3000, - ACTION_TYPE_TRAVEL_ORIENTED = 4000 +enum EntityDynamicType { + // keep these synchronized with dynamicTypeFromString and dynamicTypeToString + DYNAMIC_TYPE_NONE = 0, + DYNAMIC_TYPE_OFFSET = 1000, + DYNAMIC_TYPE_SPRING = 2000, + DYNAMIC_TYPE_HOLD = 3000, + DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000, + DYNAMIC_TYPE_HINGE = 5000, + DYNAMIC_TYPE_FAR_GRAB = 6000 }; -class EntityActionInterface { +class EntityDynamicInterface { public: - EntityActionInterface(EntityActionType type, const QUuid& id) : _id(id), _type(type) { } - virtual ~EntityActionInterface() { } + EntityDynamicInterface(EntityDynamicType type, const QUuid& id) : _id(id), _type(type) { } + virtual ~EntityDynamicInterface() { } const QUuid& getID() const { return _id; } - EntityActionType getType() const { return _type; } + EntityDynamicType getType() const { return _type; } + virtual bool isAction() const { return false; } + virtual bool isConstraint() const { return false; } + virtual bool isReadyForAdd() const { return true; } bool isActive() { return _active; } @@ -51,8 +56,8 @@ public: virtual QByteArray serialize() const = 0; virtual void deserialize(QByteArray serializedArguments) = 0; - static EntityActionType actionTypeFromString(QString actionTypeString); - static QString actionTypeToString(EntityActionType actionType); + static EntityDynamicType dynamicTypeFromString(QString dynamicTypeString); + static QString dynamicTypeToString(EntityDynamicType dynamicType); virtual bool lifetimeIsOver() { return false; } virtual quint64 getExpires() { return 0; } @@ -82,26 +87,26 @@ public: protected: virtual glm::vec3 getPosition() = 0; - virtual void setPosition(glm::vec3 position) = 0; + // virtual void setPosition(glm::vec3 position) = 0; virtual glm::quat getRotation() = 0; - virtual void setRotation(glm::quat rotation) = 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; QUuid _id; - EntityActionType _type; + EntityDynamicType _type; bool _active { false }; - bool _isMine { false }; // did this interface create / edit this action? + bool _isMine { false }; // did this interface create / edit this dynamic? }; -typedef std::shared_ptr EntityActionPointer; +typedef std::shared_ptr EntityDynamicPointer; -QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType); -QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType); +QDataStream& operator<<(QDataStream& stream, const EntityDynamicType& entityDynamicType); +QDataStream& operator>>(QDataStream& stream, EntityDynamicType& entityDynamicType); -QString serializedActionsToDebugString(QByteArray data); +QString serializedDynamicsToDebugString(QByteArray data); -#endif // hifi_EntityActionInterface_h +#endif // hifi_EntityDynamicInterface_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3f732e26cb..a6de541958 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -30,7 +30,7 @@ #include "EntitiesLogging.h" #include "EntityTree.h" #include "EntitySimulation.h" -#include "EntityActionFactoryInterface.h" +#include "EntityDynamicFactoryInterface.h" int EntityItem::_maxActionsDataSize = 800; @@ -280,7 +280,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL()); APPEND_ENTITY_PROPERTY(PROP_HREF, getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, getDescription()); - APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData()); + APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getDynamicData()); // convert AVATAR_SELF_ID to actual sessionUUID. QUuid actualParentID = getParentID(); @@ -821,7 +821,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); - READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData); + READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setDynamicData); { // parentID and parentJointIndex are also protected by simulation ownership bool oldOverwrite = overwriteLocalData; @@ -1251,7 +1251,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName); COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref); COPY_ENTITY_PROPERTY_TO_PROPERTIES(description, getDescription); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getDynamicData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID); COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex); COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube); @@ -1358,7 +1358,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName); SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setDynamicData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); @@ -1872,10 +1872,20 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask iAmHoldingThis = true; } // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); + QList holdActions = getActionsOfType(DYNAMIC_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); while (i != holdActions.end()) { - EntityActionPointer action = *i; + EntityDynamicPointer action = *i; + if (action->isMine()) { + iAmHoldingThis = true; + break; + } + i++; + } + QList farGrabActions = getActionsOfType(DYNAMIC_TYPE_FAR_GRAB); + i = farGrabActions.begin(); + while (i != farGrabActions.end()) { + EntityDynamicPointer action = *i; if (action->isMine()) { iAmHoldingThis = true; break; @@ -1941,18 +1951,18 @@ void EntityItem::rememberHasSimulationOwnershipBid() const { QString EntityItem::actionsToDebugString() { QString result; QVector serializedActions; - QHash::const_iterator i = _objectActions.begin(); + QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { const QUuid id = i.key(); - EntityActionPointer action = _objectActions[id]; - EntityActionType actionType = action->getType(); + EntityDynamicPointer action = _objectActions[id]; + EntityDynamicType actionType = action->getType(); result += QString("") + actionType + ":" + action->getID().toString() + " "; i++; } return result; } -bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPointer action) { +bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPointer action) { bool result; withWriteLock([&] { checkWaitingToRemove(simulation); @@ -1960,7 +1970,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint result = addActionInternal(simulation, action); if (result) { action->setIsMine(true); - _actionDataDirty = true; + _dynamicDataDirty = true; } else { removeActionInternal(action->getID()); } @@ -1969,7 +1979,7 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPoint return result; } -bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action) { +bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action) { assert(action); assert(simulation); auto actionOwnerEntity = action->getOwnerEntity().lock(); @@ -1979,7 +1989,7 @@ bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityAct const QUuid& actionID = action->getID(); assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action); _objectActions[actionID] = action; - simulation->addAction(action); + simulation->addDynamic(action); bool success; QByteArray newDataCache; @@ -2003,14 +2013,13 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a return; } - EntityActionPointer action = _objectActions[actionID]; + EntityDynamicPointer action = _objectActions[actionID]; success = action->updateArguments(arguments); if (success) { action->setIsMine(true); serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; - _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar } else { qCDebug(entities) << "EntityItem::updateAction failed"; } @@ -2035,7 +2044,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi simulation = entityTree ? entityTree->getSimulation() : nullptr; } - EntityActionPointer action = _objectActions[actionID]; + EntityDynamicPointer action = _objectActions[actionID]; action->setOwnerEntity(nullptr); action->setIsMine(false); @@ -2049,7 +2058,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar - setActionDataNeedsTransmit(true); + setDynamicDataNeedsTransmit(true); return success; } return false; @@ -2057,10 +2066,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi bool EntityItem::clearActions(EntitySimulationPointer simulation) { withWriteLock([&] { - QHash::iterator i = _objectActions.begin(); + QHash::iterator i = _objectActions.begin(); while (i != _objectActions.end()) { const QUuid id = i.key(); - EntityActionPointer action = _objectActions[id]; + EntityDynamicPointer action = _objectActions[id]; i = _objectActions.erase(i); action->setOwnerEntity(nullptr); action->removeFromSimulation(simulation); @@ -2101,12 +2110,12 @@ void EntityItem::deserializeActionsInternal() { serializedActionsStream >> serializedActions; } - // Keep track of which actions got added or updated by the new actionData + // Keep track of which actions got added or updated by the new dynamicData QSet updated; foreach(QByteArray serializedAction, serializedActions) { QDataStream serializedActionStream(serializedAction); - EntityActionType actionType; + EntityDynamicType actionType; QUuid actionID; serializedActionStream >> actionType; serializedActionStream >> actionID; @@ -2115,7 +2124,7 @@ void EntityItem::deserializeActionsInternal() { } if (_objectActions.contains(actionID)) { - EntityActionPointer action = _objectActions[actionID]; + EntityDynamicPointer action = _objectActions[actionID]; // TODO: make sure types match? there isn't currently a way to // change the type of an existing action. if (!action->isMine()) { @@ -2123,9 +2132,9 @@ void EntityItem::deserializeActionsInternal() { } updated << actionID; } else { - auto actionFactory = DependencyManager::get(); + auto actionFactory = DependencyManager::get(); EntityItemPointer entity = getThisPointer(); - EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction); + EntityDynamicPointer action = actionFactory->factoryBA(entity, serializedAction); if (action) { entity->addActionInternal(simulation, action); updated << actionID; @@ -2140,15 +2149,15 @@ void EntityItem::deserializeActionsInternal() { } // remove any actions that weren't included in the new data. - QHash::const_iterator i = _objectActions.begin(); + QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { QUuid id = i.key(); if (!updated.contains(id)) { - EntityActionPointer action = i.value(); + EntityDynamicPointer action = i.value(); if (action->isMine()) { // we just received an update that didn't include one of our actions. tell the server about it (again). - setActionDataNeedsTransmit(true); + setDynamicDataNeedsTransmit(true); } else { // don't let someone else delete my action. _actionsToRemove << id; @@ -2167,7 +2176,7 @@ void EntityItem::deserializeActionsInternal() { } } - _actionDataDirty = true; + _dynamicDataDirty = true; return; } @@ -2179,15 +2188,15 @@ void EntityItem::checkWaitingToRemove(EntitySimulationPointer simulation) { _actionsToRemove.clear(); } -void EntityItem::setActionData(QByteArray actionData) { +void EntityItem::setDynamicData(QByteArray dynamicData) { withWriteLock([&] { - setActionDataInternal(actionData); + setDynamicDataInternal(dynamicData); }); } -void EntityItem::setActionDataInternal(QByteArray actionData) { - if (_allActionsDataCache != actionData) { - _allActionsDataCache = actionData; +void EntityItem::setDynamicDataInternal(QByteArray dynamicData) { + if (_allActionsDataCache != dynamicData) { + _allActionsDataCache = dynamicData; deserializeActionsInternal(); } checkWaitingToRemove(); @@ -2201,10 +2210,10 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { } QVector serializedActions; - QHash::const_iterator i = _objectActions.begin(); + QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { const QUuid id = i.key(); - EntityActionPointer action = _objectActions[id]; + EntityDynamicPointer action = _objectActions[id]; QByteArray bytesForAction = action->serialize(); serializedActions << bytesForAction; i++; @@ -2224,23 +2233,23 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { return; } -const QByteArray EntityItem::getActionDataInternal() const { - if (_actionDataDirty) { +const QByteArray EntityItem::getDynamicDataInternal() const { + if (_dynamicDataDirty) { bool success; serializeActions(success, _allActionsDataCache); if (success) { - _actionDataDirty = false; + _dynamicDataDirty = false; } } return _allActionsDataCache; } -const QByteArray EntityItem::getActionData() const { +const QByteArray EntityItem::getDynamicData() const { QByteArray result; - if (_actionDataDirty) { + if (_dynamicDataDirty) { withWriteLock([&] { - getActionDataInternal(); + getDynamicDataInternal(); result = _allActionsDataCache; }); } else { @@ -2255,9 +2264,9 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { QVariantMap result; withReadLock([&] { if (_objectActions.contains(actionID)) { - EntityActionPointer action = _objectActions[actionID]; + EntityDynamicPointer action = _objectActions[actionID]; result = action->getArguments(); - result["type"] = EntityActionInterface::actionTypeToString(action->getType()); + result["type"] = EntityDynamicInterface::dynamicTypeToString(action->getType()); } }); @@ -2265,7 +2274,7 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { } bool EntityItem::shouldSuppressLocationEdits() const { - QHash::const_iterator i = _objectActions.begin(); + QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { if (i.value()->shouldSuppressLocationEdits()) { return true; @@ -2276,12 +2285,12 @@ bool EntityItem::shouldSuppressLocationEdits() const { return false; } -QList EntityItem::getActionsOfType(EntityActionType typeToGet) const { - QList result; +QList EntityItem::getActionsOfType(EntityDynamicType typeToGet) const { + QList result; - QHash::const_iterator i = _objectActions.begin(); + QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { - EntityActionPointer action = i.value(); + EntityDynamicPointer action = i.value(); if (action->getType() == typeToGet && action->isActive()) { result += action; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3f75c595a5..ff5f12b2f7 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -36,17 +36,17 @@ #include "EntityTypes.h" #include "SimulationOwner.h" #include "SimulationFlags.h" -#include "EntityActionInterface.h" +#include "EntityDynamicInterface.h" class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; -class EntityActionInterface; +class EntityDynamicInterface; class EntityItemProperties; class EntityTree; class btCollisionShape; typedef std::shared_ptr EntityTreePointer; -typedef std::shared_ptr EntityActionPointer; +typedef std::shared_ptr EntityDynamicPointer; typedef std::shared_ptr EntityTreeElementPointer; using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr; @@ -398,22 +398,22 @@ public: void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } QString actionsToDebugString(); - bool addAction(EntitySimulationPointer simulation, EntityActionPointer action); + bool addAction(EntitySimulationPointer simulation, EntityDynamicPointer action); bool updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments); bool removeAction(EntitySimulationPointer simulation, const QUuid& actionID); bool clearActions(EntitySimulationPointer simulation); - void setActionData(QByteArray actionData); - const QByteArray getActionData() const; + void setDynamicData(QByteArray dynamicData); + const QByteArray getDynamicData() const; bool hasActions() const { return !_objectActions.empty(); } QList getActionIDs() const { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); - void setActionDataDirty(bool value) const { _actionDataDirty = value; } - bool actionDataDirty() const { return _actionDataDirty; } + void setDynamicDataDirty(bool value) const { _dynamicDataDirty = value; } + bool dynamicDataDirty() const { return _dynamicDataDirty; } - void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; } - bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; } + void setDynamicDataNeedsTransmit(bool value) const { _dynamicDataNeedsTransmit = value; } + bool dynamicDataNeedsTransmit() const { return _dynamicDataNeedsTransmit; } bool shouldSuppressLocationEdits() const; @@ -421,7 +421,7 @@ public: const QUuid& getSourceUUID() const { return _sourceUUID; } bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } - QList getActionsOfType(EntityActionType typeToGet) const; + QList getActionsOfType(EntityDynamicType typeToGet) const; // these are in the frame of this object virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); } @@ -479,8 +479,8 @@ protected: void setSimulated(bool simulated) { _simulated = simulated; } - const QByteArray getActionDataInternal() const; - void setActionDataInternal(QByteArray actionData); + const QByteArray getDynamicDataInternal() const; + void setDynamicDataInternal(QByteArray dynamicData); virtual void locationChanged(bool tellPhysics = true) override; virtual void dimensionsChanged() override; @@ -570,22 +570,22 @@ protected: void* _physicsInfo { nullptr }; // set by EntitySimulation bool _simulated; // set by EntitySimulation - bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action); + bool addActionInternal(EntitySimulationPointer simulation, EntityDynamicPointer action); bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr); void deserializeActionsInternal(); void serializeActions(bool& success, QByteArray& result) const; - QHash _objectActions; + QHash _objectActions; static int _maxActionsDataSize; mutable QByteArray _allActionsDataCache; - // when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is + // when an entity-server starts up, EntityItem::setDynamicData is called before the entity-tree is // ready. This means we can't find our EntityItemPointer or add the action to the simulation. These // are used to keep track of and work around this situation. void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr); mutable QSet _actionsToRemove; - mutable bool _actionDataDirty { false }; - mutable bool _actionDataNeedsTransmit { false }; + mutable bool _dynamicDataDirty { false }; + mutable bool _dynamicDataNeedsTransmit { false }; // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 2c332e8d05..10479e931c 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -24,8 +24,8 @@ #include #include "EntitiesLogging.h" -#include "EntityActionFactoryInterface.h" -#include "EntityActionInterface.h" +#include "EntityDynamicFactoryInterface.h" +#include "EntityDynamicInterface.h" #include "EntitySimulation.h" #include "EntityTree.h" #include "LightEntityItem.h" @@ -1133,7 +1133,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, PROFILE_RANGE(script_entities, __FUNCTION__); QUuid actionID = QUuid::createUuid(); - auto actionFactory = DependencyManager::get(); + auto actionFactory = DependencyManager::get(); bool success = false; actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { // create this action even if the entity doesn't have physics info. it will often be the @@ -1142,11 +1142,11 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, // if (!entity->getPhysicsInfo()) { // return false; // } - EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); - if (actionType == ACTION_TYPE_NONE) { + EntityDynamicType dynamicType = EntityDynamicInterface::dynamicTypeFromString(actionTypeString); + if (dynamicType == DYNAMIC_TYPE_NONE) { return false; } - EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments); + EntityDynamicPointer action = actionFactory->factory(dynamicType, actionID, entity, arguments); if (!action) { return false; } diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index a29ea8e2c8..fbbc1bde71 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -279,25 +279,25 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) { } } -void EntitySimulation::addAction(EntityActionPointer action) { +void EntitySimulation::addDynamic(EntityDynamicPointer dynamic) { QMutexLocker lock(&_mutex); - _actionsToAdd += action; + _dynamicsToAdd += dynamic; } -void EntitySimulation::removeAction(const QUuid actionID) { +void EntitySimulation::removeDynamic(const QUuid dynamicID) { QMutexLocker lock(&_mutex); - _actionsToRemove += actionID; + _dynamicsToRemove += dynamicID; } -void EntitySimulation::removeActions(QList actionIDsToRemove) { +void EntitySimulation::removeDynamics(QList dynamicIDsToRemove) { QMutexLocker lock(&_mutex); - foreach(QUuid uuid, actionIDsToRemove) { - _actionsToRemove.insert(uuid); + foreach(QUuid uuid, dynamicIDsToRemove) { + _dynamicsToRemove.insert(uuid); } } -void EntitySimulation::applyActionChanges() { +void EntitySimulation::applyDynamicChanges() { QMutexLocker lock(&_mutex); - _actionsToAdd.clear(); - _actionsToRemove.clear(); + _dynamicsToAdd.clear(); + _dynamicsToRemove.clear(); } diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index f8f506ac70..84d30c495d 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -18,7 +18,7 @@ #include -#include "EntityActionInterface.h" +#include "EntityDynamicInterface.h" #include "EntityItem.h" #include "EntityTree.h" @@ -59,10 +59,10 @@ public: // friend class EntityTree; - virtual void addAction(EntityActionPointer action); - virtual void removeAction(const QUuid actionID); - virtual void removeActions(QList actionIDsToRemove); - virtual void applyActionChanges(); + virtual void addDynamic(EntityDynamicPointer dynamic); + virtual void removeDynamic(const QUuid dynamicID); + virtual void removeDynamics(QList dynamicIDsToRemove); + virtual void applyDynamicChanges(); /// \param entity pointer to EntityItem to be added /// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked @@ -103,8 +103,8 @@ protected: SetOfEntities _entitiesToSort; // entities moved by simulation (and might need resort in EntityTree) SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion - QList _actionsToAdd; - QSet _actionsToRemove; + QList _dynamicsToAdd; + QSet _dynamicsToRemove; protected: SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f544a4e5c7..3ad5cc92a5 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -808,7 +808,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { QByteArray value = properties.getActionData(); - QString changeHint = serializedActionsToDebugString(value); + QString changeHint = serializedDynamicsToDebugString(value); changedProperties[index] = QString("actionData:") + changeHint; } } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 246821908a..3ad4dbf28d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -49,7 +49,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_ZONE_FILTERS; + return VERSION_ENTITIES_HINGE_CONSTRAINT; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 03a773f24f..074876862f 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -206,6 +206,7 @@ const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65; const PacketVersion VERSION_ENTITIES_SERVER_SCRIPTS = 66; const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67; const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68; +const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d383f4c199..0c804fb5b7 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -94,7 +94,7 @@ void EntityMotionState::updateServerPhysicsVariables() { _serverPosition = localTransform.getTranslation(); _serverRotation = localTransform.getRotation(); _serverAcceleration = _entity->getAcceleration(); - _serverActionData = _entity->getActionData(); + _serverActionData = _entity->getDynamicData(); } void EntityMotionState::handleDeactivation() { @@ -309,7 +309,7 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(entityTreeIsLocked()); return _outgoingPriority != 0 || Physics::getSessionUUID() == _entity->getSimulatorID() - || _entity->actionDataNeedsTransmit(); + || _entity->dynamicDataNeedsTransmit(); } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { @@ -335,7 +335,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverAcceleration = Vectors::ZERO; _serverAngularVelocity = worldVelocityToLocal.transform(bulletToGLM(_body->getAngularVelocity())); _lastStep = simulationStep; - _serverActionData = _entity->getActionData(); + _serverActionData = _entity->getDynamicData(); _numInactiveUpdates = 1; return false; } @@ -387,7 +387,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } } - if (_entity->actionDataNeedsTransmit()) { + if (_entity->dynamicDataNeedsTransmit()) { _outgoingPriority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY; return true; } @@ -474,7 +474,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { return false; } - if (_entity->actionDataNeedsTransmit()) { + if (_entity->dynamicDataNeedsTransmit()) { return true; } @@ -551,7 +551,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ _serverPosition = localTransform.getTranslation(); _serverRotation = localTransform.getRotation(); _serverAcceleration = _entity->getAcceleration(); - _serverActionData = _entity->getActionData(); + _serverActionData = _entity->getDynamicData(); EntityItemProperties properties; @@ -562,8 +562,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setVelocity(_serverVelocity); properties.setAcceleration(_serverAcceleration); properties.setAngularVelocity(_serverAngularVelocity); - if (_entity->actionDataNeedsTransmit()) { - _entity->setActionDataNeedsTransmit(false); + if (_entity->dynamicDataNeedsTransmit()) { + _entity->setDynamicDataNeedsTransmit(false); properties.setActionData(_serverActionData); } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 95448ad029..5f5f763ca6 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -16,13 +16,10 @@ #include "PhysicsLogging.h" -ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) : +ObjectAction::ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) : btActionInterface(), - EntityActionInterface(type, id), - _ownerEntity(ownerEntity) { -} - -ObjectAction::~ObjectAction() { + ObjectDynamic(type, id, ownerEntity) +{ } void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { @@ -35,7 +32,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta }); if (!ownerEntity) { - qCDebug(physics) << "warning -- action with no entity removing self from btCollisionWorld."; + qCDebug(physics) << "warning -- action [" << _tag << "] with no entity removing self from btCollisionWorld."; btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld); if (dynamicsWorld) { dynamicsWorld->removeAction(this); @@ -64,240 +61,5 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta updateActionWorker(deltaTimeStep); } -qint64 ObjectAction::getEntityServerClockSkew() const { - auto nodeList = DependencyManager::get(); - - auto ownerEntity = _ownerEntity.lock(); - if (!ownerEntity) { - return 0; - } - - const QUuid& entityServerNodeID = ownerEntity->getSourceUUID(); - auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID); - if (entityServerNode) { - return entityServerNode->getClockSkewUsec(); - } - return 0; -} - -bool ObjectAction::updateArguments(QVariantMap arguments) { - bool somethingChanged = false; - - withWriteLock([&]{ - quint64 previousExpires = _expires; - QString previousTag = _tag; - - bool ttlSet = true; - float ttl = EntityActionInterface::extractFloatArgument("action", arguments, "ttl", ttlSet, false); - if (ttlSet) { - quint64 now = usecTimestampNow(); - _expires = now + (quint64)(ttl * USECS_PER_SECOND); - } else { - _expires = 0; - } - - bool tagSet = true; - QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false); - if (tagSet) { - _tag = tag; - } else { - tag = ""; - } - - if (previousExpires != _expires || previousTag != _tag) { - somethingChanged = true; - } - }); - - return somethingChanged; -} - -QVariantMap ObjectAction::getArguments() { - QVariantMap arguments; - withReadLock([&]{ - if (_expires == 0) { - arguments["ttl"] = 0.0f; - } else { - quint64 now = usecTimestampNow(); - arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND; - } - arguments["tag"] = _tag; - - EntityItemPointer entity = _ownerEntity.lock(); - if (entity) { - ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState) { - arguments["::active"] = motionState->isActive(); - arguments["::motion-type"] = motionTypeToString(motionState->getMotionType()); - } else { - arguments["::no-motion-state"] = true; - } - } - arguments["isMine"] = isMine(); - }); - return arguments; -} - - void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { } - -void ObjectAction::removeFromSimulation(EntitySimulationPointer simulation) const { - QUuid myID; - withReadLock([&]{ - myID = _id; - }); - simulation->removeAction(myID); -} - -btRigidBody* ObjectAction::getRigidBody() { - ObjectMotionState* motionState = nullptr; - withReadLock([&]{ - auto ownerEntity = _ownerEntity.lock(); - if (!ownerEntity) { - return; - } - void* physicsInfo = ownerEntity->getPhysicsInfo(); - if (!physicsInfo) { - return; - } - motionState = static_cast(physicsInfo); - }); - if (motionState) { - return motionState->getRigidBody(); - } - return nullptr; -} - -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(); -} - -void ObjectAction::activateBody(bool forceActivation) { - auto rigidBody = getRigidBody(); - if (rigidBody) { - rigidBody->activate(forceActivation); - } else { - qCDebug(physics) << "ObjectAction::activateBody -- no rigid body" << (void*)rigidBody; - } -} - -void ObjectAction::forceBodyNonStatic() { - auto ownerEntity = _ownerEntity.lock(); - if (!ownerEntity) { - return; - } - void* physicsInfo = ownerEntity->getPhysicsInfo(); - ObjectMotionState* motionState = static_cast(physicsInfo); - if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) { - ownerEntity->flagForMotionStateChange(); - } -} - -bool ObjectAction::lifetimeIsOver() { - if (_expires == 0) { - return false; - } - - quint64 now = usecTimestampNow(); - if (now >= _expires) { - return true; - } - return false; -} - -quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) const { - // 0 indicates no expiration - if (timeValue == 0) { - return 0; - } - - qint64 serverClockSkew = getEntityServerClockSkew(); - if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) { - return 1; // non-zero but long-expired value to avoid negative roll-over - } - - return timeValue + serverClockSkew; -} - -quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) const { - // 0 indicates no expiration - if (timeValue == 0) { - return 0; - } - - qint64 serverClockSkew = getEntityServerClockSkew(); - if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) { - return 1; // non-zero but long-expired value to avoid negative roll-over - } - - return timeValue - serverClockSkew; -} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 43330269ac..fb141a4620 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -14,27 +14,15 @@ #define hifi_ObjectAction_h #include - #include +#include "ObjectDynamic.h" -#include - -#include "ObjectMotionState.h" -#include "BulletUtil.h" -#include "EntityActionInterface.h" - - -class ObjectAction : public btActionInterface, public EntityActionInterface, public ReadWriteLockable { +class ObjectAction : public btActionInterface, public ObjectDynamic { public: - ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); - virtual ~ObjectAction(); + ObjectAction(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectAction() {} - virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; - virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } - virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } - - virtual bool updateArguments(QVariantMap arguments) override; - virtual QVariantMap getArguments() override; + virtual bool isAction() const override { return true; } // this is called from updateAction and should be overridden by subclasses virtual void updateActionWorker(float deltaTimeStep) = 0; @@ -42,35 +30,6 @@ public: // these are from btActionInterface virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) override; virtual void debugDraw(btIDebugDraw* debugDrawer) override; - - virtual QByteArray serialize() const override = 0; - virtual void deserialize(QByteArray serializedArguments) override = 0; - - virtual bool lifetimeIsOver() override; - virtual quint64 getExpires() override { return _expires; } - -protected: - quint64 localTimeToServerTime(quint64 timeValue) const; - quint64 serverTimeToLocalTime(quint64 timeValue) const; - - virtual btRigidBody* getRigidBody(); - virtual glm::vec3 getPosition() override; - virtual void setPosition(glm::vec3 position) override; - virtual glm::quat getRotation() override; - virtual void setRotation(glm::quat rotation) override; - virtual glm::vec3 getLinearVelocity() override; - virtual void setLinearVelocity(glm::vec3 linearVelocity) override; - virtual glm::vec3 getAngularVelocity() override; - virtual void setAngularVelocity(glm::vec3 angularVelocity) override; - virtual void activateBody(bool forceActivation = false); - virtual void forceBodyNonStatic(); - - EntityItemWeakPointer _ownerEntity; - QString _tag; - quint64 _expires { 0 }; // in seconds since epoch - -private: - qint64 getEntityServerClockSkew() const; }; #endif // hifi_ObjectAction_h diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index f23b3985de..c1fb397e19 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -19,7 +19,7 @@ const uint16_t ObjectActionOffset::offsetVersion = 1; ObjectActionOffset::ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectAction(ACTION_TYPE_OFFSET, id, ownerEntity), + ObjectAction(DYNAMIC_TYPE_OFFSET, id, ownerEntity), _pointToOffsetFrom(0.0f), _linearDistance(0.0f), _linearTimeScale(FLT_MAX), @@ -88,26 +88,26 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { float linearDistance; bool needUpdate = false; - bool somethingChanged = ObjectAction::updateArguments(arguments); + bool somethingChanged = ObjectDynamic::updateArguments(arguments); withReadLock([&]{ bool ok = true; pointToOffsetFrom = - EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); + EntityDynamicInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); if (!ok) { pointToOffsetFrom = _pointToOffsetFrom; } ok = true; linearTimeScale = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); + EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); if (!ok) { linearTimeScale = _linearTimeScale; } ok = true; linearDistance = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false); + EntityDynamicInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false); if (!ok) { linearDistance = _linearDistance; } @@ -132,8 +132,8 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - ownerEntity->setActionDataNeedsTransmit(true); + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); } }); activateBody(); @@ -143,7 +143,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { } QVariantMap ObjectActionOffset::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); + QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom); arguments["linearTimeScale"] = _linearTimeScale; @@ -155,7 +155,7 @@ QVariantMap ObjectActionOffset::getArguments() { QByteArray ObjectActionOffset::serialize() const { QByteArray ba; QDataStream dataStream(&ba, QIODevice::WriteOnly); - dataStream << ACTION_TYPE_OFFSET; + dataStream << DYNAMIC_TYPE_OFFSET; dataStream << getID(); dataStream << ObjectActionOffset::offsetVersion; @@ -174,7 +174,7 @@ QByteArray ObjectActionOffset::serialize() const { void ObjectActionOffset::deserialize(QByteArray serializedArguments) { QDataStream dataStream(serializedArguments); - EntityActionType type; + EntityDynamicType type; dataStream >> type; assert(type == getType()); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index b22b3c3368..df7e5f87a3 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -21,7 +21,7 @@ const uint16_t ObjectActionSpring::springVersion = 1; ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity), + ObjectAction(DYNAMIC_TYPE_SPRING, id, ownerEntity), _positionalTarget(glm::vec3(0.0f)), _desiredPositionalTarget(glm::vec3(0.0f)), _linearTimeScale(FLT_MAX), @@ -64,11 +64,12 @@ bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) { bool valid = false; int springCount = 0; - QList springDerivedActions; - springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_SPRING)); - springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_HOLD)); + QList springDerivedActions; + springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_SPRING)); + springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_FAR_GRAB)); + springDerivedActions.append(ownerEntity->getActionsOfType(DYNAMIC_TYPE_HOLD)); - foreach (EntityActionPointer action, springDerivedActions) { + foreach (EntityDynamicPointer action, springDerivedActions) { std::shared_ptr springAction = std::static_pointer_cast(action); glm::quat rotationForAction; glm::vec3 positionForAction; @@ -190,29 +191,29 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { float angularTimeScale; bool needUpdate = false; - bool somethingChanged = ObjectAction::updateArguments(arguments); + bool somethingChanged = ObjectDynamic::updateArguments(arguments); withReadLock([&]{ // targets are required, spring-constants are optional bool ok = true; - positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false); + positionalTarget = EntityDynamicInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false); if (!ok) { positionalTarget = _desiredPositionalTarget; } ok = true; - linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false); + linearTimeScale = EntityDynamicInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false); if (!ok || linearTimeScale <= 0.0f) { linearTimeScale = _linearTimeScale; } ok = true; - rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false); + rotationalTarget = EntityDynamicInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false); if (!ok) { rotationalTarget = _desiredRotationalTarget; } ok = true; angularTimeScale = - EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false); + EntityDynamicInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false); if (!ok) { angularTimeScale = _angularTimeScale; } @@ -237,8 +238,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - ownerEntity->setActionDataNeedsTransmit(true); + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); } }); activateBody(); @@ -248,7 +249,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { } QVariantMap ObjectActionSpring::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); + QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { arguments["linearTimeScale"] = _linearTimeScale; arguments["targetPosition"] = glmToQMap(_desiredPositionalTarget); @@ -259,14 +260,7 @@ QVariantMap ObjectActionSpring::getArguments() { return arguments; } -QByteArray ObjectActionSpring::serialize() const { - QByteArray serializedActionArguments; - QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); - - dataStream << ACTION_TYPE_SPRING; - dataStream << getID(); - dataStream << ObjectActionSpring::springVersion; - +void ObjectActionSpring::serializeParameters(QDataStream& dataStream) const { withReadLock([&] { dataStream << _desiredPositionalTarget; dataStream << _linearTimeScale; @@ -277,28 +271,22 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << localTimeToServerTime(_expires); dataStream << _tag; }); +} + +QByteArray ObjectActionSpring::serialize() const { + QByteArray serializedActionArguments; + QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_SPRING; + dataStream << getID(); + dataStream << ObjectActionSpring::springVersion; + + serializeParameters(dataStream); return serializedActionArguments; } -void ObjectActionSpring::deserialize(QByteArray serializedArguments) { - QDataStream dataStream(serializedArguments); - - EntityActionType type; - dataStream >> type; - assert(type == getType()); - - QUuid id; - dataStream >> id; - assert(id == getID()); - - uint16_t serializationVersion; - dataStream >> serializationVersion; - if (serializationVersion != ObjectActionSpring::springVersion) { - assert(false); - return; - } - +void ObjectActionSpring::deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream) { withWriteLock([&] { dataStream >> _desiredPositionalTarget; dataStream >> _linearTimeScale; @@ -317,3 +305,24 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { _active = true; }); } + +void ObjectActionSpring::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectActionSpring::springVersion) { + assert(false); + return; + } + + deserializeParameters(serializedArguments, dataStream); +} diff --git a/libraries/physics/src/ObjectActionSpring.h b/libraries/physics/src/ObjectActionSpring.h index 498bb6c1f5..de9562d3fa 100644 --- a/libraries/physics/src/ObjectActionSpring.h +++ b/libraries/physics/src/ObjectActionSpring.h @@ -47,6 +47,9 @@ protected: glm::vec3 _angularVelocityTarget; virtual bool prepareForSpringUpdate(btScalar deltaTimeStep); + + void serializeParameters(QDataStream& dataStream) const; + void deserializeParameters(QByteArray serializedArguments, QDataStream& dataStream); }; #endif // hifi_ObjectActionSpring_h diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp index 8f6d45c6f1..8ab24511d7 100644 --- a/libraries/physics/src/ObjectActionTravelOriented.cpp +++ b/libraries/physics/src/ObjectActionTravelOriented.cpp @@ -19,7 +19,7 @@ const uint16_t ObjectActionTravelOriented::actionVersion = 1; ObjectActionTravelOriented::ObjectActionTravelOriented(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectAction(ACTION_TYPE_TRAVEL_ORIENTED, id, ownerEntity) { + ObjectAction(DYNAMIC_TYPE_TRAVEL_ORIENTED, id, ownerEntity) { #if WANT_DEBUG qCDebug(physics) << "ObjectActionTravelOriented::ObjectActionTravelOriented"; #endif @@ -106,16 +106,16 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) { float angularTimeScale; bool needUpdate = false; - bool somethingChanged = ObjectAction::updateArguments(arguments); + bool somethingChanged = ObjectDynamic::updateArguments(arguments); withReadLock([&]{ bool ok = true; - forward = EntityActionInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true); + forward = EntityDynamicInterface::extractVec3Argument("travel oriented action", arguments, "forward", ok, true); if (!ok) { forward = _forward; } ok = true; angularTimeScale = - EntityActionInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false); + EntityDynamicInterface::extractFloatArgument("travel oriented action", arguments, "angularTimeScale", ok, false); if (!ok) { angularTimeScale = _angularTimeScale; } @@ -136,8 +136,8 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - ownerEntity->setActionDataNeedsTransmit(true); + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); } }); activateBody(); @@ -147,7 +147,7 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) { } QVariantMap ObjectActionTravelOriented::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); + QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { arguments["forward"] = glmToQMap(_forward); arguments["angularTimeScale"] = _angularTimeScale; @@ -159,7 +159,7 @@ QByteArray ObjectActionTravelOriented::serialize() const { QByteArray serializedActionArguments; QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); - dataStream << ACTION_TYPE_TRAVEL_ORIENTED; + dataStream << DYNAMIC_TYPE_TRAVEL_ORIENTED; dataStream << getID(); dataStream << ObjectActionTravelOriented::actionVersion; @@ -177,7 +177,7 @@ QByteArray ObjectActionTravelOriented::serialize() const { void ObjectActionTravelOriented::deserialize(QByteArray serializedArguments) { QDataStream dataStream(serializedArguments); - EntityActionType type; + EntityDynamicType type; dataStream >> type; assert(type == getType()); diff --git a/libraries/physics/src/ObjectConstraint.cpp b/libraries/physics/src/ObjectConstraint.cpp new file mode 100644 index 0000000000..54fd4777e0 --- /dev/null +++ b/libraries/physics/src/ObjectConstraint.cpp @@ -0,0 +1,25 @@ +// +// ObjectConstraint.cpp +// libraries/physcis/src +// +// Created by Seth Alves 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 "EntitySimulation.h" + +#include "ObjectConstraint.h" + +#include "PhysicsLogging.h" + +ObjectConstraint::ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) : + ObjectDynamic(type, id, ownerEntity) +{ +} + +void ObjectConstraint::invalidate() { + _constraint = nullptr; +} diff --git a/libraries/physics/src/ObjectConstraint.h b/libraries/physics/src/ObjectConstraint.h new file mode 100644 index 0000000000..711daea812 --- /dev/null +++ b/libraries/physics/src/ObjectConstraint.h @@ -0,0 +1,35 @@ +// +// ObjectConstraint.h +// libraries/physcis/src +// +// Created by Seth Alves 2017-4-11 +// 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 +// +// http://bulletphysics.org/Bullet/BulletFull/classbtConstraintInterface.html + +#ifndef hifi_ObjectConstraint_h +#define hifi_ObjectConstraint_h + +#include +#include +#include "ObjectDynamic.h" + +class ObjectConstraint : public ObjectDynamic +{ +public: + ObjectConstraint(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraint() {} + + virtual btTypedConstraint* getConstraint() = 0; + + virtual bool isConstraint() const override { return true; } + virtual void invalidate() override; + +protected: + btTypedConstraint* _constraint { nullptr }; +}; + +#endif // hifi_ObjectConstraint_h diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp new file mode 100644 index 0000000000..6c55d9c5dd --- /dev/null +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -0,0 +1,363 @@ +// +// ObjectConstraintHinge.cpp +// libraries/physics/src +// +// Created by Seth Alves 2017-4-11 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "QVariantGLM.h" + +#include "EntityTree.h" +#include "ObjectConstraintHinge.h" +#include "PhysicsLogging.h" + + +const uint16_t ObjectConstraintHinge::constraintVersion = 1; + + +ObjectConstraintHinge::ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectConstraint(DYNAMIC_TYPE_HINGE, id, ownerEntity), + _pivotInA(glm::vec3(0.0f)), + _axisInA(glm::vec3(0.0f)) +{ + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintHinge::ObjectConstraintHinge"; + #endif +} + +ObjectConstraintHinge::~ObjectConstraintHinge() { + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintHinge::~ObjectConstraintHinge"; + #endif +} + +QList ObjectConstraintHinge::getRigidBodies() { + QList result; + result += getRigidBody(); + QUuid otherEntityID; + withReadLock([&]{ + otherEntityID = _otherEntityID; + }); + if (!otherEntityID.isNull()) { + result += getOtherRigidBody(otherEntityID); + } + return result; +} + +void ObjectConstraintHinge::updateHinge() { + btHingeConstraint* constraint { nullptr }; + float low; + float high; + float softness; + float biasFactor; + float relaxationFactor; + float motorVelocity; + + withReadLock([&]{ + constraint = static_cast(_constraint); + low = _low; + high = _high; + softness = _softness; + biasFactor = _biasFactor; + relaxationFactor = _relaxationFactor; + motorVelocity = _motorVelocity; + }); + + if (!constraint) { + return; + } + + constraint->setLimit(low, high, softness, biasFactor, relaxationFactor); + if (motorVelocity != 0.0f) { + constraint->setMotorTargetVelocity(motorVelocity); + constraint->enableMotor(true); + } else { + constraint->enableMotor(false); + } +} + + +btTypedConstraint* ObjectConstraintHinge::getConstraint() { + btHingeConstraint* constraint { nullptr }; + QUuid otherEntityID; + glm::vec3 pivotInA; + glm::vec3 axisInA; + glm::vec3 pivotInB; + glm::vec3 axisInB; + + withReadLock([&]{ + constraint = static_cast(_constraint); + pivotInA = _pivotInA; + axisInA = _axisInA; + otherEntityID = _otherEntityID; + pivotInB = _pivotInB; + axisInB = _axisInB; + }); + if (constraint) { + return constraint; + } + + btRigidBody* rigidBodyA = getRigidBody(); + if (!rigidBodyA) { + qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyA"; + return nullptr; + } + + if (!otherEntityID.isNull()) { + // This hinge is between two entities... find the other rigid body. + btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); + if (!rigidBodyB) { + return nullptr; + } + constraint = new btHingeConstraint(*rigidBodyA, *rigidBodyB, + glmToBullet(pivotInA), glmToBullet(pivotInB), + glmToBullet(axisInA), glmToBullet(axisInB), + true); // useReferenceFrameA + + } else { + // This hinge is between an entity and the world-frame. + constraint = new btHingeConstraint(*rigidBodyA, + glmToBullet(pivotInA), glmToBullet(axisInA), + true); // useReferenceFrameA + } + + withWriteLock([&]{ + _constraint = constraint; + }); + + // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network + forceBodyNonStatic(); + activateBody(); + + updateHinge(); + + return constraint; +} + + +bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { + glm::vec3 pivotInA; + glm::vec3 axisInA; + QUuid otherEntityID; + glm::vec3 pivotInB; + glm::vec3 axisInB; + float low; + float high; + float softness; + float biasFactor; + float relaxationFactor; + float motorVelocity; + + bool needUpdate = false; + bool somethingChanged = ObjectDynamic::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + pivotInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "pivot", ok, false); + if (!ok) { + pivotInA = _pivotInA; + } + + ok = true; + axisInA = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "axis", ok, false); + if (!ok) { + axisInA = _axisInA; + } + + ok = true; + otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("hinge constraint", + arguments, "otherEntityID", ok, false)); + if (!ok) { + otherEntityID = _otherEntityID; + } + + ok = true; + pivotInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherPivot", ok, false); + if (!ok) { + pivotInB = _pivotInB; + } + + ok = true; + axisInB = EntityDynamicInterface::extractVec3Argument("hinge constraint", arguments, "otherAxis", ok, false); + if (!ok) { + axisInB = _axisInB; + } + + ok = true; + low = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "low", ok, false); + if (!ok) { + low = _low; + } + + ok = true; + high = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "high", ok, false); + if (!ok) { + high = _high; + } + + ok = true; + softness = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "softness", ok, false); + if (!ok) { + softness = _softness; + } + + ok = true; + biasFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, "biasFactor", ok, false); + if (!ok) { + biasFactor = _biasFactor; + } + + ok = true; + relaxationFactor = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, + "relaxationFactor", ok, false); + if (!ok) { + relaxationFactor = _relaxationFactor; + } + + ok = true; + motorVelocity = EntityDynamicInterface::extractFloatArgument("hinge constraint", arguments, + "motorVelocity", ok, false); + if (!ok) { + motorVelocity = _motorVelocity; + } + + if (somethingChanged || + pivotInA != _pivotInA || + axisInA != _axisInA || + otherEntityID != _otherEntityID || + pivotInB != _pivotInB || + axisInB != _axisInB || + low != _low || + high != _high || + softness != _softness || + biasFactor != _biasFactor || + relaxationFactor != _relaxationFactor || + motorVelocity != _motorVelocity) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { + withWriteLock([&] { + _pivotInA = pivotInA; + _axisInA = axisInA; + _otherEntityID = otherEntityID; + _pivotInB = pivotInB; + _axisInB = axisInB; + _low = low; + _high = high; + _softness = softness; + _biasFactor = biasFactor; + _relaxationFactor = relaxationFactor; + _motorVelocity = motorVelocity; + + _active = true; + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); + } + }); + + updateHinge(); + } + + return true; +} + +QVariantMap ObjectConstraintHinge::getArguments() { + QVariantMap arguments = ObjectDynamic::getArguments(); + withReadLock([&] { + if (_constraint) { + arguments["pivot"] = glmToQMap(_pivotInA); + arguments["axis"] = glmToQMap(_axisInA); + arguments["otherEntityID"] = _otherEntityID; + arguments["otherPivot"] = glmToQMap(_pivotInB); + arguments["otherAxis"] = glmToQMap(_axisInB); + arguments["low"] = _low; + arguments["high"] = _high; + arguments["softness"] = _softness; + arguments["biasFactor"] = _biasFactor; + arguments["relaxationFactor"] = _relaxationFactor; + arguments["motorVelocity"] = _motorVelocity; + arguments["angle"] = static_cast(_constraint)->getHingeAngle(); // [-PI,PI] + } + }); + return arguments; +} + +QByteArray ObjectConstraintHinge::serialize() const { + QByteArray serializedConstraintArguments; + QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_HINGE; + dataStream << getID(); + dataStream << ObjectConstraintHinge::constraintVersion; + + withReadLock([&] { + dataStream << _pivotInA; + dataStream << _axisInA; + dataStream << _otherEntityID; + dataStream << _pivotInB; + dataStream << _axisInB; + dataStream << _low; + dataStream << _high; + dataStream << _softness; + dataStream << _biasFactor; + dataStream << _relaxationFactor; + + dataStream << localTimeToServerTime(_expires); + dataStream << _tag; + + dataStream << _motorVelocity; + }); + + return serializedConstraintArguments; +} + +void ObjectConstraintHinge::deserialize(QByteArray serializedArguments) { + QDataStream dataStream(serializedArguments); + + EntityDynamicType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != ObjectConstraintHinge::constraintVersion) { + assert(false); + return; + } + + withWriteLock([&] { + dataStream >> _pivotInA; + dataStream >> _axisInA; + dataStream >> _otherEntityID; + dataStream >> _pivotInB; + dataStream >> _axisInB; + dataStream >> _low; + dataStream >> _high; + dataStream >> _softness; + dataStream >> _biasFactor; + dataStream >> _relaxationFactor; + + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + + dataStream >> _tag; + + dataStream >> _motorVelocity; + + _active = true; + }); +} diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h new file mode 100644 index 0000000000..7d2cac7511 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -0,0 +1,53 @@ +// +// ObjectConstraintHinge.h +// libraries/physics/src +// +// Created by Seth Alves 2017-4-11 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ObjectConstraintHinge_h +#define hifi_ObjectConstraintHinge_h + +#include "ObjectConstraint.h" + +// http://bulletphysics.org/Bullet/BulletFull/classbtHingeConstraint.html + +class ObjectConstraintHinge : public ObjectConstraint { +public: + ObjectConstraintHinge(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraintHinge(); + + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; + + virtual QByteArray serialize() const override; + virtual void deserialize(QByteArray serializedArguments) override; + + virtual QList getRigidBodies() override; + virtual btTypedConstraint* getConstraint() override; + +protected: + static const uint16_t constraintVersion; + + void updateHinge(); + + glm::vec3 _pivotInA; + glm::vec3 _axisInA; + + EntityItemID _otherEntityID; + glm::vec3 _pivotInB; + glm::vec3 _axisInB; + + float _low { -2.0f * PI }; + float _high { 2.0f * PI }; + float _softness { 0.9f }; + float _biasFactor { 0.3f }; + float _relaxationFactor { 1.0f }; + float _motorVelocity { 0.0f }; +}; + +#endif // hifi_ObjectConstraintHinge_h diff --git a/libraries/physics/src/ObjectDynamic.cpp b/libraries/physics/src/ObjectDynamic.cpp new file mode 100644 index 0000000000..3cb9f5b405 --- /dev/null +++ b/libraries/physics/src/ObjectDynamic.cpp @@ -0,0 +1,276 @@ +// +// ObjectDynamic.cpp +// libraries/physcis/src +// +// Created by Seth Alves 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 "EntitySimulation.h" + +#include "ObjectDynamic.h" + +#include "PhysicsLogging.h" + + +ObjectDynamic::ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) : + EntityDynamicInterface(type, id), + _ownerEntity(ownerEntity) { +} + +ObjectDynamic::~ObjectDynamic() { +} + +qint64 ObjectDynamic::getEntityServerClockSkew() const { + auto nodeList = DependencyManager::get(); + + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return 0; + } + + const QUuid& entityServerNodeID = ownerEntity->getSourceUUID(); + auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID); + if (entityServerNode) { + return entityServerNode->getClockSkewUsec(); + } + return 0; +} + +bool ObjectDynamic::updateArguments(QVariantMap arguments) { + bool somethingChanged = false; + + withWriteLock([&]{ + quint64 previousExpires = _expires; + QString previousTag = _tag; + + bool ttlSet = true; + float ttl = EntityDynamicInterface::extractFloatArgument("dynamic", arguments, "ttl", ttlSet, false); + if (ttlSet) { + quint64 now = usecTimestampNow(); + _expires = now + (quint64)(ttl * USECS_PER_SECOND); + } else { + _expires = 0; + } + + bool tagSet = true; + QString tag = EntityDynamicInterface::extractStringArgument("dynamic", arguments, "tag", tagSet, false); + if (tagSet) { + _tag = tag; + } else { + tag = ""; + } + + if (previousExpires != _expires || previousTag != _tag) { + somethingChanged = true; + } + }); + + return somethingChanged; +} + +QVariantMap ObjectDynamic::getArguments() { + QVariantMap arguments; + withReadLock([&]{ + if (_expires == 0) { + arguments["ttl"] = 0.0f; + } else { + quint64 now = usecTimestampNow(); + arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND; + } + arguments["tag"] = _tag; + + EntityItemPointer entity = _ownerEntity.lock(); + if (entity) { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + arguments["::active"] = motionState->isActive(); + arguments["::motion-type"] = motionTypeToString(motionState->getMotionType()); + } else { + arguments["::no-motion-state"] = true; + } + } + arguments["isMine"] = isMine(); + }); + return arguments; +} + +void ObjectDynamic::removeFromSimulation(EntitySimulationPointer simulation) const { + QUuid myID; + withReadLock([&]{ + myID = _id; + }); + simulation->removeDynamic(myID); +} + +EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const { + EntityItemPointer ownerEntity; + withReadLock([&]{ + ownerEntity = _ownerEntity.lock(); + }); + EntityTreeElementPointer element = ownerEntity ? ownerEntity->getElement() : nullptr; + EntityTreePointer tree = element ? element->getTree() : nullptr; + if (!tree) { + return nullptr; + } + return tree->findEntityByID(entityID); +} + + +btRigidBody* ObjectDynamic::getRigidBody() { + ObjectMotionState* motionState = nullptr; + withReadLock([&]{ + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + void* physicsInfo = ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return; + } + motionState = static_cast(physicsInfo); + }); + if (motionState) { + return motionState->getRigidBody(); + } + return nullptr; +} + +glm::vec3 ObjectDynamic::getPosition() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getCenterOfMassPosition()); +} + +glm::quat ObjectDynamic::getRotation() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::quat(0.0f, 0.0f, 0.0f, 1.0f); + } + return bulletToGLM(rigidBody->getOrientation()); +} + +glm::vec3 ObjectDynamic::getLinearVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getLinearVelocity()); +} + +void ObjectDynamic::setLinearVelocity(glm::vec3 linearVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setLinearVelocity(glmToBullet(glm::vec3(0.0f))); + rigidBody->activate(); +} + +glm::vec3 ObjectDynamic::getAngularVelocity() { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return glm::vec3(0.0f); + } + return bulletToGLM(rigidBody->getAngularVelocity()); +} + +void ObjectDynamic::setAngularVelocity(glm::vec3 angularVelocity) { + auto rigidBody = getRigidBody(); + if (!rigidBody) { + return; + } + rigidBody->setAngularVelocity(glmToBullet(angularVelocity)); + rigidBody->activate(); +} + +void ObjectDynamic::activateBody(bool forceActivation) { + auto rigidBody = getRigidBody(); + if (rigidBody) { + rigidBody->activate(forceActivation); + } else { + qCDebug(physics) << "ObjectDynamic::activateBody -- no rigid body" << (void*)rigidBody; + } +} + +void ObjectDynamic::forceBodyNonStatic() { + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + void* physicsInfo = ownerEntity->getPhysicsInfo(); + ObjectMotionState* motionState = static_cast(physicsInfo); + if (motionState && motionState->getMotionType() == MOTION_TYPE_STATIC) { + ownerEntity->flagForMotionStateChange(); + } +} + +bool ObjectDynamic::lifetimeIsOver() { + if (_expires == 0) { + return false; + } + + quint64 now = usecTimestampNow(); + if (now >= _expires) { + return true; + } + return false; +} + +quint64 ObjectDynamic::localTimeToServerTime(quint64 timeValue) const { + // 0 indicates no expiration + if (timeValue == 0) { + return 0; + } + + qint64 serverClockSkew = getEntityServerClockSkew(); + if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) { + return 1; // non-zero but long-expired value to avoid negative roll-over + } + + return timeValue + serverClockSkew; +} + +quint64 ObjectDynamic::serverTimeToLocalTime(quint64 timeValue) const { + // 0 indicates no expiration + if (timeValue == 0) { + return 0; + } + + qint64 serverClockSkew = getEntityServerClockSkew(); + if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) { + return 1; // non-zero but long-expired value to avoid negative roll-over + } + + return timeValue - serverClockSkew; +} + +btRigidBody* ObjectDynamic::getOtherRigidBody(EntityItemID otherEntityID) { + EntityItemPointer otherEntity = getEntityByID(otherEntityID); + if (!otherEntity) { + return nullptr; + } + + void* otherPhysicsInfo = otherEntity->getPhysicsInfo(); + if (!otherPhysicsInfo) { + return nullptr; + } + + ObjectMotionState* otherMotionState = static_cast(otherPhysicsInfo); + if (!otherMotionState) { + return nullptr; + } + + return otherMotionState->getRigidBody(); +} + +QList ObjectDynamic::getRigidBodies() { + QList result; + result += getRigidBody(); + return result; +} diff --git a/libraries/physics/src/ObjectDynamic.h b/libraries/physics/src/ObjectDynamic.h new file mode 100644 index 0000000000..dcd0103a55 --- /dev/null +++ b/libraries/physics/src/ObjectDynamic.h @@ -0,0 +1,76 @@ +// +// ObjectDynamic.h +// libraries/physcis/src +// +// Created by Seth Alves 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 +// +// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html + +#ifndef hifi_ObjectDynamic_h +#define hifi_ObjectDynamic_h + +#include + +#include + +#include + +#include "ObjectMotionState.h" +#include "BulletUtil.h" +#include "EntityDynamicInterface.h" + + +class ObjectDynamic : public EntityDynamicInterface, public ReadWriteLockable { +public: + ObjectDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectDynamic(); + + virtual void removeFromSimulation(EntitySimulationPointer simulation) const override; + virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; } + virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; } + + virtual void invalidate() {}; + + virtual bool updateArguments(QVariantMap arguments) override; + virtual QVariantMap getArguments() override; + + + virtual QByteArray serialize() const override = 0; + virtual void deserialize(QByteArray serializedArguments) override = 0; + + virtual bool lifetimeIsOver() override; + virtual quint64 getExpires() override { return _expires; } + + virtual QList getRigidBodies(); + +protected: + quint64 localTimeToServerTime(quint64 timeValue) const; + quint64 serverTimeToLocalTime(quint64 timeValue) const; + + btRigidBody* getOtherRigidBody(EntityItemID otherEntityID); + EntityItemPointer getEntityByID(EntityItemID entityID) const; + virtual btRigidBody* getRigidBody(); + virtual glm::vec3 getPosition() override; + virtual glm::quat getRotation() override; + virtual glm::vec3 getLinearVelocity() override; + virtual void setLinearVelocity(glm::vec3 linearVelocity) override; + virtual glm::vec3 getAngularVelocity() override; + virtual void setAngularVelocity(glm::vec3 angularVelocity) override; + virtual void activateBody(bool forceActivation = false); + virtual void forceBodyNonStatic(); + + EntityItemWeakPointer _ownerEntity; + QString _tag; + quint64 _expires { 0 }; // in seconds since epoch + +private: + qint64 getEntityServerClockSkew() const; +}; + +typedef std::shared_ptr ObjectDynamicPointer; + +#endif // hifi_ObjectDynamic_h diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 6f5b474810..5081f981d4 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -330,33 +330,41 @@ void PhysicalEntitySimulation::handleCollisionEvents(const CollisionEvents& coll } -void PhysicalEntitySimulation::addAction(EntityActionPointer action) { +void PhysicalEntitySimulation::addDynamic(EntityDynamicPointer dynamic) { if (_physicsEngine) { // FIXME put fine grain locking into _physicsEngine { QMutexLocker lock(&_mutex); - const QUuid& actionID = action->getID(); - if (_physicsEngine->getActionByID(actionID)) { - qCDebug(physics) << "warning -- PhysicalEntitySimulation::addAction -- adding an " - "action that was already in _physicsEngine"; + const QUuid& dynamicID = dynamic->getID(); + if (_physicsEngine->getDynamicByID(dynamicID)) { + qCDebug(physics) << "warning -- PhysicalEntitySimulation::addDynamic -- adding an " + "dynamic that was already in _physicsEngine"; } } - EntitySimulation::addAction(action); + EntitySimulation::addDynamic(dynamic); } } -void PhysicalEntitySimulation::applyActionChanges() { +void PhysicalEntitySimulation::applyDynamicChanges() { + QList dynamicsFailedToAdd; if (_physicsEngine) { // FIXME put fine grain locking into _physicsEngine QMutexLocker lock(&_mutex); - foreach(QUuid actionToRemove, _actionsToRemove) { - _physicsEngine->removeAction(actionToRemove); + foreach(QUuid dynamicToRemove, _dynamicsToRemove) { + _physicsEngine->removeDynamic(dynamicToRemove); } - foreach (EntityActionPointer actionToAdd, _actionsToAdd) { - if (!_actionsToRemove.contains(actionToAdd->getID())) { - _physicsEngine->addAction(actionToAdd); + foreach (EntityDynamicPointer dynamicToAdd, _dynamicsToAdd) { + if (!_dynamicsToRemove.contains(dynamicToAdd->getID())) { + if (!_physicsEngine->addDynamic(dynamicToAdd)) { + dynamicsFailedToAdd += dynamicToAdd; + } } } } - EntitySimulation::applyActionChanges(); + // applyDynamicChanges will clear _dynamicsToRemove and _dynamicsToAdd + EntitySimulation::applyDynamicChanges(); + // put back the ones that couldn't yet be added + foreach (EntityDynamicPointer dynamicFailedToAdd, dynamicsFailedToAdd) { + addDynamic(dynamicFailedToAdd); + } } diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 5f6185add3..e0b15440bb 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -34,8 +34,8 @@ public: void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender); - virtual void addAction(EntityActionPointer action) override; - virtual void applyActionChanges() override; + virtual void addDynamic(EntityDynamicPointer dynamic) override; + virtual void applyDynamicChanges() override; virtual void takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) override; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a8a8e6acfd..ca6889485a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -142,6 +142,26 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { motionState->clearIncomingDirtyFlags(); } +QList PhysicsEngine::removeDynamicsForBody(btRigidBody* body) { + // remove dynamics that are attached to this body + QList removedDynamics; + QMutableSetIterator i(_objectDynamicsByBody[body]); + + while (i.hasNext()) { + QUuid dynamicID = i.next(); + if (dynamicID.isNull()) { + continue; + } + EntityDynamicPointer dynamic = _objectDynamics[dynamicID]; + if (!dynamic) { + continue; + } + removeDynamic(dynamicID); + removedDynamics += dynamic; + } + return removedDynamics; +} + void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { // bump and prune contacts for all objects in the list for (auto object : objects) { @@ -175,6 +195,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { for (auto object : objects) { btRigidBody* body = object->getRigidBody(); if (body) { + removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. @@ -191,6 +212,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) { for (auto object : objects) { btRigidBody* body = object->getRigidBody(); if (body) { + removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. @@ -240,7 +262,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { btRigidBody* body = object->getRigidBody(); if (body) { _dynamicsWorld->removeRigidBody(body); - // add it back addObjectToDynamicsWorld(object); } @@ -548,44 +569,84 @@ void PhysicsEngine::setCharacterController(CharacterController* character) { } } -EntityActionPointer PhysicsEngine::getActionByID(const QUuid& actionID) const { - if (_objectActions.contains(actionID)) { - return _objectActions[actionID]; +EntityDynamicPointer PhysicsEngine::getDynamicByID(const QUuid& dynamicID) const { + if (_objectDynamics.contains(dynamicID)) { + return _objectDynamics[dynamicID]; } return nullptr; } -void PhysicsEngine::addAction(EntityActionPointer action) { - assert(action); - const QUuid& actionID = action->getID(); - if (_objectActions.contains(actionID)) { - if (_objectActions[actionID] == action) { +bool PhysicsEngine::addDynamic(EntityDynamicPointer dynamic) { + assert(dynamic); + + if (!dynamic->isReadyForAdd()) { + return false; + } + + const QUuid& dynamicID = dynamic->getID(); + if (_objectDynamics.contains(dynamicID)) { + if (_objectDynamics[dynamicID] == dynamic) { + return true; + } + removeDynamic(dynamic->getID()); + } + + bool success { false }; + if (dynamic->isAction()) { + ObjectAction* objectAction = static_cast(dynamic.get()); + _dynamicsWorld->addAction(objectAction); + success = true; + } else if (dynamic->isConstraint()) { + ObjectConstraint* objectConstraint = static_cast(dynamic.get()); + btTypedConstraint* constraint = objectConstraint->getConstraint(); + if (constraint) { + _dynamicsWorld->addConstraint(constraint); + success = true; + } // else perhaps not all the rigid bodies are available, yet + } + + if (success) { + _objectDynamics[dynamicID] = dynamic; + foreach(btRigidBody* rigidBody, std::static_pointer_cast(dynamic)->getRigidBodies()) { + _objectDynamicsByBody[rigidBody] += dynamic->getID(); + } + } + return success; +} + +void PhysicsEngine::removeDynamic(const QUuid dynamicID) { + if (_objectDynamics.contains(dynamicID)) { + ObjectDynamicPointer dynamic = std::static_pointer_cast(_objectDynamics[dynamicID]); + if (!dynamic) { return; } - removeAction(action->getID()); - } - - _objectActions[actionID] = action; - - // bullet needs a pointer to the action, but it doesn't use shared pointers. - // is there a way to bump the reference count? - ObjectAction* objectAction = static_cast(action.get()); - _dynamicsWorld->addAction(objectAction); -} - -void PhysicsEngine::removeAction(const QUuid actionID) { - if (_objectActions.contains(actionID)) { - EntityActionPointer action = _objectActions[actionID]; - ObjectAction* objectAction = static_cast(action.get()); - _dynamicsWorld->removeAction(objectAction); - _objectActions.remove(actionID); + QList rigidBodies = dynamic->getRigidBodies(); + if (dynamic->isAction()) { + ObjectAction* objectAction = static_cast(dynamic.get()); + _dynamicsWorld->removeAction(objectAction); + } else { + ObjectConstraint* objectConstraint = static_cast(dynamic.get()); + btTypedConstraint* constraint = objectConstraint->getConstraint(); + if (constraint) { + _dynamicsWorld->removeConstraint(constraint); + } else { + qCDebug(physics) << "PhysicsEngine::removeDynamic of constraint failed"; + } + } + _objectDynamics.remove(dynamicID); + foreach(btRigidBody* rigidBody, rigidBodies) { + _objectDynamicsByBody[rigidBody].remove(dynamic->getID()); + } + dynamic->invalidate(); } } -void PhysicsEngine::forEachAction(std::function actor) { - QHashIterator iter(_objectActions); +void PhysicsEngine::forEachDynamic(std::function actor) { + QMutableHashIterator iter(_objectDynamics); while (iter.hasNext()) { iter.next(); - actor(iter.value()); + if (iter.value()) { + actor(iter.value()); + } } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index b2ebe58f08..9f2f1aff5c 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -24,6 +24,7 @@ #include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" #include "ObjectAction.h" +#include "ObjectConstraint.h" const float HALF_SIMULATION_EXTENT = 512.0f; // meters @@ -84,12 +85,13 @@ public: void dumpNextStats() { _dumpNextStats = true; } - EntityActionPointer getActionByID(const QUuid& actionID) const; - void addAction(EntityActionPointer action); - void removeAction(const QUuid actionID); - void forEachAction(std::function actor); + EntityDynamicPointer getDynamicByID(const QUuid& dynamicID) const; + bool addDynamic(EntityDynamicPointer dynamic); + void removeDynamic(const QUuid dynamicID); + void forEachDynamic(std::function actor); private: + QList removeDynamicsForBody(btRigidBody* body); void addObjectToDynamicsWorld(ObjectMotionState* motionState); void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName); @@ -110,7 +112,8 @@ private: ContactMap _contactMap; CollisionEvents _collisionEvents; - QHash _objectActions; + QHash _objectDynamics; + QHash> _objectDynamicsByBody; std::vector _activeStaticBodies; glm::vec3 _originOffset; @@ -122,6 +125,7 @@ private: bool _dumpNextStats = false; bool _hasOutgoingChanges = false; + }; typedef std::shared_ptr PhysicsEnginePointer; diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 10f477b3af..811799917d 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -529,7 +529,7 @@ Grabber.prototype.moveEvent = function(event) { if (!this.actionID) { if (!entityIsGrabbedByOther(this.entityID)) { - this.actionID = Entities.addAction("spring", this.entityID, actionArgs); + this.actionID = Entities.addAction("far-grab", this.entityID, actionArgs); } } else { Entities.updateAction(this.entityID, this.actionID, actionArgs); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 464101a4e3..f1a2e7bd08 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2526,7 +2526,7 @@ function MyController(hand) { var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); this.linearTimeScale = timeScale; this.actionID = NULL_UUID; - this.actionID = Entities.addAction("spring", this.grabbedThingID, { + this.actionID = Entities.addAction("far-grab", this.grabbedThingID, { targetPosition: this.currentObjectPosition, linearTimeScale: timeScale, targetRotation: this.currentObjectRotation, @@ -2741,7 +2741,7 @@ function MyController(hand) { relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, + kinematic: this.kinematicGrab, kinematicSetVelocity: true, ignoreIK: this.ignoreIK }); @@ -2838,12 +2838,14 @@ function MyController(hand) { this.ignoreIK = true; } else { grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedThingID, DEFAULT_GRABBABLE_DATA); if (FORCE_IGNORE_IK) { this.ignoreIK = true; } else { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedThingID, DEFAULT_GRABBABLE_DATA); this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; } + + this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; } var handRotation; @@ -3207,7 +3209,7 @@ function MyController(hand) { relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC, + kinematic: this.kinematicGrab, kinematicSetVelocity: true, ignoreIK: this.ignoreIK }); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index f407e97a10..a1120ee3e1 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -48,8 +48,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -365,17 +365,17 @@ public: } }; -class TestActionFactory : public EntityActionFactoryInterface { +class TestActionFactory : public EntityDynamicFactoryInterface { public: - virtual EntityActionPointer factory(EntityActionType type, + virtual EntityDynamicPointer factory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity, QVariantMap arguments) override { - return EntityActionPointer(); + return EntityDynamicPointer(); } - virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override { + virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override { return nullptr; } }; @@ -475,7 +475,7 @@ protected: public: //"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126" static void setup() { - DependencyManager::registerInheritance(); + DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::set();