Skip to content

Commit 1cc88ec

Browse files
committed
- use XPBD to implement an implicit spring
- added distance joint for rigid bodies
1 parent cca5558 commit 1cc88ec

20 files changed

+1139
-57
lines changed

Changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
- use XPBD to implement an implicit spring
2+
- added distance joint for rigid bodies
13
- small optimizations
24
- fixed single precision build
35
- cleanup code

Demos/Common/DemoBase.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ void DemoBase::cleanup()
127127
m_scene.m_targetPositionMotorSliderJointData.clear();
128128
m_scene.m_targetVelocityMotorSliderJointData.clear();
129129
m_scene.m_rigidBodySpringData.clear();
130+
m_scene.m_distanceJointData.clear();
130131
}
131132

132133
void DemoBase::init(int argc, char **argv, const char *demoName)
@@ -384,6 +385,10 @@ void DemoBase::render()
384385
{
385386
renderSpring(*(RigidBodySpring*)constraints[i]);
386387
}
388+
else if (constraints[i]->getTypeId() == DistanceJoint::TYPE_ID)
389+
{
390+
renderDistanceJoint(*(DistanceJoint*)constraints[i]);
391+
}
387392
}
388393

389394

@@ -695,6 +700,13 @@ void DemoBase::renderSpring(RigidBodySpring &s)
695700
MiniGL::drawCylinder(s.m_jointInfo.col(2), s.m_jointInfo.col(3), m_jointColor, 0.05f);
696701
}
697702

703+
void DemoBase::renderDistanceJoint(DistanceJoint &j)
704+
{
705+
MiniGL::drawSphere(j.m_jointInfo.col(2), 0.1f, m_jointColor);
706+
MiniGL::drawSphere(j.m_jointInfo.col(3), 0.1f, m_jointColor);
707+
MiniGL::drawCylinder(j.m_jointInfo.col(2), j.m_jointInfo.col(3), m_jointColor, 0.05f);
708+
}
709+
698710
void DemoBase::mouseMove(int x, int y, void *clientData)
699711
{
700712
DemoBase *base = (DemoBase*)clientData;

Demos/Common/DemoBase.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ namespace PBD
6666
void renderRigidBodyContact(RigidBodyContactConstraint &cc);
6767
void renderParticleRigidBodyContact(ParticleRigidBodyContactConstraint &cc);
6868
void renderSpring(RigidBodySpring &s);
69+
void renderDistanceJoint(DistanceJoint &j);
6970

7071
public:
7172
static int PAUSE;

Demos/RigidBodyDemos/JointDemo.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ void render ()
162162
MiniGL::drawStrokeText(6.6f, -4.0, 1.0, 0.002f, "target position motor", 21, textColor);
163163
MiniGL::drawStrokeText(10.6f, -4.0, 1.0, 0.002f, "target velocity motor", 21, textColor);
164164

165+
MiniGL::drawStrokeText(-0.25, -9.5, 1.0, 0.002f, "spring", 6, textColor);
166+
MiniGL::drawStrokeText(3.3, -9.5, 1.0, 0.002f, "distance joint", 14, textColor);
167+
165168
MiniGL::drawTime( TimeManager::getCurrent ()->getTime ());
166169
}
167170

@@ -239,12 +242,18 @@ void createBodyModel()
239242
loadObj(fileName, vdStatic, meshStatic, Vector3r(0.5, 0.5, 0.5));
240243

241244
// static body
242-
const unsigned int numberOfBodies = 24;
245+
const unsigned int numberOfBodies = 30;
243246
rb.resize(numberOfBodies);
244247
Real startX = 0.0;
245-
Real startY = 1.0;
246-
for (unsigned int i = 0; i < 8; i++)
248+
Real startY = 6.5;
249+
for (unsigned int i = 0; i < 10; i++)
247250
{
251+
if (i % 4 == 0)
252+
{
253+
startY -= 5.5;
254+
startX = 0.0;
255+
}
256+
248257
rb[3*i] = new RigidBody();
249258
rb[3*i]->initBody(0.0,
250259
Vector3r(startX, startY, 1.0),
@@ -269,12 +278,6 @@ void createBodyModel()
269278
vd, mesh);
270279

271280
startX += 4.0;
272-
273-
if (i == 3)
274-
{
275-
startY -= 5.5;
276-
startX = 0.0;
277-
}
278281
}
279282

