// // ObjectConstraintBallSocket.cpp // libraries/physics/src // // Created by Seth Alves 2017-4-29 // 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 #include "QVariantGLM.h" #include "EntityTree.h" #include "ObjectConstraintBallSocket.h" #include "PhysicsLogging.h" const uint16_t ObjectConstraintBallSocket::constraintVersion = 1; ObjectConstraintBallSocket::ObjectConstraintBallSocket(const QUuid& id, EntityItemPointer ownerEntity) : ObjectConstraint(DYNAMIC_TYPE_BALL_SOCKET, id, ownerEntity), _pivotInA(glm::vec3(0.0f)), _pivotInB(glm::vec3(0.0f)) { #if WANT_DEBUG qCDebug(physics) << "ObjectConstraintBallSocket::ObjectConstraintBallSocket"; #endif } ObjectConstraintBallSocket::~ObjectConstraintBallSocket() { #if WANT_DEBUG qCDebug(physics) << "ObjectConstraintBallSocket::~ObjectConstraintBallSocket"; #endif } QList ObjectConstraintBallSocket::getRigidBodies() { QList result; result += getRigidBody(); QUuid otherEntityID; withReadLock([&]{ otherEntityID = _otherID; }); if (!otherEntityID.isNull()) { result += getOtherRigidBody(otherEntityID); } return result; } void ObjectConstraintBallSocket::prepareForPhysicsSimulation() { } void ObjectConstraintBallSocket::updateBallSocket() { btPoint2PointConstraint* constraint { nullptr }; withReadLock([&]{ constraint = static_cast(_constraint); }); if (!constraint) { return; } constraint->setPivotA(glmToBullet(_pivotInA)); constraint->setPivotB(glmToBullet(_pivotInB)); } btTypedConstraint* ObjectConstraintBallSocket::getConstraint() { btPoint2PointConstraint* constraint { nullptr }; QUuid otherEntityID; glm::vec3 pivotInA; glm::vec3 pivotInB; withReadLock([&]{ constraint = static_cast(_constraint); pivotInA = _pivotInA; otherEntityID = _otherID; pivotInB = _pivotInB; }); if (constraint) { return constraint; } static QString repeatedBallSocketNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex( "ObjectConstraintBallSocket::getConstraint -- no rigidBody.*"); btRigidBody* rigidBodyA = getRigidBody(); if (!rigidBodyA) { qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA"; return nullptr; } if (!otherEntityID.isNull()) { // This constraint is between two entities... find the other rigid body. btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyB"; return nullptr; } constraint = new btPoint2PointConstraint(*rigidBodyA, *rigidBodyB, glmToBullet(pivotInA), glmToBullet(pivotInB)); } else { // This constraint is between an entity and the world-frame. constraint = new btPoint2PointConstraint(*rigidBodyA, glmToBullet(pivotInA)); } withWriteLock([&]{ _constraint = constraint; }); // if we don't wake up rigidBodyA, we may not send the dynamicData property over the network forceBodyNonStatic(); activateBody(); updateBallSocket(); return constraint; } bool ObjectConstraintBallSocket::updateArguments(QVariantMap arguments) { glm::vec3 pivotInA; QUuid otherEntityID; glm::vec3 pivotInB; bool needUpdate = false; bool somethingChanged = ObjectDynamic::updateArguments(arguments); withReadLock([&]{ bool ok = true; pivotInA = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "pivot", ok, false); if (!ok) { pivotInA = _pivotInA; } ok = true; otherEntityID = QUuid(EntityDynamicInterface::extractStringArgument("ball-socket constraint", arguments, "otherEntityID", ok, false)); if (!ok) { otherEntityID = _otherID; } ok = true; pivotInB = EntityDynamicInterface::extractVec3Argument("ball-socket constraint", arguments, "otherPivot", ok, false); if (!ok) { pivotInB = _pivotInB; } if (somethingChanged || pivotInA != _pivotInA || otherEntityID != _otherID || pivotInB != _pivotInB) { // something changed needUpdate = true; } }); if (needUpdate) { withWriteLock([&] { _pivotInA = pivotInA; _otherID = otherEntityID; _pivotInB = pivotInB; _active = true; auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { ownerEntity->setDynamicDataDirty(true); ownerEntity->setDynamicDataNeedsTransmit(true); } }); updateBallSocket(); } return true; } QVariantMap ObjectConstraintBallSocket::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { arguments["pivot"] = glmToQMap(_pivotInA); arguments["otherEntityID"] = _otherID; arguments["otherPivot"] = glmToQMap(_pivotInB); }); return arguments; } QByteArray ObjectConstraintBallSocket::serialize() const { QByteArray serializedConstraintArguments; QDataStream dataStream(&serializedConstraintArguments, QIODevice::WriteOnly); dataStream << DYNAMIC_TYPE_BALL_SOCKET; dataStream << getID(); dataStream << ObjectConstraintBallSocket::constraintVersion; withReadLock([&] { dataStream << localTimeToServerTime(_expires); dataStream << _tag; dataStream << _pivotInA; dataStream << _otherID; dataStream << _pivotInB; }); return serializedConstraintArguments; } void ObjectConstraintBallSocket::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 != ObjectConstraintBallSocket::constraintVersion) { assert(false); return; } withWriteLock([&] { quint64 serverExpires; dataStream >> serverExpires; _expires = serverTimeToLocalTime(serverExpires); dataStream >> _tag; dataStream >> _pivotInA; dataStream >> _otherID; dataStream >> _pivotInB; _active = true; }); }