540 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			540 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | /*
 | ||
|  | * Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
 | ||
|  | * | ||
|  | * This software is provided 'as-is', without any express or implied | ||
|  | * warranty.  In no event will the authors be held liable for any damages | ||
|  | * arising from the use of this software. | ||
|  | * Permission is granted to anyone to use this software for any purpose, | ||
|  | * including commercial applications, and to alter it and redistribute it | ||
|  | * freely, subject to the following restrictions: | ||
|  | * 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. | ||
|  | * 2. Altered source versions must be plainly marked as such, and must not be | ||
|  | * misrepresented as being the original software. | ||
|  | * 3. This notice may not be removed or altered from any source distribution. | ||
|  | */ | ||
|  | 
 | ||
|  | #include "Box2D/Collision/b2Distance.h"
 | ||
|  | #include "Box2D/Dynamics/b2Island.h"
 | ||
|  | #include "Box2D/Dynamics/b2Body.h"
 | ||
|  | #include "Box2D/Dynamics/b2Fixture.h"
 | ||
|  | #include "Box2D/Dynamics/b2World.h"
 | ||
|  | #include "Box2D/Dynamics/Contacts/b2Contact.h"
 | ||
|  | #include "Box2D/Dynamics/Contacts/b2ContactSolver.h"
 | ||
|  | #include "Box2D/Dynamics/Joints/b2Joint.h"
 | ||
|  | #include "Box2D/Common/b2StackAllocator.h"
 | ||
|  | #include "Box2D/Common/b2Timer.h"
 | ||
|  | 
 | ||
|  | /*
 | ||
|  | Position Correction Notes | ||
|  | ========================= | ||
|  | I tried the several algorithms for position correction of the 2D revolute joint. | ||
|  | I looked at these systems: | ||
|  | - simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s. | ||
|  | - suspension bridge with 30 1m long planks of length 1m. | ||
|  | - multi-link chain with 30 1m long links. | ||
|  | 
 | ||
|  | Here are the algorithms: | ||
|  | 
 | ||
|  | Baumgarte - A fraction of the position error is added to the velocity error. There is no | ||
|  | separate position solver. | ||
|  | 
 | ||
|  | Pseudo Velocities - After the velocity solver and position integration, | ||
|  | the position error, Jacobian, and effective mass are recomputed. Then | ||
|  | the velocity constraints are solved with pseudo velocities and a fraction | ||
|  | of the position error is added to the pseudo velocity error. The pseudo | ||
|  | velocities are initialized to zero and there is no warm-starting. After | ||
|  | the position solver, the pseudo velocities are added to the positions. | ||
|  | This is also called the First Order World method or the Position LCP method. | ||
|  | 
 | ||
|  | Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the | ||
|  | position error is re-computed for each constraint and the positions are updated | ||
|  | after the constraint is solved. The radius vectors (aka Jacobians) are | ||
|  | re-computed too (otherwise the algorithm has horrible instability). The pseudo | ||
|  | velocity states are not needed because they are effectively zero at the beginning | ||
|  | of each iteration. Since we have the current position error, we allow the | ||
|  | iterations to terminate early if the error becomes smaller than b2_linearSlop. | ||
|  | 
 | ||
|  | Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed | ||
|  | each time a constraint is solved. | ||
|  | 
 | ||
|  | Here are the results: | ||
|  | Baumgarte - this is the cheapest algorithm but it has some stability problems, | ||
|  | especially with the bridge. The chain links separate easily close to the root | ||
|  | and they jitter as they struggle to pull together. This is one of the most common | ||
|  | methods in the field. The big drawback is that the position correction artificially | ||
|  | affects the momentum, thus leading to instabilities and false bounce. I used a | ||
|  | bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller | ||
|  | factor makes joints and contacts more spongy. | ||
|  | 
 | ||
|  | Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is | ||
|  | stable. However, joints still separate with large angular velocities. Drag the | ||
|  | simple pendulum in a circle quickly and the joint will separate. The chain separates | ||
|  | easily and does not recover. I used a bias factor of 0.2. A larger value lead to | ||
|  | the bridge collapsing when a heavy cube drops on it. | ||
|  | 
 | ||
|  | Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo | ||
|  | Velocities, but in other ways it is worse. The bridge and chain are much more | ||
|  | stable, but the simple pendulum goes unstable at high angular velocities. | ||
|  | 
 | ||
|  | Full NGS - stable in all tests. The joints display good stiffness. The bridge | ||
|  | still sags, but this is better than infinite forces. | ||
|  | 
 | ||
|  | Recommendations | ||
|  | Pseudo Velocities are not really worthwhile because the bridge and chain cannot | ||
|  | recover from joint separation. In other cases the benefit over Baumgarte is small. | ||
|  | 
 | ||
|  | Modified NGS is not a robust method for the revolute joint due to the violent | ||
|  | instability seen in the simple pendulum. Perhaps it is viable with other constraint | ||
|  | types, especially scalar constraints where the effective mass is a scalar. | ||
|  | 
 | ||
|  | This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities | ||
|  | and is very fast. I don't think we can escape Baumgarte, especially in highly | ||
|  | demanding cases where high constraint fidelity is not needed. | ||
|  | 
 | ||
|  | Full NGS is robust and easy on the eyes. I recommend this as an option for | ||
|  | higher fidelity simulation and certainly for suspension bridges and long chains. | ||
|  | Full NGS might be a good choice for ragdolls, especially motorized ragdolls where | ||
|  | joint separation can be problematic. The number of NGS iterations can be reduced | ||
|  | for better performance without harming robustness much. | ||
|  | 
 | ||
|  | Each joint in a can be handled differently in the position solver. So I recommend | ||
|  | a system where the user can select the algorithm on a per joint basis. I would | ||
|  | probably default to the slower Full NGS and let the user select the faster | ||
|  | Baumgarte method in performance critical scenarios. | ||
|  | */ | ||
|  | 
 | ||