280283
Real jointY = 0.75;
@@ -302,6 +305,13 @@ void createBodyModel()
302305

303306
model->addTargetVelocityMotorSliderJoint(21, 22, Vector3r(12.0, jointY, 1.0), Vector3r(1.0, 0.0, 0.0));
304307
model->addBallJoint(22, 23, Vector3r(12.25, jointY, 3.0));
308+
309+
jointY -= 5.5;
310+
model->addRigidBodySpring(24, 25, Vector3r(0.25, jointY, 1.0), Vector3r(0.25, jointY, 1.0), 50.0);
311+
model->addBallJoint(25, 26, Vector3r(0.25, jointY, 3.0));
312+
313+
model->addDistanceJoint(27, 28, Vector3r(4.25, jointY, 1.0), Vector3r(4.25, jointY, 2.0));
314+
model->addBallJoint(28, 29, Vector3r(4.25, jointY, 3.0));
305315
}
306316

307317

Demos/SceneLoaderDemo/SceneLoaderDemo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,12 @@ void readScene(const bool readFile)
10751075
const SceneLoader::RigidBodySpringData &jd = data.m_rigidBodySpringData[i];
10761076
model->addRigidBodySpring(id_index[jd.m_bodyID[0]], id_index[jd.m_bodyID[1]], jd.m_position1, jd.m_position2, jd.m_stiffness);
10771077
}
1078+
1079+
for (unsigned int i = 0; i < data.m_distanceJointData.size(); i++)
1080+
{
1081+
const SceneLoader::DistanceJointData &jd = data.m_distanceJointData[i];
1082+
model->addDistanceJoint(id_index[jd.m_bodyID[0]], id_index[jd.m_bodyID[1]], jd.m_position1, jd.m_position2);
1083+
}
10781084
}
10791085

10801086
void TW_CALL setContactTolerance(const void *value, void *clientData)

