Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ConeConstraint.cpp
9912 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Constraints/ConeConstraint.h>7#include <Jolt/Physics/Body/Body.h>8#include <Jolt/ObjectStream/TypeDeclarations.h>9#include <Jolt/Core/StreamIn.h>10#include <Jolt/Core/StreamOut.h>11#ifdef JPH_DEBUG_RENDERER12#include <Jolt/Renderer/DebugRenderer.h>13#endif // JPH_DEBUG_RENDERER1415JPH_NAMESPACE_BEGIN1617JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConeConstraintSettings)18{19JPH_ADD_BASE_CLASS(ConeConstraintSettings, TwoBodyConstraintSettings)2021JPH_ADD_ENUM_ATTRIBUTE(ConeConstraintSettings, mSpace)22JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint1)23JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis1)24JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint2)25JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis2)26JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mHalfConeAngle)27}2829void ConeConstraintSettings::SaveBinaryState(StreamOut &inStream) const30{31ConstraintSettings::SaveBinaryState(inStream);3233inStream.Write(mSpace);34inStream.Write(mPoint1);35inStream.Write(mTwistAxis1);36inStream.Write(mPoint2);37inStream.Write(mTwistAxis2);38inStream.Write(mHalfConeAngle);39}4041void ConeConstraintSettings::RestoreBinaryState(StreamIn &inStream)42{43ConstraintSettings::RestoreBinaryState(inStream);4445inStream.Read(mSpace);46inStream.Read(mPoint1);47inStream.Read(mTwistAxis1);48inStream.Read(mPoint2);49inStream.Read(mTwistAxis2);50inStream.Read(mHalfConeAngle);51}5253TwoBodyConstraint *ConeConstraintSettings::Create(Body &inBody1, Body &inBody2) const54{55return new ConeConstraint(inBody1, inBody2, *this);56}5758ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings) :59TwoBodyConstraint(inBody1, inBody2, inSettings)60{61// Store limits62SetHalfConeAngle(inSettings.mHalfConeAngle);6364// Initialize rotation axis to perpendicular of twist axis in case the angle between the twist axis is 0 in the first frame65mWorldSpaceRotationAxis = inSettings.mTwistAxis1.GetNormalizedPerpendicular();6667if (inSettings.mSpace == EConstraintSpace::WorldSpace)68{69// If all properties were specified in world space, take them to local space now70RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform();71mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1);72mLocalSpaceTwistAxis1 = inv_transform1.Multiply3x3(inSettings.mTwistAxis1);7374RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform();75mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2);76mLocalSpaceTwistAxis2 = inv_transform2.Multiply3x3(inSettings.mTwistAxis2);77}78else79{80// Properties already in local space81mLocalSpacePosition1 = Vec3(inSettings.mPoint1);82mLocalSpacePosition2 = Vec3(inSettings.mPoint2);83mLocalSpaceTwistAxis1 = inSettings.mTwistAxis1;84mLocalSpaceTwistAxis2 = inSettings.mTwistAxis2;8586// If they were in local space, we need to take the initial rotation axis to world space87mWorldSpaceRotationAxis = inBody1.GetRotation() * mWorldSpaceRotationAxis;88}89}9091void ConeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)92{93if (mBody1->GetID() == inBodyID)94mLocalSpacePosition1 -= inDeltaCOM;95else if (mBody2->GetID() == inBodyID)96mLocalSpacePosition2 -= inDeltaCOM;97}9899void ConeConstraint::CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2)100{101// Rotation is along the cross product of both twist axis102Vec3 twist1 = inRotation1.Multiply3x3(mLocalSpaceTwistAxis1);103Vec3 twist2 = inRotation2.Multiply3x3(mLocalSpaceTwistAxis2);104105// Calculate dot product between twist axis, if it's smaller than the cone angle we need to correct106mCosTheta = twist1.Dot(twist2);107if (mCosTheta < mCosHalfConeAngle)108{109// Rotation axis is defined by the two twist axis110Vec3 rot_axis = twist2.Cross(twist1);111112// If we can't find a rotation axis because the twist is too small, we'll use last frame's rotation axis113float len = rot_axis.Length();114if (len > 0.0f)115mWorldSpaceRotationAxis = rot_axis / len;116117mAngleConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceRotationAxis);118}119else120mAngleConstraintPart.Deactivate();121}122123void ConeConstraint::SetupVelocityConstraint(float inDeltaTime)124{125Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());126Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());127mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2);128CalculateRotationConstraintProperties(rotation1, rotation2);129}130131void ConeConstraint::ResetWarmStart()132{133mPointConstraintPart.Deactivate();134mAngleConstraintPart.Deactivate();135}136137void ConeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)138{139// Warm starting: Apply previous frame impulse140mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);141mAngleConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);142}143144bool ConeConstraint::SolveVelocityConstraint(float inDeltaTime)145{146bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);147148bool rot = false;149if (mAngleConstraintPart.IsActive())150rot = mAngleConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceRotationAxis, 0, FLT_MAX);151152return pos || rot;153}154155bool ConeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)156{157mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);158bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);159160bool rot = false;161CalculateRotationConstraintProperties(Mat44::sRotation(mBody1->GetRotation()), Mat44::sRotation(mBody2->GetRotation()));162if (mAngleConstraintPart.IsActive())163rot = mAngleConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mCosTheta - mCosHalfConeAngle, inBaumgarte);164165return pos || rot;166}167168#ifdef JPH_DEBUG_RENDERER169void ConeConstraint::DrawConstraint(DebugRenderer *inRenderer) const170{171RMat44 transform1 = mBody1->GetCenterOfMassTransform();172RMat44 transform2 = mBody2->GetCenterOfMassTransform();173174RVec3 p1 = transform1 * mLocalSpacePosition1;175RVec3 p2 = transform2 * mLocalSpacePosition2;176177// Draw constraint178inRenderer->DrawMarker(p1, Color::sRed, 0.1f);179inRenderer->DrawMarker(p2, Color::sGreen, 0.1f);180181// Draw twist axis182inRenderer->DrawLine(p1, p1 + mDrawConstraintSize * transform1.Multiply3x3(mLocalSpaceTwistAxis1), Color::sRed);183inRenderer->DrawLine(p2, p2 + mDrawConstraintSize * transform2.Multiply3x3(mLocalSpaceTwistAxis2), Color::sGreen);184}185186void ConeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const187{188// Get constraint properties in world space189RMat44 transform1 = mBody1->GetCenterOfMassTransform();190RVec3 position1 = transform1 * mLocalSpacePosition1;191Vec3 twist_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1);192Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1.GetNormalizedPerpendicular());193194inRenderer->DrawOpenCone(position1, twist_axis1, normal_axis1, ACos(mCosHalfConeAngle), mDrawConstraintSize * mCosHalfConeAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);195}196#endif // JPH_DEBUG_RENDERER197198void ConeConstraint::SaveState(StateRecorder &inStream) const199{200TwoBodyConstraint::SaveState(inStream);201202mPointConstraintPart.SaveState(inStream);203mAngleConstraintPart.SaveState(inStream);204inStream.Write(mWorldSpaceRotationAxis); // When twist is too small, the rotation is used from last frame so we need to store it205}206207void ConeConstraint::RestoreState(StateRecorder &inStream)208{209TwoBodyConstraint::RestoreState(inStream);210211mPointConstraintPart.RestoreState(inStream);212mAngleConstraintPart.RestoreState(inStream);213inStream.Read(mWorldSpaceRotationAxis);214}215216Ref<ConstraintSettings> ConeConstraint::GetConstraintSettings() const217{218ConeConstraintSettings *settings = new ConeConstraintSettings;219ToConstraintSettings(*settings);220settings->mSpace = EConstraintSpace::LocalToBodyCOM;221settings->mPoint1 = RVec3(mLocalSpacePosition1);222settings->mTwistAxis1 = mLocalSpaceTwistAxis1;223settings->mPoint2 = RVec3(mLocalSpacePosition2);224settings->mTwistAxis2 = mLocalSpaceTwistAxis2;225settings->mHalfConeAngle = ACos(mCosHalfConeAngle);226return settings;227}228229Mat44 ConeConstraint::GetConstraintToBody1Matrix() const230{231Vec3 perp = mLocalSpaceTwistAxis1.GetNormalizedPerpendicular();232Vec3 perp2 = mLocalSpaceTwistAxis1.Cross(perp);233return Mat44(Vec4(mLocalSpaceTwistAxis1, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition1, 1));234}235236Mat44 ConeConstraint::GetConstraintToBody2Matrix() const237{238// Note: Incorrect in rotation around the twist axis (the perpendicular does not match that of body 1),239// this should not matter as we're not limiting rotation around the twist axis.240Vec3 perp = mLocalSpaceTwistAxis2.GetNormalizedPerpendicular();241Vec3 perp2 = mLocalSpaceTwistAxis2.Cross(perp);242return Mat44(Vec4(mLocalSpaceTwistAxis2, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition2, 1));243}244245JPH_NAMESPACE_END246247248