|  | /*
 | ||
|  | Cache Performance | ||
|  | 
 | ||
|  | The Box2D solvers are dominated by cache misses. Data structures are designed | ||
|  | to increase the number of cache hits. Much of misses are due to random access | ||
|  | to body data. The constraint structures are iterated over linearly, which leads | ||
|  | to few cache misses. | ||
|  | 
 | ||
|  | The bodies are not accessed during iteration. Instead read only data, such as | ||
|  | the mass values are stored with the constraints. The mutable data are the constraint | ||
|  | impulses and the bodies velocities/positions. The impulses are held inside the | ||
|  | constraint structures. The body velocities/positions are held in compact, temporary | ||
|  | arrays to increase the number of cache hits. Linear and angular velocity are | ||
|  | stored in a single array since multiple arrays lead to multiple misses. | ||
|  | */ | ||
|  | 
 | ||
|  | /*
 | ||
|  | 2D Rotation | ||
|  | 
 | ||
|  | R = [cos(theta) -sin(theta)] | ||
|  |     [sin(theta) cos(theta) ] | ||
|  | 
 | ||
|  | thetaDot = omega | ||
|  | 
 | ||
|  | Let q1 = cos(theta), q2 = sin(theta). | ||
|  | R = [q1 -q2] | ||
|  |     [q2  q1] | ||
|  | 
 | ||
|  | q1Dot = -thetaDot * q2 | ||
|  | q2Dot = thetaDot * q1 | ||
|  | 
 | ||
|  | q1_new = q1_old - dt * w * q2 | ||
|  | q2_new = q2_old + dt * w * q1 | ||
|  | then normalize. | ||
|  | 
 | ||
|  | This might be faster than computing sin+cos. | ||
|  | However, we can compute sin+cos of the same angle fast. | ||
|  | */ | ||
|  | 
 | ||