Demos/StiffRodsDemos/StiffRodsSceneLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,12 @@ void PBD::StiffRodsSceneLoader::readStiffRodsScene(const std::string &fileName,
609609
if (m_json.find("RigidBodySprings") != m_json.end())
610610
readRigidBodySprings(m_json, "RigidBodySprings", sceneData);
611611

612+
//////////////////////////////////////////////////////////////////////////
613+
// read DistanceJoints
614+
//////////////////////////////////////////////////////////////////////////
615+
if (m_json.find("DistanceJoints") != m_json.end())
616+
readDistanceJoints(m_json, "DistanceJoints", sceneData);
617+
612618
//////////////////////////////////////////////////////////////////////////
613619
// read TargetAngleMotorHingeJoint
614620
//////////////////////////////////////////////////////////////////////////

PositionBasedDynamics/PositionBasedRigidBodyDynamics.cpp

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ bool PositionBasedRigidBodyDynamics::solve_BallJoint(
195195
}
196196

197197
// ----------------------------------------------------------------------------------------------
198-
bool PositionBasedRigidBodyDynamics::init_Spring(
198+
bool PositionBasedRigidBodyDynamics::init_DistanceJoint(
199199
const Vector3r &x0,
200200
const Quaternionr &q0,
201201
const Vector3r &x1,
@@ -224,7 +224,7 @@ bool PositionBasedRigidBodyDynamics::init_Spring(
224224
}
225225

226226
// ----------------------------------------------------------------------------------------------
227-
bool PositionBasedRigidBodyDynamics::update_Spring(
227+
bool PositionBasedRigidBodyDynamics::update_DistanceJoint(
228228
const Vector3r &x0,
229229
const Quaternionr &q0,
230230
const Vector3r &x1,
@@ -249,7 +249,7 @@ bool PositionBasedRigidBodyDynamics::update_Spring(
249249

250250

251251
// ----------------------------------------------------------------------------------------------
252-
bool PositionBasedRigidBodyDynamics::solve_Spring(
252+
bool PositionBasedRigidBodyDynamics::solve_DistanceJoint(
253253
const Real invMass0,
254254
const Vector3r &x0,
255255
const Matrix3r &inertiaInverseW0,
@@ -259,8 +259,10 @@ bool PositionBasedRigidBodyDynamics::solve_Spring(
259259
const Matrix3r &inertiaInverseW1,
260260
const Quaternionr &q1,
261261
const Real stiffness,
262-
const Real restLength,
262+
const Real restLength,
263+
const Real dt,
263264
const Eigen::Matrix<Real, 3, 4> &jointInfo,
265+
Real &lambda,
264266
Vector3r &corr_x0, Quaternionr &corr_q0,
265267
Vector3r &corr_x1, Quaternionr &corr_q1)
266268
{
@@ -270,14 +272,38 @@ bool PositionBasedRigidBodyDynamics::solve_Spring(
270272
// 2: connector in body 0 (global)
271273
// 3: connector in body 1 (global)
272274

273-
const Vector3r &connector0 = jointInfo.col(2);
274-
const Vector3r &connector1 = jointInfo.col(3);
275+
// evaluate constraint function
276+
const Vector3r &c0 = jointInfo.col(2);
277+
const Vector3r &c1 = jointInfo.col(3);
278+
const Real length = (c0 - c1).norm();
275279

276-
Vector3r dir = connector1 - connector0;
277-
Real length = dir.norm();
278-
dir = (1.0 / length) * dir;
280+
const Real C = (length - restLength);
279281

280-
const Vector3r pt = stiffness * (length- restLength) * dir;
282+
// compute K = J M^-1 J^T
283+
Matrix3r K1, K2;
284+
computeMatrixK(c0, invMass0, x0, inertiaInverseW0, K1);
285+
computeMatrixK(c1, invMass1, x1, inertiaInverseW1, K2);
286+
Vector3r dir = c0 - c1;
287+
if (length > static_cast<Real>(1e-6))
288+
dir /= length;
289+
290+
Real K = (dir.transpose() * (K1 + K2)).dot(dir);
291+
292+
Real alpha = 0.0;
293+
if (stiffness != 0)
294+
{
295+
alpha = 1.0 / (stiffness * dt * dt);
296+
K += alpha;
297+
}
298+
299+
Real Kinv = 0.0;
300+
if (fabs(K) > static_cast<Real>(1e-6))
301+
Kinv = 1.0 / K;
302+
303+
const Real delta_lambda = -Kinv * (C + alpha * lambda);
304+
lambda += delta_lambda;
305+
306+
const Vector3r pt = dir * delta_lambda;
281307

282308
corr_x0.setZero();
283309
corr_q0.coeffs().setZero();
@@ -286,8 +312,8 @@ bool PositionBasedRigidBodyDynamics::solve_Spring(
286312

287313
if (invMass0 != 0.0)
288314
{
289-
const Vector3r r0 = connector0 - x0;
290-
corr_x0 = invMass0*pt;
315+
const Vector3r r0 = c0 - x0;
316+
corr_x0 = invMass0 * pt;
291317

292318
const Vector3r ot = (inertiaInverseW0 * (r0.cross(pt)));
293319
const Quaternionr otQ(0.0, ot[0], ot[1], ot[2]);
@@ -296,8 +322,8 @@ bool PositionBasedRigidBodyDynamics::solve_Spring(
296322

297323
if (invMass1 != 0.0)
298324
{
299-
const Vector3r r1 = connector1 - x1;
300-
corr_x1 = -invMass1*pt;
325+
const Vector3r r1 = c1 - x1;
326+
corr_x1 = -invMass1 * pt;
301327

302328
const Vector3r ot = (inertiaInverseW1 * (r1.cross(-pt)));
303329
const Quaternionr otQ(0.0, ot[0], ot[1], ot[2]);

PositionBasedDynamics/PositionBasedRigidBodyDynamics.h

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,24 +1191,24 @@ namespace PBD
11911191
Vector3r &corr_v1, Vector3r &corr_omega1);
11921192

11931193

1194-
/** Initialize ball joint and return info which is required by the solver step.
1194+
/** Initialize distance joint and return info which is required by the solver step.
11951195
*
11961196
* @param x0 center of mass of first body
11971197
* @param q0 rotation of first body
11981198
* @param x1 center of mass of second body
11991199
* @param q1 rotation of second body
1200-
* @param jointPosition position of ball joint
1200+
* @param jointPosition position of distance joint
12011201
* @param jointInfo Stores the local and global positions of the connector points.
12021202
* The first two columns store the local connectors in body 0 and 1, respectively, while
12031203
* the last two columns contain the global connector positions which have to be
1204-
* updated in each simulation step by calling update_BallJoint().\n
1204+
* updated in each simulation step by calling update_DistanceJoint().\n
12051205
* The joint info contains the following columns:\n
12061206
* 0: connector in body 0 (local)\n
12071207
* 1: connector in body 1 (local)\n
12081208
* 2: connector in body 0 (global)\n
12091209
* 3: connector in body 1 (global)
12101210
*/
1211-
static bool init_Spring(
1211+
static bool init_DistanceJoint(
12121212
const Vector3r &x0, // center of mass of body 0
12131213
const Quaternionr &q0, // rotation of body 0
12141214
const Vector3r &x1, // center of mass of body 1
@@ -1218,35 +1218,33 @@ namespace PBD
12181218
Eigen::Matrix<Real, 3, 4> &jointInfo
12191219
);
12201220

1221-
/** Update ball joint info which is required by the solver step.
1222-
* The ball joint info must be generated in the initialization process of the model
1223-
* by calling the function init_BallJoint().
1221+
/** Update distance joint info which is required by the solver step.
1222+
* The distance joint info must be generated in the initialization process of the model
1223+
* by calling the function init_DistanceJoint().
12241224
* This method should be called once per simulation step before executing the solver.\n\n
12251225
*
12261226
* @param x0 center of mass of first body
12271227
* @param q0 rotation of first body
12281228
* @param x1 center of mass of second body
12291229
* @param q1 rotation of second body
1230-
* @param ballJointInfo ball joint information which should be updated
1230+
* @param jointInfo joint information which should be updated
12311231
*/
1232-
static bool update_Spring(
1232+
static bool update_DistanceJoint(
12331233
const Vector3r &x0, // center of mass of body 0
12341234
const Quaternionr &q0, // rotation of body 0
12351235
const Vector3r &x1, // center of mass of body 1
12361236
const Quaternionr &q1, // rotation of body 1
12371237
Eigen::Matrix<Real, 3, 4> &jointInfo
12381238
);
12391239

1240-
/** Perform a solver step for a ball joint which links two rigid bodies.
1241-
* A ball joint removes three translational degrees of freedom between the bodies.
1242-
* The ball joint info must be generated in the initialization process of the model
1243-
* by calling the function init_BallJoint() and updated each time the bodies
1244-
* change their state by update_BallJoint().\n\n
1240+
/** Perform a solver step for a distance joint which links two rigid bodies.
1241+
* A distance joint removes one translational degrees of freedom between the bodies.
1242+
* When setting a stiffness value which is not zero, we get an implicit spring.
1243+
* The distance joint info must be generated in the initialization process of the model
1244+
* by calling the function init_DistanceJoint() and updated each time the bodies
1245+
* change their state by update_DistanceJoint().\n\n
12451246
* More information can be found in: \cite Deul2014
12461247
*
1247-
* \image html balljoint.jpg "ball joint"
1248-
* \image latex balljoint.jpg "ball joint" width=0.5\textwidth
1249-
*
12501248
* @param invMass0 inverse mass of first body
12511249
* @param x0 center of mass of first body
12521250
* @param inertiaInverseW0 inverse inertia tensor in world coordinates of first body
@@ -1255,15 +1253,19 @@ namespace PBD
12551253
* @param x1 center of mass of second body
12561254
* @param inertiaInverseW1 inverse inertia tensor in world coordinates of second body
12571255
* @param q1 rotation of second body
1258-
* @param ballJointInfo Ball joint information which is required by the solver. This
1259-
* information must be generated in the beginning by calling init_BallJoint()
1260-
* and updated each time the bodies change their state by update_BallJoint().
1256+
* @param stiffness Stiffness of the constraint. 0 means it is totally stiff and we get a distance joint otherwise we get an implicit spring.
1257+
* @param restLength Rest length of the joint.
1258+
* @param dt Time step size (required for XPBD when simulating a spring)
1259+
* @param jointInfo Joint information which is required by the solver. This
1260+
* information must be generated in the beginning by calling init_DistanceJoint()
1261+
* and updated each time the bodies change their state by update_DistanceJoint().
1262+
* @param lambda Lagrange multiplier (required for XPBD). Must be 0 in the first iteration.
12611263
* @param corr_x0 position correction of center of mass of first body
12621264
* @param corr_q0 rotation correction of first body
12631265
* @param corr_x1 position correction of center of mass of second body
12641266
* @param corr_q1 rotation correction of second body
12651267
*/
1266-
static bool solve_Spring(
1268+
static bool solve_DistanceJoint(
12671269
const Real invMass0, // inverse mass is zero if body is static
12681270
const Vector3r &x0, // center of mass of body 0
12691271
const Matrix3r &inertiaInverseW0, // inverse inertia tensor (world space) of body 0
@@ -1274,7 +1276,9 @@ namespace PBD
12741276
const Quaternionr &q1, // rotation of body 1
12751277
const Real stiffness,
12761278
const Real restLength,
1279+
const Real dt,
12771280
const Eigen::Matrix<Real,3,4> &jointInfo, // precomputed joint info
1281+
Real &lambda,
12781282
Vector3r &corr_x0, Quaternionr &corr_q0,
12791283
Vector3r &corr_x1, Quaternionr &corr_q1);
12801284
};

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ The PositionBasedDynamics library allows the position-based handling of many typ
77
Furthermore we use our own library:
88
- [Discregrid](https://github.com/InteractiveComputerGraphics/Discregrid/) to generate cubic signed distance fields for the collision detection
99

10-
The library was tested on Windows 10, Ubuntu 16.10 and Mac OS X 10.10.5.
1110

1211
**Author**: [Jan Bender](http://www.interactive-graphics.de), **License**: MIT
1312

@@ -23,7 +22,7 @@ The library was tested on Windows 10, Ubuntu 16.10 and Mac OS X 10.10.5.
2322

2423
This project is based on [CMake](https://cmake.org/). Simply generate project, Makefiles, etc. using [CMake](https://cmake.org/) and compile the project with the compiler of your choice. The code was tested with the following configurations:
2524
- Windows 10 64-bit, CMake 3.9.5, Visual Studio 2017
26-
- Ubuntu 16.10 64-bit, CMake 3.5.2, GCC 6.2.0.
25+
- Debian 9 64-bit, CMake 3.12.3, GCC 6.3.0.
2726

2827
Note: Please use a 64-bit target on a 64-bit operating system. 32-bit builds on a 64-bit OS are not supported.
2928

@@ -89,6 +88,8 @@ http://www.interactive-graphics.de/PositionBasedDynamics/doc/html
8988
- target position motor slider joint
9089
- target velocity motor slider joint
9190
- ball joint between rigid body and particle
91+
- distance joint
92+
- implicit spring
9293
- Generic constraints
9394

9495
## Videos

0 commit comments

Comments
 (0)