Contact.cpp
Go to the documentation of this file.
1 /*
2  * Original work Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
3  * Modified work Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/PlayRho
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * arising from the use of this software.
8  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 
31 
32 namespace playrho {
33 namespace d2 {
34 
35 namespace {
36 
37 inline Manifold::Conf GetManifoldConf(const playrho::StepConf& conf)
38 {
39  auto manifoldConf = Manifold::Conf{};
40  manifoldConf.linearSlop = conf.linearSlop;
41  manifoldConf.tolerance = conf.tolerance;
42  manifoldConf.targetDepth = conf.targetDepth;
43  manifoldConf.maxCirclesRatio = conf.maxCirclesRatio;
44  return manifoldConf;
45 }
46 
47 inline DistanceConf GetDistanceConf(const playrho::StepConf& conf)
48 {
49  DistanceConf distanceConf;
50  distanceConf.maxIterations = conf.maxDistanceIters;
51  return distanceConf;
52 }
53 } // namespace
54 
56 {
57  return UpdateConf{GetDistanceConf(conf), GetManifoldConf(conf)};
58 }
59 
61  m_fixtureA{fA}, m_fixtureB{fB},
62  m_indexA{iA}, m_indexB{iB},
63  m_friction{MixFriction(fA->GetFriction(), fB->GetFriction())},
64  m_restitution{MixRestitution(fA->GetRestitution(), fB->GetRestitution())}
65 {
66  assert(fA != fB);
67  assert(fA->GetBody() != fB->GetBody());
68 }
69 
70 void Contact::Update(const UpdateConf& conf, ContactListener* listener)
71 {
72  const auto oldManifold = m_manifold;
73 
74  // Note: do not assume the fixture AABBs are overlapping or are valid.
75  const auto oldTouching = (m_flags & e_touchingFlag) != 0;
76  auto newTouching = false;
77 
78  const auto fixtureA = GetFixtureA();
79  const auto indexA = GetChildIndexA();
80  const auto fixtureB = GetFixtureB();
81  const auto indexB = GetChildIndexB();
82  const auto shapeA = fixtureA->GetShape();
83  const auto xfA = fixtureA->GetBody()->GetTransformation();
84  const auto shapeB = fixtureB->GetShape();
85  const auto xfB = fixtureB->GetBody()->GetTransformation();
86  const auto childA = GetChild(shapeA, indexA);
87  const auto childB = GetChild(shapeB, indexB);
88 
89  // NOTE: Ideally, the touching state returned by the TestOverlap function
90  // agrees 100% of the time with that returned from the CollideShapes function.
91  // This is not always the case however especially as the separation or overlap
92  // approaches zero.
93 #define OVERLAP_TOLERANCE (SquareMeter / Real(20))
94 
95  const auto sensor = fixtureA->IsSensor() || fixtureB->IsSensor();
96  if (sensor)
97  {
98  const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
99  newTouching = (overlapping >= 0_m2);
100 
101 #ifdef OVERLAP_TOLERANCE
102 #ifndef NDEBUG
103  const auto tolerance = OVERLAP_TOLERANCE;
104  const auto manifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
105  assert(newTouching == (manifold.GetPointCount() > 0) ||
106  abs(overlapping) < tolerance);
107 #endif
108 #endif
109 
110  // Sensors don't generate manifolds.
111  m_manifold = Manifold{};
112  }
113  else
114  {
115  auto newManifold = CollideShapes(childA, xfA, childB, xfB, conf.manifold);
116 
117  const auto old_point_count = oldManifold.GetPointCount();
118  const auto new_point_count = newManifold.GetPointCount();
119 
120  newTouching = new_point_count > 0;
121 
122 #ifdef OVERLAP_TOLERANCE
123 #ifndef NDEBUG
124  const auto tolerance = OVERLAP_TOLERANCE;
125  const auto overlapping = TestOverlap(childA, xfA, childB, xfB, conf.distance);
126  assert(newTouching == (overlapping >= 0_m2) ||
127  abs(overlapping) < tolerance);
128 #endif
129 #endif
130  // Match old contact ids to new contact ids and copy the stored impulses to warm
131  // start the solver. Note: missing any opportunities to warm start the solver
132  // results in squishier stacking and less stable simulations.
133  bool found[2] = {false, new_point_count < 2};
134  for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i)
135  {
136  const auto new_cf = newManifold.GetContactFeature(i);
137  for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j)
138  {
139  if (new_cf == oldManifold.GetContactFeature(j))
140  {
141  found[i] = true;
142  newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
143  break;
144  }
145  }
146  }
147  // If warm starting data wasn't found for a manifold point via contact feature
148  // matching, it's better to just set the data to whatever old point is closest
149  // to the new one.
150  for (auto i = decltype(new_point_count){0}; i < new_point_count; ++i)
151  {
152  if (!found[i])
153  {
154  auto leastSquareDiff = std::numeric_limits<Area>::infinity();
155  const auto newPt = newManifold.GetPoint(i);
156  for (auto j = decltype(old_point_count){0}; j < old_point_count; ++j)
157  {
158  const auto oldPt = oldManifold.GetPoint(j);
159  const auto squareDiff = GetMagnitudeSquared(oldPt.localPoint - newPt.localPoint);
160  if (leastSquareDiff > squareDiff)
161  {
162  leastSquareDiff = squareDiff;
163  newManifold.SetContactImpulses(i, oldManifold.GetContactImpulses(j));
164  }
165  }
166  }
167  }
168 
169  // Ideally this method is **NEVER** called unless a dependency changed such
170  // that the following assertion is **ALWAYS** valid.
171  //assert(newManifold != oldManifold);
172 
173  m_manifold = newManifold;
174 
175 #ifdef MAKE_CONTACT_PROCESSING_ORDER_DEPENDENT
176  const auto bodyA = fixtureA->GetBody();
177  const auto bodyB = fixtureB->GetBody();
178 
179  assert(bodyA);
180  assert(bodyB);
181 
182  /*
183  * The following code creates an ordering dependency in terms of update processing
184  * over a container of contacts. It also puts this method into the situation of
185  * modifying bodies which adds race potential in a multi-threaded mode of operation.
186  * Lastly, without this code, the step-statistics show a world getting to sleep in
187  * less TOI position iterations.
188  */
189  if (newTouching != oldTouching)
190  {
191  bodyA->SetAwake();
192  bodyB->SetAwake();
193  }
194 #endif
195  }
196 
197  UnflagForUpdating();
198 
199  if (!oldTouching && newTouching)
200  {
201  SetTouching();
202  if (listener)
203  {
204  listener->BeginContact(*this);
205  }
206  }
207  else if (oldTouching && !newTouching)
208  {
209  UnsetTouching();
210  if (listener)
211  {
212  listener->EndContact(*this);
213  }
214  }
215 
216  if (!sensor && newTouching)
217  {
218  if (listener)
219  {
220  listener->PreSolve(*this, oldManifold);
221  }
222  }
223 }
224 
225 // Free functions...
226 
227 Body* GetBodyA(const Contact& contact) noexcept
228 {
229  return contact.GetFixtureA()->GetBody();
230 }
231 
232 Body* GetBodyB(const Contact& contact) noexcept
233 {
234  return contact.GetFixtureB()->GetBody();
235 }
236 
237 bool HasSensor(const Contact& contact) noexcept
238 {
239  return contact.GetFixtureA()->IsSensor() || contact.GetFixtureB()->IsSensor();
240 }
241 
242 bool IsImpenetrable(const Contact& contact) noexcept
243 {
244  const auto bA = contact.GetFixtureA()->GetBody();
245  const auto bB = contact.GetFixtureB()->GetBody();
246  return bA->IsImpenetrable() || bB->IsImpenetrable();
247 }
248 
249 bool IsActive(const Contact& contact) noexcept
250 {
251  const auto bA = contact.GetFixtureA()->GetBody();
252  const auto bB = contact.GetFixtureB()->GetBody();
253 
254  assert(!bA->IsAwake() || bA->IsSpeedable());
255  assert(!bB->IsAwake() || bB->IsSpeedable());
256 
257  const auto activeA = bA->IsAwake();
258  const auto activeB = bB->IsAwake();
259 
260  // Is at least one body active (awake and dynamic or kinematic)?
261  return activeA || activeB;
262 }
263 
264 void SetAwake(const Contact& c) noexcept
265 {
266  SetAwake(*c.GetFixtureA());
267  SetAwake(*c.GetFixtureB());
268 }
269 
271 void ResetFriction(Contact& contact)
272 {
273  contact.SetFriction(MixFriction(contact.GetFixtureA()->GetFriction(), contact.GetFixtureB()->GetFriction()));
274 }
275 
277 void ResetRestitution(Contact& contact) noexcept
278 {
279  const auto restitutionA = contact.GetFixtureA()->GetRestitution();
280  const auto restitutionB = contact.GetFixtureB()->GetRestitution();
281  contact.SetRestitution(MixRestitution(restitutionA, restitutionB));
282 }
283 
284 TOIOutput CalcToi(const Contact& contact, ToiConf conf)
285 {
286  const auto fA = contact.GetFixtureA();
287  const auto fB = contact.GetFixtureB();
288  const auto bA = fA->GetBody();
289  const auto bB = fB->GetBody();
290 
291  const auto proxyA = GetChild(fA->GetShape(), contact.GetChildIndexA());
292  const auto proxyB = GetChild(fB->GetShape(), contact.GetChildIndexB());
293 
294  // Large rotations can make the root finder of TimeOfImpact fail, so normalize sweep angles.
295  const auto sweepA = GetNormalized(bA->GetSweep());
296  const auto sweepB = GetNormalized(bB->GetSweep());
297 
298  // Compute the TOI for this contact (one or both bodies are active and impenetrable).
299  // Computes the time of impact in interval [0, 1]
300  // Large rotations can make the root finder of TimeOfImpact fail, so normalize the sweep angles.
301  return GetToiViaSat(proxyA, sweepA, proxyB, sweepB, conf);
302 }
303 
304 } // namespace d2
305 } // namespace playrho