1 /*
2 * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty.  In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 * Permission is granted to anyone to use this software for any purpose,
8 * including commercial applications, and to alter it and redistribute it
9 * freely, subject to the following restrictions:
10 * 1. The origin of this software must not be misrepresented; you must not
11 * claim that you wrote the original software. If you use this software
12 * in a product, an acknowledgment in the product documentation would be
13 * appreciated but is not required.
14 * 2. Altered source versions must be plainly marked as such, and must not be
15 * misrepresented as being the original software.
16 * 3. This notice may not be removed or altered from any source distribution.
17 */
18 
19 #include <Box2D/Collision/b2Distance.h>
20 #include <Box2D/Dynamics/b2Island.h>
21 #include <Box2D/Dynamics/b2Body.h>
22 #include <Box2D/Dynamics/b2Fixture.h>
23 #include <Box2D/Dynamics/b2World.h>
24 #include <Box2D/Dynamics/Contacts/b2Contact.h>
25 #include <Box2D/Dynamics/Contacts/b2ContactSolver.h>
26 #include <Box2D/Dynamics/Joints/b2Joint.h>
27 #include <Box2D/Common/b2StackAllocator.h>
28 #include <Box2D/Common/b2Timer.h>
29 
30 /*
31 Position Correction Notes
32 =========================
33 I tried the several algorithms for position correction of the 2D revolute joint.
34 I looked at these systems:
35 - simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s.
36 - suspension bridge with 30 1m long planks of length 1m.
37 - multi-link chain with 30 1m long links.
38 
39 Here are the algorithms:
40 
41 Baumgarte - A fraction of the position error is added to the velocity error. There is no
42 separate position solver.
43 
44 Pseudo Velocities - After the velocity solver and position integration,
45 the position error, Jacobian, and effective mass are recomputed. Then
46 the velocity constraints are solved with pseudo velocities and a fraction
47 of the position error is added to the pseudo velocity error. The pseudo
48 velocities are initialized to zero and there is no warm-starting. After
49 the position solver, the pseudo velocities are added to the positions.
50 This is also called the First Order World method or the Position LCP method.
51 
52 Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the
53 position error is re-computed for each constraint and the positions are updated
54 after the constraint is solved. The radius vectors (aka Jacobians) are
55 re-computed too (otherwise the algorithm has horrible instability). The pseudo
56 velocity states are not needed because they are effectively zero at the beginning
57 of each iteration. Since we have the current position error, we allow the
58 iterations to terminate early if the error becomes smaller than b2_linearSlop.
59 
60 Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed
61 each time a constraint is solved.
62 
63 Here are the results:
64 Baumgarte - this is the cheapest algorithm but it has some stability problems,
65 especially with the bridge. The chain links separate easily close to the root
66 and they jitter as they struggle to pull together. This is one of the most common
67 methods in the field. The big drawback is that the position correction artificially
68 affects the momentum, thus leading to instabilities and false bounce. I used a
69 bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller
70 factor makes joints and contacts more spongy.
71 
72 Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is
73 stable. However, joints still separate with large angular velocities. Drag the
74 simple pendulum in a circle quickly and the joint will separate. The chain separates
75 easily and does not recover. I used a bias factor of 0.2. A larger value lead to
76 the bridge collapsing when a heavy cube drops on it.
77 
78 Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo
79 Velocities, but in other ways it is worse. The bridge and chain are much more
80 stable, but the simple pendulum goes unstable at high angular velocities.
81 
82 Full NGS - stable in all tests. The joints display good stiffness. The bridge
83 still sags, but this is better than infinite forces.
84 
85 Recommendations
86 Pseudo Velocities are not really worthwhile because the bridge and chain cannot
87 recover from joint separation. In other cases the benefit over Baumgarte is small.
88 
89 Modified NGS is not a robust method for the revolute joint due to the violent
90 instability seen in the simple pendulum. Perhaps it is viable with other constraint
91 types, especially scalar constraints where the effective mass is a scalar.
92 
93 This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities
94 and is very fast. I don't think we can escape Baumgarte, especially in highly
95 demanding cases where high constraint fidelity is not needed.
96 
97 Full NGS is robust and easy on the eyes. I recommend this as an option for
98 higher fidelity simulation and certainly for suspension bridges and long chains.
99 Full NGS might be a good choice for ragdolls, especially motorized ragdolls where
100 joint separation can be problematic. The number of NGS iterations can be reduced
101 for better performance without harming robustness much.
102 
103 Each joint in a can be handled differently in the position solver. So I recommend
104 a system where the user can select the algorithm on a per joint basis. I would
105 probably default to the slower Full NGS and let the user select the faster
106 Baumgarte method in performance critical scenarios.
107 */
108 
109 /*
110 Cache Performance
111 
112 The Box2D solvers are dominated by cache misses. Data structures are designed
113 to increase the number of cache hits. Much of misses are due to random access
114 to body data. The constraint structures are iterated over linearly, which leads
115 to few cache misses.
116 
117 The bodies are not accessed during iteration. Instead read only data, such as
118 the mass values are stored with the constraints. The mutable data are the constraint
119 impulses and the bodies velocities/positions. The impulses are held inside the
120 constraint structures. The body velocities/positions are held in compact, temporary
121 arrays to increase the number of cache hits. Linear and angular velocity are
122 stored in a single array since multiple arrays lead to multiple misses.
123 */
124 
125 /*
126 2D Rotation
127 
128 R = [cos(theta) -sin(theta)]
129     [sin(theta) cos(theta) ]
130 
131 thetaDot = omega
132 
133 Let q1 = cos(theta), q2 = sin(theta).
134 R = [q1 -q2]
135     [q2  q1]
136 
137 q1Dot = -thetaDot * q2
138 q2Dot = thetaDot * q1
139 
140 q1_new = q1_old - dt * w * q2
141 q2_new = q2_old + dt * w * q1
142 then normalize.
143 
144 This might be faster than computing sin+cos.
145 However, we can compute sin+cos of the same angle fast.
146 */
147 
b2Island(int32 bodyCapacity,int32 contactCapacity,int32 jointCapacity,b2StackAllocator * allocator,b2ContactListener * listener)148 b2Island::b2Island(
149 	int32 bodyCapacity,
150 	int32 contactCapacity,
151 	int32 jointCapacity,
152 	b2StackAllocator* allocator,
153 	b2ContactListener* listener)
154 {
155 	m_bodyCapacity = bodyCapacity;
156 	m_contactCapacity = contactCapacity;
157 	m_jointCapacity	 = jointCapacity;
158 	m_bodyCount = 0;
159 	m_contactCount = 0;
160 	m_jointCount = 0;
161 
162 	m_allocator = allocator;
163 	m_listener = listener;
164 
165 	m_bodies = (b2Body**)m_allocator->Allocate(bodyCapacity * sizeof(b2Body*));
166 	m_contacts = (b2Contact**)m_allocator->Allocate(contactCapacity	 * sizeof(b2Contact*));
167 	m_joints = (b2Joint**)m_allocator->Allocate(jointCapacity * sizeof(b2Joint*));
168 
169 	m_velocities = (b2Velocity*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Velocity));
170 	m_positions = (b2Position*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Position));
171 }
172 
~b2Island()173 b2Island::~b2Island()
174 {
175 	// Warning: the order should reverse the constructor order.
176 	m_allocator->Free(m_positions);
177 	m_allocator->Free(m_velocities);
178 	m_allocator->Free(m_joints);
179 	m_allocator->Free(m_contacts);
180 	m_allocator->Free(m_bodies);
181 }
182 
Solve(b2Profile * profile,const b2TimeStep & step,const b2Vec2 & gravity,bool allowSleep)183 void b2Island::Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep)
184 {
185 	b2Timer timer;
186 
187 	float32 h = step.dt;
188 
189 	// Integrate velocities and apply damping. Initialize the body state.
190 	for (int32 i = 0; i < m_bodyCount; ++i)
191 	{
192 		b2Body* b = m_bodies[i];
193 
194 		b2Vec2 c = b->m_sweep.c;
195 		float32 a = b->m_sweep.a;
196 		b2Vec2 v = b->m_linearVelocity;
197 		float32 w = b->m_angularVelocity;
198 
199 		// Store positions for continuous collision.
200 		b->m_sweep.c0 = b->m_sweep.c;
201 		b->m_sweep.a0 = b->m_sweep.a;
202 
203 		if (b->m_type == b2_dynamicBody)
204 		{
205 			// Integrate velocities.
206 			v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force);
207 			w += h * b->m_invI * b->m_torque;
208 
209 			// Apply damping.
210 			// ODE: dv/dt + c * v = 0
211 			// Solution: v(t) = v0 * exp(-c * t)
212 			// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
213 			// v2 = exp(-c * dt) * v1
214 			// Taylor expansion:
215 			// v2 = (1.0f - c * dt) * v1
216 			v *= b2Clamp(1.0f - h * b->m_linearDamping, 0.0f, 1.0f);
217 			w *= b2Clamp(1.0f - h * b->m_angularDamping, 0.0f, 1.0f);
218 		}
219 
220 		m_positions[i].c = c;
221 		m_positions[i].a = a;
222 		m_velocities[i].v = v;
223 		m_velocities[i].w = w;
224 	}
225 
226 	timer.Reset();
227 
228 	// Solver data
229 	b2SolverData solverData;
230 	solverData.step = step;
231 	solverData.positions = m_positions;
232 	solverData.velocities = m_velocities;
233 
234 	// Initialize velocity constraints.
235 	b2ContactSolverDef contactSolverDef;
236 	contactSolverDef.step = step;
237 	contactSolverDef.contacts = m_contacts;
238 	contactSolverDef.count = m_contactCount;
239 	contactSolverDef.positions = m_positions;
240 	contactSolverDef.velocities = m_velocities;
241 	contactSolverDef.allocator = m_allocator;
242 
243 	b2ContactSolver contactSolver(&contactSolverDef);
244 	contactSolver.InitializeVelocityConstraints();
245 
246 	if (step.warmStarting)
247 	{
248 		contactSolver.WarmStart();
249 	}
250 
251 	for (int32 i = 0; i < m_jointCount; ++i)
252 	{
253 		m_joints[i]->InitVelocityConstraints(solverData);
254 	}
255 
256 	profile->solveInit = timer.GetMilliseconds();
257 
258 	// Solve velocity constraints
259 	timer.Reset();
260 	for (int32 i = 0; i < step.velocityIterations; ++i)
261 	{
262 		for (int32 j = 0; j < m_jointCount; ++j)
263 		{
264 			m_joints[j]->SolveVelocityConstraints(solverData);
265 		}
266 
267 		contactSolver.SolveVelocityConstraints();
268 	}
269 
270 	// Store impulses for warm starting
271 	contactSolver.StoreImpulses();
272 	profile->solveVelocity = timer.GetMilliseconds();
273 
274 	// Integrate positions
275 	for (int32 i = 0; i < m_bodyCount; ++i)
276 	{
277 		b2Vec2 c = m_positions[i].c;
278 		float32 a = m_positions[i].a;
279 		b2Vec2 v = m_velocities[i].v;
280 		float32 w = m_velocities[i].w;
281 
282 		// Check for large velocities
283 		b2Vec2 translation = h * v;
284 		if (b2Dot(translation, translation) > b2_maxTranslationSquared)
285 		{
286 			float32 ratio = b2_maxTranslation / translation.Length();
287 			v *= ratio;
288 		}
289 
290 		float32 rotation = h * w;
291 		if (rotation * rotation > b2_maxRotationSquared)
292 		{
293 			float32 ratio = b2_maxRotation / b2Abs(rotation);
294 			w *= ratio;
295 		}
296 
297 		// Integrate
298 		c += h * v;
299 		a += h * w;
300 
301 		m_positions[i].c = c;
302 		m_positions[i].a = a;
303 		m_velocities[i].v = v;
304 		m_velocities[i].w = w;
305 	}
306 
307 	// Solve position constraints
308 	timer.Reset();
309 	bool positionSolved = false;
310 	for (int32 i = 0; i < step.positionIterations; ++i)
311 	{
312 		bool contactsOkay = contactSolver.SolvePositionConstraints();
313 
314 		bool jointsOkay = true;
315 		for (int32 i = 0; i < m_jointCount; ++i)
316 		{
317 			bool jointOkay = m_joints[i]->SolvePositionConstraints(solverData);
318 			jointsOkay = jointsOkay && jointOkay;
319 		}
320 
321 		if (contactsOkay && jointsOkay)
322 		{
323 			// Exit early if the position errors are small.
324 			positionSolved = true;
325 			break;
326 		}
327 	}
328 
329 	// Copy state buffers back to the bodies
330 	for (int32 i = 0; i < m_bodyCount; ++i)
331 	{
332 		b2Body* body = m_bodies[i];
333 		body->m_sweep.c = m_positions[i].c;
334 		body->m_sweep.a = m_positions[i].a;
335 		body->m_linearVelocity = m_velocities[i].v;
336 		body->m_angularVelocity = m_velocities[i].w;
337 		body->SynchronizeTransform();
338 	}
339 
340 	profile->solvePosition = timer.GetMilliseconds();
341 
342 	Report(contactSolver.m_velocityConstraints);
343 
344 	if (allowSleep)
345 	{
346 		float32 minSleepTime = b2_maxFloat;
347 
348 		const float32 linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance;
349 		const float32 angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance;
350 
351 		for (int32 i = 0; i < m_bodyCount; ++i)
352 		{
353 			b2Body* b = m_bodies[i];
354 			if (b->GetType() == b2_staticBody)
355 			{
356 				continue;
357 			}
358 
359 			if ((b->m_flags & b2Body::e_autoSleepFlag) == 0 ||
360 				b->m_angularVelocity * b->m_angularVelocity > angTolSqr ||
361 				b2Dot(b->m_linearVelocity, b->m_linearVelocity) > linTolSqr)
362 			{
363 				b->m_sleepTime = 0.0f;
364 				minSleepTime = 0.0f;
365 			}
366 			else
367 			{
368 				b->m_sleepTime += h;
369 				minSleepTime = b2Min(minSleepTime, b->m_sleepTime);
370 			}
371 		}
372 
373 		if (minSleepTime >= b2_timeToSleep && positionSolved)
374 		{
375 			for (int32 i = 0; i < m_bodyCount; ++i)
376 			{
377 				b2Body* b = m_bodies[i];
378 				b->SetAwake(false);
379 			}
380 		}
381 	}
382 }
383 
SolveTOI(const b2TimeStep & subStep,int32 toiIndexA,int32 toiIndexB)384 void b2Island::SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB)
385 {
386 	b2Assert(toiIndexA < m_bodyCount);
387 	b2Assert(toiIndexB < m_bodyCount);
388 
389 	// Initialize the body state.
390 	for (int32 i = 0; i < m_bodyCount; ++i)
391 	{
392 		b2Body* b = m_bodies[i];
393 		m_positions[i].c = b->m_sweep.c;
394 		m_positions[i].a = b->m_sweep.a;
395 		m_velocities[i].v = b->m_linearVelocity;
396 		m_velocities[i].w = b->m_angularVelocity;
397 	}
398 
399 	b2ContactSolverDef contactSolverDef;
400 	contactSolverDef.contacts = m_contacts;
401 	contactSolverDef.count = m_contactCount;
402 	contactSolverDef.allocator = m_allocator;
403 	contactSolverDef.step = subStep;
404 	contactSolverDef.positions = m_positions;
405 	contactSolverDef.velocities = m_velocities;
406 	b2ContactSolver contactSolver(&contactSolverDef);
407 
408 	// Solve position constraints.
409 	for (int32 i = 0; i < subStep.positionIterations; ++i)
410 	{
411 		bool contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB);
412 		if (contactsOkay)
413 		{
414 			break;
415 		}
416 	}
417 
418 #if 0
419 	// Is the new position really safe?
420 	for (int32 i = 0; i < m_contactCount; ++i)
421 	{
422 		b2Contact* c = m_contacts[i];
423 		b2Fixture* fA = c->GetFixtureA();
424 		b2Fixture* fB = c->GetFixtureB();
425 
426 		b2Body* bA = fA->GetBody();
427 		b2Body* bB = fB->GetBody();
428 
429 		int32 indexA = c->GetChildIndexA();
430 		int32 indexB = c->GetChildIndexB();
431 
432 		b2DistanceInput input;
433 		input.proxyA.Set(fA->GetShape(), indexA);
434 		input.proxyB.Set(fB->GetShape(), indexB);
435 		input.transformA = bA->GetTransform();
436 		input.transformB = bB->GetTransform();
437 		input.useRadii = false;
438 
439 		b2DistanceOutput output;
440 		b2SimplexCache cache;
441 		cache.count = 0;
442 		b2Distance(&output, &cache, &input);
443 
444 		if (output.distance == 0 || cache.count == 3)
445 		{
446 			cache.count += 0;
447 		}
448 	}
449 #endif
450 
451 	// Leap of faith to new safe state.
452 	m_bodies[toiIndexA]->m_sweep.c0 = m_positions[toiIndexA].c;
453 	m_bodies[toiIndexA]->m_sweep.a0 = m_positions[toiIndexA].a;
454 	m_bodies[toiIndexB]->m_sweep.c0 = m_positions[toiIndexB].c;
455 	m_bodies[toiIndexB]->m_sweep.a0 = m_positions[toiIndexB].a;
456 
457 	// No warm starting is needed for TOI events because warm
458 	// starting impulses were applied in the discrete solver.
459 	contactSolver.InitializeVelocityConstraints();
460 
461 	// Solve velocity constraints.
462 	for (int32 i = 0; i < subStep.velocityIterations; ++i)
463 	{
464 		contactSolver.SolveVelocityConstraints();
465 	}
466 
467 	// Don't store the TOI contact forces for warm starting
468 	// because they can be quite large.
469 
470 	float32 h = subStep.dt;
471 
472 	// Integrate positions
473 	for (int32 i = 0; i < m_bodyCount; ++i)
474 	{
475 		b2Vec2 c = m_positions[i].c;
476 		float32 a = m_positions[i].a;
477 		b2Vec2 v = m_velocities[i].v;
478 		float32 w = m_velocities[i].w;
479 
480 		// Check for large velocities
481 		b2Vec2 translation = h * v;
482 		if (b2Dot(translation, translation) > b2_maxTranslationSquared)
483 		{
484 			float32 ratio = b2_maxTranslation / translation.Length();
485 			v *= ratio;
486 		}
487 
488 		float32 rotation = h * w;
489 		if (rotation * rotation > b2_maxRotationSquared)
490 		{
491 			float32 ratio = b2_maxRotation / b2Abs(rotation);
492 			w *= ratio;
493 		}
494 
495 		// Integrate
496 		c += h * v;
497 		a += h * w;
498 
499 		m_positions[i].c = c;
500 		m_positions[i].a = a;
501 		m_velocities[i].v = v;
502 		m_velocities[i].w = w;
503 
504 		// Sync bodies
505 		b2Body* body = m_bodies[i];
506 		body->m_sweep.c = c;
507 		body->m_sweep.a = a;
508 		body->m_linearVelocity = v;
509 		body->m_angularVelocity = w;
510 		body->SynchronizeTransform();
511 	}
512 
513 	Report(contactSolver.m_velocityConstraints);
514 }
515 
Report(const b2ContactVelocityConstraint * constraints)516 void b2Island::Report(const b2ContactVelocityConstraint* constraints)
517 {
518 	if (m_listener == NULL)
519 	{
520 		return;
521 	}
522 
523 	for (int32 i = 0; i < m_contactCount; ++i)
524 	{
525 		b2Contact* c = m_contacts[i];
526 
527 		const b2ContactVelocityConstraint* vc = constraints + i;
528 
529 		b2ContactImpulse impulse;
530 		impulse.count = vc->pointCount;
531 		for (int32 j = 0; j < vc->pointCount; ++j)
532 		{
533 			impulse.normalImpulses[j] = vc->points[j].normalImpulse;
534 			impulse.tangentImpulses[j] = vc->points[j].tangentImpulse;
535 		}
536 
537 		m_listener->PostSolve(c, &impulse);
538 	}
539 }
540