Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Constraints/ConeConstraint.cpp
9912 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Constraints/ConeConstraint.h>
8
#include <Jolt/Physics/Body/Body.h>
9
#include <Jolt/ObjectStream/TypeDeclarations.h>
10
#include <Jolt/Core/StreamIn.h>
11
#include <Jolt/Core/StreamOut.h>
12
#ifdef JPH_DEBUG_RENDERER
13
#include <Jolt/Renderer/DebugRenderer.h>
14
#endif // JPH_DEBUG_RENDERER
15
16
JPH_NAMESPACE_BEGIN
17
18
JPH_IMPLEMENT_SERIALIZABLE_VIRTUAL(ConeConstraintSettings)
19
{
20
JPH_ADD_BASE_CLASS(ConeConstraintSettings, TwoBodyConstraintSettings)
21
22
JPH_ADD_ENUM_ATTRIBUTE(ConeConstraintSettings, mSpace)
23
JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint1)
24
JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis1)
25
JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mPoint2)
26
JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mTwistAxis2)
27
JPH_ADD_ATTRIBUTE(ConeConstraintSettings, mHalfConeAngle)
28
}
29
30
void ConeConstraintSettings::SaveBinaryState(StreamOut &inStream) const
31
{
32
ConstraintSettings::SaveBinaryState(inStream);
33
34
inStream.Write(mSpace);
35
inStream.Write(mPoint1);
36
inStream.Write(mTwistAxis1);
37
inStream.Write(mPoint2);
38
inStream.Write(mTwistAxis2);
39
inStream.Write(mHalfConeAngle);
40
}
41
42
void ConeConstraintSettings::RestoreBinaryState(StreamIn &inStream)
43
{
44
ConstraintSettings::RestoreBinaryState(inStream);
45
46
inStream.Read(mSpace);
47
inStream.Read(mPoint1);
48
inStream.Read(mTwistAxis1);
49
inStream.Read(mPoint2);
50
inStream.Read(mTwistAxis2);
51
inStream.Read(mHalfConeAngle);
52
}
53
54
TwoBodyConstraint *ConeConstraintSettings::Create(Body &inBody1, Body &inBody2) const
55
{
56
return new ConeConstraint(inBody1, inBody2, *this);
57
}
58
59
ConeConstraint::ConeConstraint(Body &inBody1, Body &inBody2, const ConeConstraintSettings &inSettings) :
60
TwoBodyConstraint(inBody1, inBody2, inSettings)
61
{
62
// Store limits
63
SetHalfConeAngle(inSettings.mHalfConeAngle);
64
65
// Initialize rotation axis to perpendicular of twist axis in case the angle between the twist axis is 0 in the first frame
66
mWorldSpaceRotationAxis = inSettings.mTwistAxis1.GetNormalizedPerpendicular();
67
68
if (inSettings.mSpace == EConstraintSpace::WorldSpace)
69
{
70
// If all properties were specified in world space, take them to local space now
71
RMat44 inv_transform1 = inBody1.GetInverseCenterOfMassTransform();
72
mLocalSpacePosition1 = Vec3(inv_transform1 * inSettings.mPoint1);
73
mLocalSpaceTwistAxis1 = inv_transform1.Multiply3x3(inSettings.mTwistAxis1);
74
75
RMat44 inv_transform2 = inBody2.GetInverseCenterOfMassTransform();
76
mLocalSpacePosition2 = Vec3(inv_transform2 * inSettings.mPoint2);
77
mLocalSpaceTwistAxis2 = inv_transform2.Multiply3x3(inSettings.mTwistAxis2);
78
}
79
else
80
{
81
// Properties already in local space
82
mLocalSpacePosition1 = Vec3(inSettings.mPoint1);
83
mLocalSpacePosition2 = Vec3(inSettings.mPoint2);
84
mLocalSpaceTwistAxis1 = inSettings.mTwistAxis1;
85
mLocalSpaceTwistAxis2 = inSettings.mTwistAxis2;
86
87
// If they were in local space, we need to take the initial rotation axis to world space
88
mWorldSpaceRotationAxis = inBody1.GetRotation() * mWorldSpaceRotationAxis;
89
}
90
}
91
92
void ConeConstraint::NotifyShapeChanged(const BodyID &inBodyID, Vec3Arg inDeltaCOM)
93
{
94
if (mBody1->GetID() == inBodyID)
95
mLocalSpacePosition1 -= inDeltaCOM;
96
else if (mBody2->GetID() == inBodyID)
97
mLocalSpacePosition2 -= inDeltaCOM;
98
}
99
100
void ConeConstraint::CalculateRotationConstraintProperties(Mat44Arg inRotation1, Mat44Arg inRotation2)
101
{
102
// Rotation is along the cross product of both twist axis
103
Vec3 twist1 = inRotation1.Multiply3x3(mLocalSpaceTwistAxis1);
104
Vec3 twist2 = inRotation2.Multiply3x3(mLocalSpaceTwistAxis2);
105
106
// Calculate dot product between twist axis, if it's smaller than the cone angle we need to correct
107
mCosTheta = twist1.Dot(twist2);
108
if (mCosTheta < mCosHalfConeAngle)
109
{
110
// Rotation axis is defined by the two twist axis
111
Vec3 rot_axis = twist2.Cross(twist1);
112
113
// If we can't find a rotation axis because the twist is too small, we'll use last frame's rotation axis
114
float len = rot_axis.Length();
115
if (len > 0.0f)
116
mWorldSpaceRotationAxis = rot_axis / len;
117
118
mAngleConstraintPart.CalculateConstraintProperties(*mBody1, *mBody2, mWorldSpaceRotationAxis);
119
}
120
else
121
mAngleConstraintPart.Deactivate();
122
}
123
124
void ConeConstraint::SetupVelocityConstraint(float inDeltaTime)
125
{
126
Mat44 rotation1 = Mat44::sRotation(mBody1->GetRotation());
127
Mat44 rotation2 = Mat44::sRotation(mBody2->GetRotation());
128
mPointConstraintPart.CalculateConstraintProperties(*mBody1, rotation1, mLocalSpacePosition1, *mBody2, rotation2, mLocalSpacePosition2);
129
CalculateRotationConstraintProperties(rotation1, rotation2);
130
}
131
132
void ConeConstraint::ResetWarmStart()
133
{
134
mPointConstraintPart.Deactivate();
135
mAngleConstraintPart.Deactivate();
136
}
137
138
void ConeConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRatio)
139
{
140
// Warm starting: Apply previous frame impulse
141
mPointConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
142
mAngleConstraintPart.WarmStart(*mBody1, *mBody2, inWarmStartImpulseRatio);
143
}
144
145
bool ConeConstraint::SolveVelocityConstraint(float inDeltaTime)
146
{
147
bool pos = mPointConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2);
148
149
bool rot = false;
150
if (mAngleConstraintPart.IsActive())
151
rot = mAngleConstraintPart.SolveVelocityConstraint(*mBody1, *mBody2, mWorldSpaceRotationAxis, 0, FLT_MAX);
152
153
return pos || rot;
154
}
155
156
bool ConeConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumgarte)
157
{
158
mPointConstraintPart.CalculateConstraintProperties(*mBody1, Mat44::sRotation(mBody1->GetRotation()), mLocalSpacePosition1, *mBody2, Mat44::sRotation(mBody2->GetRotation()), mLocalSpacePosition2);
159
bool pos = mPointConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, inBaumgarte);
160
161
bool rot = false;
162
CalculateRotationConstraintProperties(Mat44::sRotation(mBody1->GetRotation()), Mat44::sRotation(mBody2->GetRotation()));
163
if (mAngleConstraintPart.IsActive())
164
rot = mAngleConstraintPart.SolvePositionConstraint(*mBody1, *mBody2, mCosTheta - mCosHalfConeAngle, inBaumgarte);
165
166
return pos || rot;
167
}
168
169
#ifdef JPH_DEBUG_RENDERER
170
void ConeConstraint::DrawConstraint(DebugRenderer *inRenderer) const
171
{
172
RMat44 transform1 = mBody1->GetCenterOfMassTransform();
173
RMat44 transform2 = mBody2->GetCenterOfMassTransform();
174
175
RVec3 p1 = transform1 * mLocalSpacePosition1;
176
RVec3 p2 = transform2 * mLocalSpacePosition2;
177
178
// Draw constraint
179
inRenderer->DrawMarker(p1, Color::sRed, 0.1f);
180
inRenderer->DrawMarker(p2, Color::sGreen, 0.1f);
181
182
// Draw twist axis
183
inRenderer->DrawLine(p1, p1 + mDrawConstraintSize * transform1.Multiply3x3(mLocalSpaceTwistAxis1), Color::sRed);
184
inRenderer->DrawLine(p2, p2 + mDrawConstraintSize * transform2.Multiply3x3(mLocalSpaceTwistAxis2), Color::sGreen);
185
}
186
187
void ConeConstraint::DrawConstraintLimits(DebugRenderer *inRenderer) const
188
{
189
// Get constraint properties in world space
190
RMat44 transform1 = mBody1->GetCenterOfMassTransform();
191
RVec3 position1 = transform1 * mLocalSpacePosition1;
192
Vec3 twist_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1);
193
Vec3 normal_axis1 = transform1.Multiply3x3(mLocalSpaceTwistAxis1.GetNormalizedPerpendicular());
194
195
inRenderer->DrawOpenCone(position1, twist_axis1, normal_axis1, ACos(mCosHalfConeAngle), mDrawConstraintSize * mCosHalfConeAngle, Color::sPurple, DebugRenderer::ECastShadow::Off);
196
}
197
#endif // JPH_DEBUG_RENDERER
198
199
void ConeConstraint::SaveState(StateRecorder &inStream) const
200
{
201
TwoBodyConstraint::SaveState(inStream);
202
203
mPointConstraintPart.SaveState(inStream);
204
mAngleConstraintPart.SaveState(inStream);
205
inStream.Write(mWorldSpaceRotationAxis); // When twist is too small, the rotation is used from last frame so we need to store it
206
}
207
208
void ConeConstraint::RestoreState(StateRecorder &inStream)
209
{
210
TwoBodyConstraint::RestoreState(inStream);
211
212
mPointConstraintPart.RestoreState(inStream);
213
mAngleConstraintPart.RestoreState(inStream);
214
inStream.Read(mWorldSpaceRotationAxis);
215
}
216
217
Ref<ConstraintSettings> ConeConstraint::GetConstraintSettings() const
218
{
219
ConeConstraintSettings *settings = new ConeConstraintSettings;
220
ToConstraintSettings(*settings);
221
settings->mSpace = EConstraintSpace::LocalToBodyCOM;
222
settings->mPoint1 = RVec3(mLocalSpacePosition1);
223
settings->mTwistAxis1 = mLocalSpaceTwistAxis1;
224
settings->mPoint2 = RVec3(mLocalSpacePosition2);
225
settings->mTwistAxis2 = mLocalSpaceTwistAxis2;
226
settings->mHalfConeAngle = ACos(mCosHalfConeAngle);
227
return settings;
228
}
229
230
Mat44 ConeConstraint::GetConstraintToBody1Matrix() const
231
{
232
Vec3 perp = mLocalSpaceTwistAxis1.GetNormalizedPerpendicular();
233
Vec3 perp2 = mLocalSpaceTwistAxis1.Cross(perp);
234
return Mat44(Vec4(mLocalSpaceTwistAxis1, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition1, 1));
235
}
236
237
Mat44 ConeConstraint::GetConstraintToBody2Matrix() const
238
{
239
// Note: Incorrect in rotation around the twist axis (the perpendicular does not match that of body 1),
240
// this should not matter as we're not limiting rotation around the twist axis.
241
Vec3 perp = mLocalSpaceTwistAxis2.GetNormalizedPerpendicular();
242
Vec3 perp2 = mLocalSpaceTwistAxis2.Cross(perp);
243
return Mat44(Vec4(mLocalSpaceTwistAxis2, 0), Vec4(perp, 0), Vec4(perp2, 0), Vec4(mLocalSpacePosition2, 1));
244
}
245
246
JPH_NAMESPACE_END
247
248