Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp
10278 views
1
/**************************************************************************/
2
/* godot_hinge_joint_3d.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
/*
32
Adapted to Godot from the Bullet library.
33
*/
34
35
/*
36
Bullet Continuous Collision Detection and Physics Library
37
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
38
39
This software is provided 'as-is', without any express or implied warranty.
40
In no event will the authors be held liable for any damages arising from the use of this software.
41
Permission is granted to anyone to use this software for any purpose,
42
including commercial applications, and to alter it and redistribute it freely,
43
subject to the following restrictions:
44
45
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
46
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
47
3. This notice may not be removed or altered from any source distribution.
48
*/
49
50
#include "godot_hinge_joint_3d.h"
51
52
GodotHingeJoint3D::GodotHingeJoint3D(GodotBody3D *rbA, GodotBody3D *rbB, const Transform3D &frameA, const Transform3D &frameB) :
53
GodotJoint3D(_arr, 2) {
54
A = rbA;
55
B = rbB;
56
57
m_rbAFrame = frameA;
58
m_rbBFrame = frameB;
59
// flip axis
60
m_rbBFrame.basis[0][2] *= real_t(-1.);
61
m_rbBFrame.basis[1][2] *= real_t(-1.);
62
m_rbBFrame.basis[2][2] *= real_t(-1.);
63
64
A->add_constraint(this, 0);
65
B->add_constraint(this, 1);
66
}
67
68
GodotHingeJoint3D::GodotHingeJoint3D(GodotBody3D *rbA, GodotBody3D *rbB, const Vector3 &pivotInA, const Vector3 &pivotInB,
69
const Vector3 &axisInA, const Vector3 &axisInB) :
70
GodotJoint3D(_arr, 2) {
71
A = rbA;
72
B = rbB;
73
74
m_rbAFrame.origin = pivotInA;
75
76
// since no frame is given, assume this to be zero angle and just pick rb transform axis
77
Vector3 rbAxisA1 = rbA->get_transform().basis.get_column(0);
78
79
Vector3 rbAxisA2;
80
real_t projection = axisInA.dot(rbAxisA1);
81
if (projection >= 1.0f - CMP_EPSILON) {
82
rbAxisA1 = -rbA->get_transform().basis.get_column(2);
83
rbAxisA2 = rbA->get_transform().basis.get_column(1);
84
} else if (projection <= -1.0f + CMP_EPSILON) {
85
rbAxisA1 = rbA->get_transform().basis.get_column(2);
86
rbAxisA2 = rbA->get_transform().basis.get_column(1);
87
} else {
88
rbAxisA2 = axisInA.cross(rbAxisA1);
89
rbAxisA1 = rbAxisA2.cross(axisInA);
90
}
91
92
m_rbAFrame.basis = Basis(rbAxisA1.x, rbAxisA2.x, axisInA.x,
93
rbAxisA1.y, rbAxisA2.y, axisInA.y,
94
rbAxisA1.z, rbAxisA2.z, axisInA.z);
95
96
Quaternion rotationArc = Quaternion(axisInA, axisInB);
97
Vector3 rbAxisB1 = rotationArc.xform(rbAxisA1);
98
Vector3 rbAxisB2 = axisInB.cross(rbAxisB1);
99
100
m_rbBFrame.origin = pivotInB;
101
m_rbBFrame.basis = Basis(rbAxisB1.x, rbAxisB2.x, -axisInB.x,
102
rbAxisB1.y, rbAxisB2.y, -axisInB.y,
103
rbAxisB1.z, rbAxisB2.z, -axisInB.z);
104
105
A->add_constraint(this, 0);
106
B->add_constraint(this, 1);
107
}
108
109
bool GodotHingeJoint3D::setup(real_t p_step) {
110
dynamic_A = (A->get_mode() > PhysicsServer3D::BODY_MODE_KINEMATIC);
111
dynamic_B = (B->get_mode() > PhysicsServer3D::BODY_MODE_KINEMATIC);
112
113
if (!dynamic_A && !dynamic_B) {
114
return false;
115
}
116
117
m_appliedImpulse = real_t(0.);
118
119
if (!m_angularOnly) {
120
Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin);
121
Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin);
122
Vector3 relPos = pivotBInW - pivotAInW;
123
124
Vector3 normal[3];
125
if (Math::is_zero_approx(relPos.length_squared())) {
126
normal[0] = Vector3(real_t(1.0), 0, 0);
127
} else {
128
normal[0] = relPos.normalized();
129
}
130
131
plane_space(normal[0], normal[1], normal[2]);
132
133
for (int i = 0; i < 3; i++) {
134
memnew_placement(
135
&m_jac[i],
136
GodotJacobianEntry3D(
137
A->get_principal_inertia_axes().transposed(),
138
B->get_principal_inertia_axes().transposed(),
139
pivotAInW - A->get_transform().origin - A->get_center_of_mass(),
140
pivotBInW - B->get_transform().origin - B->get_center_of_mass(),
141
normal[i],
142
A->get_inv_inertia(),
143
A->get_inv_mass(),
144
B->get_inv_inertia(),
145
B->get_inv_mass()));
146
}
147
}
148
149
//calculate two perpendicular jointAxis, orthogonal to hingeAxis
150
//these two jointAxis require equal angular velocities for both bodies
151
152
//this is unused for now, it's a todo
153
Vector3 jointAxis0local;
154
Vector3 jointAxis1local;
155
156
plane_space(m_rbAFrame.basis.get_column(2), jointAxis0local, jointAxis1local);
157
158
Vector3 jointAxis0 = A->get_transform().basis.xform(jointAxis0local);
159
Vector3 jointAxis1 = A->get_transform().basis.xform(jointAxis1local);
160
Vector3 hingeAxisWorld = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));
161
162
memnew_placement(
163
&m_jacAng[0],
164
GodotJacobianEntry3D(
165
jointAxis0,
166
A->get_principal_inertia_axes().transposed(),
167
B->get_principal_inertia_axes().transposed(),
168
A->get_inv_inertia(),
169
B->get_inv_inertia()));
170
171
memnew_placement(
172
&m_jacAng[1],
173
GodotJacobianEntry3D(
174
jointAxis1,
175
A->get_principal_inertia_axes().transposed(),
176
B->get_principal_inertia_axes().transposed(),
177
A->get_inv_inertia(),
178
B->get_inv_inertia()));
179
180
memnew_placement(
181
&m_jacAng[2],
182
GodotJacobianEntry3D(
183
hingeAxisWorld,
184
A->get_principal_inertia_axes().transposed(),
185
B->get_principal_inertia_axes().transposed(),
186
A->get_inv_inertia(),
187
B->get_inv_inertia()));
188
189
// Compute limit information
190
real_t hingeAngle = get_hinge_angle();
191
192
//set bias, sign, clear accumulator
193
m_correction = real_t(0.);
194
m_limitSign = real_t(0.);
195
m_solveLimit = false;
196
m_accLimitImpulse = real_t(0.);
197
198
if (m_useLimit && m_lowerLimit <= m_upperLimit) {
199
if (hingeAngle <= m_lowerLimit) {
200
m_correction = (m_lowerLimit - hingeAngle);
201
m_limitSign = 1.0f;
202
m_solveLimit = true;
203
} else if (hingeAngle >= m_upperLimit) {
204
m_correction = m_upperLimit - hingeAngle;
205
m_limitSign = -1.0f;
206
m_solveLimit = true;
207
}
208
}
209
210
//Compute K = J*W*J' for hinge axis
211
Vector3 axisA = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));
212
m_kHinge = 1.0f / (A->compute_angular_impulse_denominator(axisA) + B->compute_angular_impulse_denominator(axisA));
213
214
return true;
215
}
216
217
void GodotHingeJoint3D::solve(real_t p_step) {
218
Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin);
219
Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin);
220
221
//real_t tau = real_t(0.3);
222
223
//linear part
224
if (!m_angularOnly) {
225
Vector3 rel_pos1 = pivotAInW - A->get_transform().origin;
226
Vector3 rel_pos2 = pivotBInW - B->get_transform().origin;
227
228
Vector3 vel1 = A->get_velocity_in_local_point(rel_pos1);
229
Vector3 vel2 = B->get_velocity_in_local_point(rel_pos2);
230
Vector3 vel = vel1 - vel2;
231
232
for (int i = 0; i < 3; i++) {
233
const Vector3 &normal = m_jac[i].m_linearJointAxis;
234
real_t jacDiagABInv = real_t(1.) / m_jac[i].getDiagonal();
235
236
real_t rel_vel;
237
rel_vel = normal.dot(vel);
238
//positional error (zeroth order error)
239
real_t depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal
240
real_t impulse = depth * tau / p_step * jacDiagABInv - rel_vel * jacDiagABInv;
241
m_appliedImpulse += impulse;
242
Vector3 impulse_vector = normal * impulse;
243
if (dynamic_A) {
244
A->apply_impulse(impulse_vector, pivotAInW - A->get_transform().origin);
245
}
246
if (dynamic_B) {
247
B->apply_impulse(-impulse_vector, pivotBInW - B->get_transform().origin);
248
}
249
}
250
}
251
252
{
253
///solve angular part
254
255
// get axes in world space
256
Vector3 axisA = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));
257
Vector3 axisB = B->get_transform().basis.xform(m_rbBFrame.basis.get_column(2));
258
259
const Vector3 &angVelA = A->get_angular_velocity();
260
const Vector3 &angVelB = B->get_angular_velocity();
261
262
Vector3 angVelAroundHingeAxisA = axisA * axisA.dot(angVelA);
263
Vector3 angVelAroundHingeAxisB = axisB * axisB.dot(angVelB);
264
265
Vector3 angAorthog = angVelA - angVelAroundHingeAxisA;
266
Vector3 angBorthog = angVelB - angVelAroundHingeAxisB;
267
Vector3 velrelOrthog = angAorthog - angBorthog;
268
{
269
//solve orthogonal angular velocity correction
270
real_t relaxation = real_t(1.);
271
real_t len = velrelOrthog.length();
272
if (len > real_t(0.00001)) {
273
Vector3 normal = velrelOrthog.normalized();
274
real_t denom = A->compute_angular_impulse_denominator(normal) +
275
B->compute_angular_impulse_denominator(normal);
276
// scale for mass and relaxation
277
velrelOrthog *= (real_t(1.) / denom) * m_relaxationFactor;
278
}
279
280
//solve angular positional correction
281
Vector3 angularError = -axisA.cross(axisB) * (real_t(1.) / p_step);
282
real_t len2 = angularError.length();
283
if (len2 > real_t(0.00001)) {
284
Vector3 normal2 = angularError.normalized();
285
real_t denom2 = A->compute_angular_impulse_denominator(normal2) +
286
B->compute_angular_impulse_denominator(normal2);
287
angularError *= (real_t(1.) / denom2) * relaxation;
288
}
289
290
if (dynamic_A) {
291
A->apply_torque_impulse(-velrelOrthog + angularError);
292
}
293
if (dynamic_B) {
294
B->apply_torque_impulse(velrelOrthog - angularError);
295
}
296
297
// solve limit
298
if (m_solveLimit) {
299
real_t amplitude = ((angVelB - angVelA).dot(axisA) * m_relaxationFactor + m_correction * (real_t(1.) / p_step) * m_biasFactor) * m_limitSign;
300
301
real_t impulseMag = amplitude * m_kHinge;
302
303
// Clamp the accumulated impulse
304
real_t temp = m_accLimitImpulse;
305
m_accLimitImpulse = MAX(m_accLimitImpulse + impulseMag, real_t(0));
306
impulseMag = m_accLimitImpulse - temp;
307
308
Vector3 impulse = axisA * impulseMag * m_limitSign;
309
if (dynamic_A) {
310
A->apply_torque_impulse(impulse);
311
}
312
if (dynamic_B) {
313
B->apply_torque_impulse(-impulse);
314
}
315
}
316
}
317
318
//apply motor
319
if (m_enableAngularMotor) {
320
//todo: add limits too
321
Vector3 angularLimit(0, 0, 0);
322
323
Vector3 velrel = angVelAroundHingeAxisA - angVelAroundHingeAxisB;
324
real_t projRelVel = velrel.dot(axisA);
325
326
real_t desiredMotorVel = m_motorTargetVelocity;
327
real_t motor_relvel = desiredMotorVel - projRelVel;
328
329
real_t unclippedMotorImpulse = m_kHinge * motor_relvel;
330
//todo: should clip against accumulated impulse
331
real_t clippedMotorImpulse = unclippedMotorImpulse > m_maxMotorImpulse ? m_maxMotorImpulse : unclippedMotorImpulse;
332
clippedMotorImpulse = clippedMotorImpulse < -m_maxMotorImpulse ? -m_maxMotorImpulse : clippedMotorImpulse;
333
Vector3 motorImp = clippedMotorImpulse * axisA;
334
335
if (dynamic_A) {
336
A->apply_torque_impulse(motorImp + angularLimit);
337
}
338
if (dynamic_B) {
339
B->apply_torque_impulse(-motorImp - angularLimit);
340
}
341
}
342
}
343
}
344
345
/*
346
void HingeJointSW::updateRHS(real_t timeStep)
347
{
348
(void)timeStep;
349
}
350
351
*/
352
353
real_t GodotHingeJoint3D::get_hinge_angle() {
354
const Vector3 refAxis0 = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(0));
355
const Vector3 refAxis1 = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(1));
356
const Vector3 swingAxis = B->get_transform().basis.xform(m_rbBFrame.basis.get_column(1));
357
358
return atan2fast(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1));
359
}
360
361
void GodotHingeJoint3D::set_param(PhysicsServer3D::HingeJointParam p_param, real_t p_value) {
362
switch (p_param) {
363
case PhysicsServer3D::HINGE_JOINT_BIAS:
364
tau = p_value;
365
break;
366
case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER:
367
m_upperLimit = p_value;
368
break;
369
case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER:
370
m_lowerLimit = p_value;
371
break;
372
case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS:
373
m_biasFactor = p_value;
374
break;
375
case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS:
376
m_limitSoftness = p_value;
377
break;
378
case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION:
379
m_relaxationFactor = p_value;
380
break;
381
case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY:
382
m_motorTargetVelocity = p_value;
383
break;
384
case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE:
385
m_maxMotorImpulse = p_value;
386
break;
387
case PhysicsServer3D::HINGE_JOINT_MAX:
388
break; // Can't happen, but silences warning
389
}
390
}
391
392
real_t GodotHingeJoint3D::get_param(PhysicsServer3D::HingeJointParam p_param) const {
393
switch (p_param) {
394
case PhysicsServer3D::HINGE_JOINT_BIAS:
395
return tau;
396
case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER:
397
return m_upperLimit;
398
case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER:
399
return m_lowerLimit;
400
case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS:
401
return m_biasFactor;
402
case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS:
403
return m_limitSoftness;
404
case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION:
405
return m_relaxationFactor;
406
case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY:
407
return m_motorTargetVelocity;
408
case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE:
409
return m_maxMotorImpulse;
410
case PhysicsServer3D::HINGE_JOINT_MAX:
411
break; // Can't happen, but silences warning
412
}
413
414
return 0;
415
}
416
417
void GodotHingeJoint3D::set_flag(PhysicsServer3D::HingeJointFlag p_flag, bool p_value) {
418
switch (p_flag) {
419
case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT:
420
m_useLimit = p_value;
421
break;
422
case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR:
423
m_enableAngularMotor = p_value;
424
break;
425
case PhysicsServer3D::HINGE_JOINT_FLAG_MAX:
426
break; // Can't happen, but silences warning
427
}
428
}
429
430
bool GodotHingeJoint3D::get_flag(PhysicsServer3D::HingeJointFlag p_flag) const {
431
switch (p_flag) {
432
case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT:
433
return m_useLimit;
434
case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR:
435
return m_enableAngularMotor;
436
case PhysicsServer3D::HINGE_JOINT_FLAG_MAX:
437
break; // Can't happen, but silences warning
438
}
439
440
return false;
441
}
442
443