|  | b2Island::b2Island( | ||
|  | 	int32 bodyCapacity, | ||
|  | 	int32 contactCapacity, | ||
|  | 	int32 jointCapacity, | ||
|  | 	b2StackAllocator* allocator, | ||
|  | 	b2ContactListener* listener) | ||
|  | { | ||
|  | 	m_bodyCapacity = bodyCapacity; | ||
|  | 	m_contactCapacity = contactCapacity; | ||
|  | 	m_jointCapacity	 = jointCapacity; | ||
|  | 	m_bodyCount = 0; | ||
|  | 	m_contactCount = 0; | ||
|  | 	m_jointCount = 0; | ||
|  | 
 | ||
|  | 	m_allocator = allocator; | ||
|  | 	m_listener = listener; | ||
|  | 
 | ||
|  | 	m_bodies = (b2Body**)m_allocator->Allocate(bodyCapacity * sizeof(b2Body*)); | ||
|  | 	m_contacts = (b2Contact**)m_allocator->Allocate(contactCapacity	 * sizeof(b2Contact*)); | ||
|  | 	m_joints = (b2Joint**)m_allocator->Allocate(jointCapacity * sizeof(b2Joint*)); | ||
|  | 
 | ||
|  | 	m_velocities = (b2Velocity*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Velocity)); | ||
|  | 	m_positions = (b2Position*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Position)); | ||
|  | } | ||
|  | 
 | ||
|  | b2Island::~b2Island() | ||
|  | { | ||
|  | 	// Warning: the order should reverse the constructor order.
 | ||
|  | 	m_allocator->Free(m_positions); | ||
|  | 	m_allocator->Free(m_velocities); | ||
|  | 	m_allocator->Free(m_joints); | ||
|  | 	m_allocator->Free(m_contacts); | ||
|  | 	m_allocator->Free(m_bodies); | ||
|  | } | ||
|  | 
 | ||
