From b196dd082b0ef9eab222e48d2619a202a9cb6690 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 07:56:35 -0700 Subject: [PATCH] expose bullet slider constraint to javascript --- interface/src/InterfaceDynamicFactory.cpp | 3 + .../entities/src/EntityDynamicInterface.cpp | 5 + .../entities/src/EntityDynamicInterface.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + .../physics/src/ObjectConstraintHinge.cpp | 9 +- libraries/physics/src/ObjectConstraintHinge.h | 4 +- .../physics/src/ObjectConstraintSlider.cpp | 410 ++++++++++++++++++ .../physics/src/ObjectConstraintSlider.h | 63 +++ 9 files changed, 489 insertions(+), 11 deletions(-) create mode 100644 libraries/physics/src/ObjectConstraintSlider.cpp create mode 100644 libraries/physics/src/ObjectConstraintSlider.h diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index 5951ccef9e..8f8d57994a 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "InterfaceDynamicFactory.h" @@ -38,6 +39,8 @@ EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid return std::make_shared(id, ownerEntity); case DYNAMIC_TYPE_FAR_GRAB: return std::make_shared(id, ownerEntity); + case DYNAMIC_TYPE_SLIDER: + return std::make_shared(id, ownerEntity); } Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type"); diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index bed3185b8f..5538639053 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -117,6 +117,9 @@ EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicT if (normalizedDynamicTypeString == "fargrab") { return DYNAMIC_TYPE_FAR_GRAB; } + if (normalizedDynamicTypeString == "slider") { + return DYNAMIC_TYPE_SLIDER; + } qCDebug(entities) << "Warning -- EntityDynamicInterface::dynamicTypeFromString got unknown dynamic-type name" << dynamicTypeString; @@ -139,6 +142,8 @@ QString EntityDynamicInterface::dynamicTypeToString(EntityDynamicType dynamicTyp return "hinge"; case DYNAMIC_TYPE_FAR_GRAB: return "far-grab"; + case DYNAMIC_TYPE_SLIDER: + return "slider"; } assert(false); return "none"; diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index 93d9ffa43e..cfb82dbdf8 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -31,7 +31,8 @@ enum EntityDynamicType { DYNAMIC_TYPE_HOLD = 3000, DYNAMIC_TYPE_TRAVEL_ORIENTED = 4000, DYNAMIC_TYPE_HINGE = 5000, - DYNAMIC_TYPE_FAR_GRAB = 6000 + DYNAMIC_TYPE_FAR_GRAB = 6000, + DYNAMIC_TYPE_SLIDER = 7000 }; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 3ad4dbf28d..706a523367 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_HINGE_CONSTRAINT; + return VERSION_ENTITIES_SLIDER_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 074876862f..affc7fd0b0 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -207,6 +207,7 @@ 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; +const PacketVersion VERSION_ENTITIES_SLIDER_CONSTRAINT = 70; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 6599f74867..9f591da18b 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -54,7 +54,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { // https://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7020 if (!isMine()) { - // XXX + // TODO // don't activate motor for someone else's action? // maybe don't if this interface isn't the sim owner? return; @@ -77,11 +77,6 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { float t = (float)(now - _startMotorTime) / (float)USECS_PER_SECOND; float motorTarget = _motorVelocity * t; - // // bring motorTarget into the range of [-PI, PI] - // motorTarget += PI; - // motorTarget = fmodf(motorTarget, 2.0f * PI); - // motorTarget -= PI; - if (!_motorEnabled) { constraint->enableMotor(true); _motorEnabled = true; @@ -90,7 +85,7 @@ void ObjectConstraintHinge::prepareForPhysicsSimulation() { constraint->setMotorTarget(motorTarget, dt); } else if (_motorTargetTimeScale > 0.0f) { - // XXX + // TODO -- we probably want a spring-like action here } else if (_motorEnabled) { constraint->enableMotor(false); _motorEnabled = false; diff --git a/libraries/physics/src/ObjectConstraintHinge.h b/libraries/physics/src/ObjectConstraintHinge.h index 3dbd3e8152..cd5f6ac7c2 100644 --- a/libraries/physics/src/ObjectConstraintHinge.h +++ b/libraries/physics/src/ObjectConstraintHinge.h @@ -44,8 +44,8 @@ protected: glm::vec3 _pivotInB; glm::vec3 _axisInB; - float _low { -2.0f * PI }; - float _high { 2.0f * PI }; + float _low { -TWO_PI }; + float _high { TWO_PI }; // https://gamedev.stackexchange.com/questions/71436/what-are-the-parameters-for-bthingeconstraintsetlimit // diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp new file mode 100644 index 0000000000..1e48d20a86 --- /dev/null +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -0,0 +1,410 @@ +// +// ObjectConstraintSlider.cpp +// libraries/physics/src +// +// Created by Seth Alves 2017-4-23 +// 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 "ObjectConstraintSlider.h" +#include "PhysicsLogging.h" + + +const uint16_t ObjectConstraintSlider::constraintVersion = 1; + + +ObjectConstraintSlider::ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity) : + ObjectConstraint(DYNAMIC_TYPE_SLIDER, id, ownerEntity), + _pointInA(glm::vec3(0.0f)), + _axisInA(glm::vec3(0.0f)) +{ + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintSlider::ObjectConstraintSlider"; + #endif +} + +ObjectConstraintSlider::~ObjectConstraintSlider() { + #if WANT_DEBUG + qCDebug(physics) << "ObjectConstraintSlider::~ObjectConstraintSlider"; + #endif +} + +QList ObjectConstraintSlider::getRigidBodies() { + QList result; + result += getRigidBody(); + QUuid otherEntityID; + withReadLock([&]{ + otherEntityID = _otherEntityID; + }); + if (!otherEntityID.isNull()) { + result += getOtherRigidBody(otherEntityID); + } + return result; +} + +void ObjectConstraintSlider::prepareForPhysicsSimulation() { +} + +void ObjectConstraintSlider::updateSlider() { + btSliderConstraint* constraint { nullptr }; + + withReadLock([&]{ + // TODO -- write this + constraint = static_cast(_constraint); + }); + + if (!constraint) { + return; + } + + + + // constraint->setFrames (const btTransform &frameA, const btTransform &frameB); + constraint->setLowerLinLimit(_linearLow); + constraint->setUpperLinLimit(_linearHigh); + constraint->setLowerAngLimit(_angularLow); + constraint->setUpperAngLimit(_angularHigh); + +} + + +btTypedConstraint* ObjectConstraintSlider::getConstraint() { + btSliderConstraint* constraint { nullptr }; + QUuid otherEntityID; + glm::vec3 pointInA; + glm::vec3 axisInA; + glm::vec3 pointInB; + glm::vec3 axisInB; + + withReadLock([&]{ + constraint = static_cast(_constraint); + pointInA = _pointInA; + axisInA = _axisInA; + otherEntityID = _otherEntityID; + pointInB = _pointInB; + axisInB = _axisInB; + }); + if (constraint) { + return constraint; + } + + btRigidBody* rigidBodyA = getRigidBody(); + if (!rigidBodyA) { + qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyA"; + return nullptr; + } + + if (!otherEntityID.isNull()) { + // This slider is between two entities... find the other rigid body. + + glm::quat rotA = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + glm::quat rotB = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInB)); + + btTransform frameInA(glmToBullet(rotA), glmToBullet(pointInA)); + btTransform frameInB(glmToBullet(rotB), glmToBullet(pointInB)); + + btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); + if (!rigidBodyB) { + return nullptr; + } + + constraint = new btSliderConstraint(*rigidBodyA, *rigidBodyB, frameInA, frameInB, true); + } else { + // This slider is between an entity and the world-frame. + + glm::quat rot = glm::rotation(glm::vec3(1.0f, 0.0f, 0.0f), glm::normalize(axisInA)); + + btTransform frameInA(glmToBullet(rot), glmToBullet(pointInA)); + + constraint = new btSliderConstraint(*rigidBodyA, frameInA, true); + } + + withWriteLock([&]{ + _constraint = constraint; + }); + + // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network + forceBodyNonStatic(); + activateBody(); + + // updateSlider(); + + return constraint; +} + + +bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { + glm::vec3 pointInA; + glm::vec3 axisInA; + QUuid otherEntityID; + glm::vec3 pointInB; + glm::vec3 axisInB; + float linearLow; + float linearHigh; + float angularLow; + float angularHigh; + float linearTarget; + float linearTimeScale; + bool linearTargetSet; + float angularTarget; + float angularTimeScale; + bool angularTargetSet; + + + bool needUpdate = false; + bool somethingChanged = ObjectDynamic::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + pointInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "point", ok, false); + if (!ok) { + pointInA = _pointInA; + } + + ok = true; + axisInA = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "axis", ok, false); + if (!ok) { + axisInA = _axisInA; + } + + ok = true; + otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("slider constraint", + arguments, "otherEntityID", ok, false)); + if (!ok) { + otherEntityID = _otherEntityID; + } + + ok = true; + pointInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherPoint", ok, false); + if (!ok) { + pointInB = _pointInB; + } + + ok = true; + axisInB = EntityDynamicInterface::extractVec3Argument("slider constraint", arguments, "otherAxis", ok, false); + if (!ok) { + axisInB = _axisInB; + } + + ok = true; + linearLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearLow", ok, false); + if (!ok) { + linearLow = _linearLow; + } + + ok = true; + linearHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "linearHigh", ok, false); + if (!ok) { + linearHigh = _linearHigh; + } + + ok = true; + angularLow = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularLow", ok, false); + if (!ok) { + angularLow = _angularLow; + } + + ok = true; + angularHigh = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, "angularHigh", ok, false); + if (!ok) { + angularHigh = _angularHigh; + } + + + ok = true; + linearTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "linearTarget", ok, false); + if (!ok) { + linearTarget = _linearTarget; + linearTargetSet = _linearTargetSet; + } else { + linearTargetSet = true; + } + + + ok = true; + linearTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "linearTimeScale", ok, false); + if (!ok) { + linearTimeScale = _linearTimeScale; + } + + ok = true; + angularTarget = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "angularTarget", ok, false); + if (!ok) { + angularTarget = _angularTarget; + angularTargetSet = _angularTargetSet; + } else { + angularTargetSet = true; + } + + + ok = true; + angularTimeScale = EntityDynamicInterface::extractFloatArgument("slider constraint", arguments, + "angularTimeScale", ok, false); + if (!ok) { + angularTimeScale = _angularTimeScale; + } + + + if (somethingChanged || + pointInA != _pointInA || + axisInA != _axisInA || + otherEntityID != _otherEntityID || + pointInB != _pointInB || + axisInB != _axisInB || + linearLow != _linearLow || + linearHigh != _linearHigh || + angularLow != _angularLow || + angularHigh != _angularHigh || + linearTarget != _linearTarget || + linearTimeScale != _linearTimeScale || + linearTargetSet != _linearTargetSet || + angularTarget != _angularTarget || + angularTimeScale != _angularTimeScale || + angularTargetSet != _angularTargetSet) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { + withWriteLock([&] { + _pointInA = pointInA; + _axisInA = axisInA; + _otherEntityID = otherEntityID; + _pointInB = pointInB; + _axisInB = axisInB; + _linearLow = linearLow; + _linearHigh = linearHigh; + _angularLow = angularLow; + _angularHigh = angularHigh; + _linearTarget = linearTarget; + _linearTimeScale = linearTimeScale; + _linearTargetSet = linearTargetSet; + _angularTarget = angularTarget; + _angularTimeScale = angularTimeScale; + _angularTargetSet = angularTargetSet; + + _active = true; + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setDynamicDataDirty(true); + ownerEntity->setDynamicDataNeedsTransmit(true); + } + }); + + updateSlider(); + } + + return true; +} + +QVariantMap ObjectConstraintSlider::getArguments() { + QVariantMap arguments = ObjectDynamic::getArguments(); + withReadLock([&] { + if (_constraint) { + arguments["point"] = glmToQMap(_pointInA); + arguments["axis"] = glmToQMap(_axisInA); + arguments["otherEntityID"] = _otherEntityID; + arguments["otherPoint"] = glmToQMap(_pointInB); + arguments["otherAxis"] = glmToQMap(_axisInB); + arguments["linearLow"] = _linearLow; + arguments["linearHigh"] = _linearHigh; + arguments["angularLow"] = _angularLow; + arguments["angularHigh"] = _angularHigh; + arguments["linearTarget"] = _linearTarget; + arguments["linearTimeScale"] = _linearTimeScale; + arguments["linearTargetSet"] = _linearTargetSet; + arguments["angularTarget"] = _angularTarget; + arguments["angularTimeScale"] = _angularTimeScale; + arguments["angularTargetSet"] = _angularTargetSet; + arguments["linearPosition"] = static_cast(_constraint)->getLinearPos(); + arguments["angularPosition"] = static_cast(_constraint)->getAngularPos(); + } + }); + return arguments; +} + +QByteArray ObjectConstraintSlider::serialize() const { + QByteArray serializedConstraintArguments; + QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); + + dataStream << DYNAMIC_TYPE_SLIDER; + dataStream << getID(); + dataStream << ObjectConstraintSlider::constraintVersion; + + withReadLock([&] { + dataStream << localTimeToServerTime(_expires); + dataStream << _tag; + + dataStream << _pointInA; + dataStream << _axisInA; + dataStream << _otherEntityID; + dataStream << _pointInB; + dataStream << _axisInB; + dataStream << _linearLow; + dataStream << _linearHigh; + dataStream << _angularLow; + dataStream << _angularHigh; + dataStream << _linearTarget; + dataStream << _linearTimeScale; + dataStream << _linearTargetSet; + dataStream << _angularTarget; + dataStream << _angularTimeScale; + dataStream << _angularTargetSet; + }); + + return serializedConstraintArguments; +} + +void ObjectConstraintSlider::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 != ObjectConstraintSlider::constraintVersion) { + assert(false); + return; + } + + withWriteLock([&] { + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; + + dataStream >> _pointInA; + dataStream >> _axisInA; + dataStream >> _otherEntityID; + dataStream >> _pointInB; + dataStream >> _axisInB; + dataStream >> _linearLow; + dataStream >> _linearHigh; + dataStream >> _angularLow; + dataStream >> _angularHigh; + dataStream >> _linearTarget; + dataStream >> _linearTimeScale; + dataStream >> _linearTargetSet; + dataStream >> _angularTarget; + dataStream >> _angularTimeScale; + dataStream >> _angularTargetSet; + + _active = true; + }); +} diff --git a/libraries/physics/src/ObjectConstraintSlider.h b/libraries/physics/src/ObjectConstraintSlider.h new file mode 100644 index 0000000000..d0746b0aba --- /dev/null +++ b/libraries/physics/src/ObjectConstraintSlider.h @@ -0,0 +1,63 @@ +// +// ObjectConstraintSlider.h +// libraries/physics/src +// +// Created by Seth Alves 2017-4-23 +// 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_ObjectConstraintSlider_h +#define hifi_ObjectConstraintSlider_h + +#include "ObjectConstraint.h" + +// http://bulletphysics.org/Bullet/BulletFull/classbtSliderConstraint.html + +class ObjectConstraintSlider : public ObjectConstraint { +public: + ObjectConstraintSlider(const QUuid& id, EntityItemPointer ownerEntity); + virtual ~ObjectConstraintSlider(); + + virtual void prepareForPhysicsSimulation() override; + + 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 updateSlider(); + + glm::vec3 _pointInA; + glm::vec3 _axisInA; + + EntityItemID _otherEntityID; + glm::vec3 _pointInB; + glm::vec3 _axisInB; + + float _linearLow { std::numeric_limits::max() }; + float _linearHigh { std::numeric_limits::min() }; + + float _angularLow { -TWO_PI }; + float _angularHigh { TWO_PI }; + + float _linearTarget { 0.0f }; + float _linearTimeScale { 0.0f }; + bool _linearTargetSet { false }; + + float _angularTarget { 0.0f }; + float _angularTimeScale { 0.0f }; + bool _angularTargetSet { false }; + +}; + +#endif // hifi_ObjectConstraintSlider_h