|  | void b2Island::Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep) | ||
|  | { | ||
|  | 	b2Timer timer; | ||
|  | 
 | ||
|  | 	float32 h = step.dt; | ||
|  | 
 | ||
|  | 	// Integrate velocities and apply damping. Initialize the body state.
 | ||
|  | 	for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Body* b = m_bodies[i]; | ||
|  | 
 | ||
|  | 		b2Vec2 c = b->m_sweep.c; | ||
|  | 		float32 a = b->m_sweep.a; | ||
|  | 		b2Vec2 v = b->m_linearVelocity; | ||
|  | 		float32 w = b->m_angularVelocity; | ||
|  | 
 | ||
|  | 		// Store positions for continuous collision.
 | ||
|  | 		b->m_sweep.c0 = b->m_sweep.c; | ||
|  | 		b->m_sweep.a0 = b->m_sweep.a; | ||
|  | 
 | ||
|  | 		if (b->m_type == b2_dynamicBody) | ||
|  | 		{ | ||
|  | 			// Integrate velocities.
 | ||
|  | 			v += h * (b->m_gravityScale * gravity + b->m_invMass * b->m_force); | ||
|  | 			w += h * b->m_invI * b->m_torque; | ||
|  | 
 | ||
|  | 			// Apply damping.
 | ||
|  | 			// ODE: dv/dt + c * v = 0
 | ||
|  | 			// Solution: v(t) = v0 * exp(-c * t)
 | ||
|  | 			// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
 | ||
|  | 			// v2 = exp(-c * dt) * v1
 | ||
|  | 			// Pade approximation:
 | ||
|  | 			// v2 = v1 * 1 / (1 + c * dt)
 | ||
|  | 			v *= 1.0f / (1.0f + h * b->m_linearDamping); | ||
|  | 			w *= 1.0f / (1.0f + h * b->m_angularDamping); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		m_positions[i].c = c; | ||
|  | 		m_positions[i].a = a; | ||
|  | 		m_velocities[i].v = v; | ||
|  | 		m_velocities[i].w = w; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	timer.Reset(); | ||
|  | 
 | ||
|  | 	// Solver data
 | ||
|  | 	b2SolverData solverData; | ||
|  | 	solverData.step = step; | ||
|  | 	solverData.positions = m_positions; | ||
|  | 	solverData.velocities = m_velocities; | ||
|  | 
 | ||
|  | 	// Initialize velocity constraints.
 | ||
|  | 	b2ContactSolverDef contactSolverDef; | ||
|  | 	contactSolverDef.step = step; | ||
|  | 	contactSolverDef.contacts = m_contacts; | ||
|  | 	contactSolverDef.count = m_contactCount; | ||
|  | 	contactSolverDef.positions = m_positions; | ||
|  | 	contactSolverDef.velocities = m_velocities; | ||
|  | 	contactSolverDef.allocator = m_allocator; | ||
|  | 
 | ||
|  | 	b2ContactSolver contactSolver(&contactSolverDef); | ||
|  | 	contactSolver.InitializeVelocityConstraints(); | ||
|  | 
 | ||
|  | 	if (step.warmStarting) | ||
|  | 	{ | ||
|  | 		contactSolver.WarmStart(); | ||
|  | 	} | ||
|  | 	 | ||
|  | 	for (int32 i = 0; i < m_jointCount; ++i) | ||
|  | 	{ | ||
|  | 		m_joints[i]->InitVelocityConstraints(solverData); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	profile->solveInit = timer.GetMilliseconds(); | ||
|  | 
 | ||
|  | 	// Solve velocity constraints
 | ||
|  | 	timer.Reset(); | ||
|  | 	for (int32 i = 0; i < step.velocityIterations; ++i) | ||
|  | 	{ | ||
|  | 		for (int32 j = 0; j < m_jointCount; ++j) | ||
|  | 		{ | ||
|  | 			m_joints[j]->SolveVelocityConstraints(solverData); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		contactSolver.SolveVelocityConstraints(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Store impulses for warm starting
 | ||
|  | 	contactSolver.StoreImpulses(); | ||
|  | 	profile->solveVelocity = timer.GetMilliseconds(); | ||
|  | 
 | ||
|  | 	// Integrate positions
 | ||
|  | 	for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Vec2 c = m_positions[i].c; | ||
|  | 		float32 a = m_positions[i].a; | ||
|  | 		b2Vec2 v = m_velocities[i].v; | ||
|  | 		float32 w = m_velocities[i].w; | ||
|  | 
 | ||
|  | 		// Check for large velocities
 | ||
|  | 		b2Vec2 translation = h * v; | ||
|  | 		if (b2Dot(translation, translation) > b2_maxTranslationSquared) | ||
|  | 		{ | ||
|  | 			float32 ratio = b2_maxTranslation / translation.Length(); | ||
|  | 			v *= ratio; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		float32 rotation = h * w; | ||
|  | 		if (rotation * rotation > b2_maxRotationSquared) | ||
|  | 		{ | ||
|  | 			float32 ratio = b2_maxRotation / b2Abs(rotation); | ||
|  | 			w *= ratio; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Integrate
 | ||
|  | 		c += h * v; | ||
|  | 		a += h * w; | ||
|  | 
 | ||
|  | 		m_positions[i].c = c; | ||
|  | 		m_positions[i].a = a; | ||
|  | 		m_velocities[i].v = v; | ||
|  | 		m_velocities[i].w = w; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Solve position constraints
 | ||
|  | 	timer.Reset(); | ||
|  | 	bool positionSolved = false; | ||
|  | 	for (int32 i = 0; i < step.positionIterations; ++i) | ||
|  | 	{ | ||
|  | 		bool contactsOkay = contactSolver.SolvePositionConstraints(); | ||
|  | 
 | ||
|  | 		bool jointsOkay = true; | ||
|  | 		for (int32 j = 0; j < m_jointCount; ++j) | ||
|  | 		{ | ||
|  | 			bool jointOkay = m_joints[j]->SolvePositionConstraints(solverData); | ||
|  | 			jointsOkay = jointsOkay && jointOkay; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (contactsOkay && jointsOkay) | ||
|  | 		{ | ||
|  | 			// Exit early if the position errors are small.
 | ||
|  | 			positionSolved = true; | ||
|  | 			break; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Copy state buffers back to the bodies
 | ||
|  | 	for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Body* body = m_bodies[i]; | ||
|  | 		body->m_sweep.c = m_positions[i].c; | ||
|  | 		body->m_sweep.a = m_positions[i].a; | ||
|  | 		body->m_linearVelocity = m_velocities[i].v; | ||
|  | 		body->m_angularVelocity = m_velocities[i].w; | ||
|  | 		body->SynchronizeTransform(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	profile->solvePosition = timer.GetMilliseconds(); | ||
|  | 
 | ||
|  | 	Report(contactSolver.m_velocityConstraints); | ||
|  | 
 | ||
|  | 	if (allowSleep) | ||
|  | 	{ | ||
|  | 		float32 minSleepTime = b2_maxFloat; | ||
|  | 
 | ||
|  | 		const float32 linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance; | ||
|  | 		const float32 angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance; | ||
|  | 
 | ||
|  | 		for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 		{ | ||
|  | 			b2Body* b = m_bodies[i]; | ||
|  | 			if (b->GetType() == b2_staticBody) | ||
|  | 			{ | ||
|  | 				continue; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if ((b->m_flags & b2Body::e_autoSleepFlag) == 0 || | ||
|  | 				b->m_angularVelocity * b->m_angularVelocity > angTolSqr || | ||
|  | 				b2Dot(b->m_linearVelocity, b->m_linearVelocity) > linTolSqr) | ||
|  | 			{ | ||
|  | 				b->m_sleepTime = 0.0f; | ||
|  | 				minSleepTime = 0.0f; | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				b->m_sleepTime += h; | ||
|  | 				minSleepTime = b2Min(minSleepTime, b->m_sleepTime); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (minSleepTime >= b2_timeToSleep && positionSolved) | ||
|  | 		{ | ||
|  | 			for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 			{ | ||
|  | 				b2Body* b = m_bodies[i]; | ||
|  | 				b->SetAwake(false); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void b2Island::SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB) | ||
|  | { | ||
|  | 	b2Assert(toiIndexA < m_bodyCount); | ||
|  | 	b2Assert(toiIndexB < m_bodyCount); | ||
|  | 
 | ||
|  | 	// Initialize the body state.
 | ||
|  | 	for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Body* b = m_bodies[i]; | ||
|  | 		m_positions[i].c = b->m_sweep.c; | ||
|  | 		m_positions[i].a = b->m_sweep.a; | ||
|  | 		m_velocities[i].v = b->m_linearVelocity; | ||
|  | 		m_velocities[i].w = b->m_angularVelocity; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	b2ContactSolverDef contactSolverDef; | ||
|  | 	contactSolverDef.contacts = m_contacts; | ||
|  | 	contactSolverDef.count = m_contactCount; | ||
|  | 	contactSolverDef.allocator = m_allocator; | ||
|  | 	contactSolverDef.step = subStep; | ||
|  | 	contactSolverDef.positions = m_positions; | ||
|  | 	contactSolverDef.velocities = m_velocities; | ||
|  | 	b2ContactSolver contactSolver(&contactSolverDef); | ||
|  | 
 | ||
|  | 	// Solve position constraints.
 | ||
|  | 	for (int32 i = 0; i < subStep.positionIterations; ++i) | ||
|  | 	{ | ||
|  | 		bool contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB); | ||
|  | 		if (contactsOkay) | ||
|  | 		{ | ||
|  | 			break; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | 	// Is the new position really safe?
 | ||
|  | 	for (int32 i = 0; i < m_contactCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Contact* c = m_contacts[i]; | ||
|  | 		b2Fixture* fA = c->GetFixtureA(); | ||
|  | 		b2Fixture* fB = c->GetFixtureB(); | ||
|  | 
 | ||
|  | 		b2Body* bA = fA->GetBody(); | ||
|  | 		b2Body* bB = fB->GetBody(); | ||
|  | 
 | ||
|  | 		int32 indexA = c->GetChildIndexA(); | ||
|  | 		int32 indexB = c->GetChildIndexB(); | ||
|  | 
 | ||
|  | 		b2DistanceInput input; | ||
|  | 		input.proxyA.Set(fA->GetShape(), indexA); | ||
|  | 		input.proxyB.Set(fB->GetShape(), indexB); | ||
|  | 		input.transformA = bA->GetTransform(); | ||
|  | 		input.transformB = bB->GetTransform(); | ||
|  | 		input.useRadii = false; | ||
|  | 
 | ||
|  | 		b2DistanceOutput output; | ||
|  | 		b2SimplexCache cache; | ||
|  | 		cache.count = 0; | ||
|  | 		b2Distance(&output, &cache, &input); | ||
|  | 
 | ||
|  | 		if (output.distance == 0 || cache.count == 3) | ||
|  | 		{ | ||
|  | 			cache.count += 0; | ||
|  | 		} | ||
|  | 	} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	// Leap of faith to new safe state.
 | ||
|  | 	m_bodies[toiIndexA]->m_sweep.c0 = m_positions[toiIndexA].c; | ||
|  | 	m_bodies[toiIndexA]->m_sweep.a0 = m_positions[toiIndexA].a; | ||
|  | 	m_bodies[toiIndexB]->m_sweep.c0 = m_positions[toiIndexB].c; | ||
|  | 	m_bodies[toiIndexB]->m_sweep.a0 = m_positions[toiIndexB].a; | ||
|  | 
 | ||
|  | 	// No warm starting is needed for TOI events because warm
 | ||
|  | 	// starting impulses were applied in the discrete solver.
 | ||
|  | 	contactSolver.InitializeVelocityConstraints(); | ||
|  | 
 | ||
|  | 	// Solve velocity constraints.
 | ||
|  | 	for (int32 i = 0; i < subStep.velocityIterations; ++i) | ||
|  | 	{ | ||
|  | 		contactSolver.SolveVelocityConstraints(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Don't store the TOI contact forces for warm starting
 | ||
|  | 	// because they can be quite large.
 | ||
|  | 
 | ||
|  | 	float32 h = subStep.dt; | ||
|  | 
 | ||
|  | 	// Integrate positions
 | ||
|  | 	for (int32 i = 0; i < m_bodyCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Vec2 c = m_positions[i].c; | ||
|  | 		float32 a = m_positions[i].a; | ||
|  | 		b2Vec2 v = m_velocities[i].v; | ||
|  | 		float32 w = m_velocities[i].w; | ||
|  | 
 | ||
|  | 		// Check for large velocities
 | ||
|  | 		b2Vec2 translation = h * v; | ||
|  | 		if (b2Dot(translation, translation) > b2_maxTranslationSquared) | ||
|  | 		{ | ||
|  | 			float32 ratio = b2_maxTranslation / translation.Length(); | ||
|  | 			v *= ratio; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		float32 rotation = h * w; | ||
|  | 		if (rotation * rotation > b2_maxRotationSquared) | ||
|  | 		{ | ||
|  | 			float32 ratio = b2_maxRotation / b2Abs(rotation); | ||
|  | 			w *= ratio; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Integrate
 | ||
|  | 		c += h * v; | ||
|  | 		a += h * w; | ||
|  | 
 | ||
|  | 		m_positions[i].c = c; | ||
|  | 		m_positions[i].a = a; | ||
|  | 		m_velocities[i].v = v; | ||
|  | 		m_velocities[i].w = w; | ||
|  | 
 | ||
|  | 		// Sync bodies
 | ||
|  | 		b2Body* body = m_bodies[i]; | ||
|  | 		body->m_sweep.c = c; | ||
|  | 		body->m_sweep.a = a; | ||
|  | 		body->m_linearVelocity = v; | ||
|  | 		body->m_angularVelocity = w; | ||
|  | 		body->SynchronizeTransform(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	Report(contactSolver.m_velocityConstraints); | ||
|  | } | ||
|  | 
 | ||
|  | void b2Island::Report(const b2ContactVelocityConstraint* constraints) | ||
|  | { | ||
|  | 	if (m_listener == nullptr) | ||
|  | 	{ | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for (int32 i = 0; i < m_contactCount; ++i) | ||
|  | 	{ | ||
|  | 		b2Contact* c = m_contacts[i]; | ||
|  | 
 | ||
|  | 		const b2ContactVelocityConstraint* vc = constraints + i; | ||
|  | 		 | ||
|  | 		b2ContactImpulse impulse; | ||
|  | 		impulse.count = vc->pointCount; | ||
|  | 		for (int32 j = 0; j < vc->pointCount; ++j) | ||
|  | 		{ | ||
|  | 			impulse.normalImpulses[j] = vc->points[j].normalImpulse; | ||
|  | 			impulse.tangentImpulses[j] = vc->points[j].tangentImpulse; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		m_listener->PostSolve(c, &impulse); | ||
|  | 	} | ||
|  | } |