1 /******************************************************************************* 2 * Copyright (c) 2013, Daniel Murphy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 17 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 22 * POSSIBILITY OF SUCH DAMAGE. 23 ******************************************************************************/ 24 package org.jbox2d.dynamics; 25 26 import org.jbox2d.collision.AABB; 27 import org.jbox2d.collision.RayCastInput; 28 import org.jbox2d.collision.RayCastOutput; 29 import org.jbox2d.collision.broadphase.BroadPhase; 30 import org.jbox2d.collision.shapes.MassData; 31 import org.jbox2d.collision.shapes.Shape; 32 import org.jbox2d.collision.shapes.ShapeType; 33 import org.jbox2d.common.MathUtils; 34 import org.jbox2d.common.Transform; 35 import org.jbox2d.common.Vec2; 36 import org.jbox2d.dynamics.contacts.Contact; 37 import org.jbox2d.dynamics.contacts.ContactEdge; 38 39 /** 40 * A fixture is used to attach a shape to a body for collision detection. A fixture inherits its 41 * transform from its parent. Fixtures hold additional non-geometric data such as friction, 42 * collision filters, etc. Fixtures are created via Body::CreateFixture. 43 * 44 * @warning you cannot reuse fixtures. 45 * 46 * @author daniel 47 */ 48 public class Fixture { 49 50 public float m_density; 51 52 public Fixture m_next; 53 public Body m_body; 54 55 public Shape m_shape; 56 57 public float m_friction; 58 public float m_restitution; 59 60 public FixtureProxy[] m_proxies; 61 public int m_proxyCount; 62 63 public final Filter m_filter; 64 65 public boolean m_isSensor; 66 67 public Object m_userData; 68 69 public Fixture() { 70 m_userData = null; 71 m_body = null; 72 m_next = null; 73 m_proxies = null; 74 m_proxyCount = 0; 75 m_shape = null; 76 m_filter = new Filter(); 77 } 78 79 /** 80 * Get the type of the child shape. You can use this to down cast to the concrete shape. 81 * 82 * @return the shape type. 83 */ 84 public ShapeType getType() { 85 return m_shape.getType(); 86 } 87 88 /** 89 * Get the child shape. You can modify the child shape, however you should not change the number 90 * of vertices because this will crash some collision caching mechanisms. 91 * 92 * @return 93 */ 94 public Shape getShape() { 95 return m_shape; 96 } 97 98 /** 99 * Is this fixture a sensor (non-solid)? 100 * 101 * @return the true if the shape is a sensor. 102 * @return 103 */ 104 public boolean isSensor() { 105 return m_isSensor; 106 } 107 108 /** 109 * Set if this fixture is a sensor. 110 * 111 * @param sensor 112 */ 113 public void setSensor(boolean sensor) { 114 if (sensor != m_isSensor) { 115 m_body.setAwake(true); 116 m_isSensor = sensor; 117 } 118 } 119 120 /** 121 * Set the contact filtering data. This is an expensive operation and should not be called 122 * frequently. This will not update contacts until the next time step when either parent body is 123 * awake. This automatically calls refilter. 124 * 125 * @param filter 126 */ 127 public void setFilterData(final Filter filter) { 128 m_filter.set(filter); 129 130 refilter(); 131 } 132 133 /** 134 * Get the contact filtering data. 135 * 136 * @return 137 */ 138 public Filter getFilterData() { 139 return m_filter; 140 } 141 142 /** 143 * Call this if you want to establish collision that was previously disabled by 144 * ContactFilter::ShouldCollide. 145 */ 146 public void refilter() { 147 if (m_body == null) { 148 return; 149 } 150 151 // Flag associated contacts for filtering. 152 ContactEdge edge = m_body.getContactList(); 153 while (edge != null) { 154 Contact contact = edge.contact; 155 Fixture fixtureA = contact.getFixtureA(); 156 Fixture fixtureB = contact.getFixtureB(); 157 if (fixtureA == this || fixtureB == this) { 158 contact.flagForFiltering(); 159 } 160 edge = edge.next; 161 } 162 163 World world = m_body.getWorld(); 164 165 if (world == null) { 166 return; 167 } 168 169 // Touch each proxy so that new pairs may be created 170 BroadPhase broadPhase = world.m_contactManager.m_broadPhase; 171 for (int i = 0; i < m_proxyCount; ++i) { 172 broadPhase.touchProxy(m_proxies[i].proxyId); 173 } 174 } 175 176 /** 177 * Get the parent body of this fixture. This is NULL if the fixture is not attached. 178 * 179 * @return the parent body. 180 * @return 181 */ 182 public Body getBody() { 183 return m_body; 184 } 185 186 /** 187 * Get the next fixture in the parent body's fixture list. 188 * 189 * @return the next shape. 190 * @return 191 */ 192 public Fixture getNext() { 193 return m_next; 194 } 195 196 public void setDensity(float density) { 197 assert (density >= 0f); 198 m_density = density; 199 } 200 201 public float getDensity() { 202 return m_density; 203 } 204 205 /** 206 * Get the user data that was assigned in the fixture definition. Use this to store your 207 * application specific data. 208 * 209 * @return 210 */ 211 public Object getUserData() { 212 return m_userData; 213 } 214 215 /** 216 * Set the user data. Use this to store your application specific data. 217 * 218 * @param data 219 */ 220 public void setUserData(Object data) { 221 m_userData = data; 222 } 223 224 /** 225 * Test a point for containment in this fixture. This only works for convex shapes. 226 * 227 * @param p a point in world coordinates. 228 * @return 229 */ 230 public boolean testPoint(final Vec2 p) { 231 return m_shape.testPoint(m_body.m_xf, p); 232 } 233 234 /** 235 * Cast a ray against this shape. 236 * 237 * @param output the ray-cast results. 238 * @param input the ray-cast input parameters. 239 * @param output 240 * @param input 241 */ 242 public boolean raycast(RayCastOutput output, RayCastInput input, int childIndex) { 243 return m_shape.raycast(output, input, m_body.m_xf, childIndex); 244 } 245 246 /** 247 * Get the mass data for this fixture. The mass data is based on the density and the shape. The 248 * rotational inertia is about the shape's origin. 249 * 250 * @return 251 */ 252 public void getMassData(MassData massData) { 253 m_shape.computeMass(massData, m_density); 254 } 255 256 /** 257 * Get the coefficient of friction. 258 * 259 * @return 260 */ 261 public float getFriction() { 262 return m_friction; 263 } 264 265 /** 266 * Set the coefficient of friction. This will _not_ change the friction of existing contacts. 267 * 268 * @param friction 269 */ 270 public void setFriction(float friction) { 271 m_friction = friction; 272 } 273 274 /** 275 * Get the coefficient of restitution. 276 * 277 * @return 278 */ 279 public float getRestitution() { 280 return m_restitution; 281 } 282 283 /** 284 * Set the coefficient of restitution. This will _not_ change the restitution of existing 285 * contacts. 286 * 287 * @param restitution 288 */ 289 public void setRestitution(float restitution) { 290 m_restitution = restitution; 291 } 292 293 /** 294 * Get the fixture's AABB. This AABB may be enlarge and/or stale. If you need a more accurate 295 * AABB, compute it using the shape and the body transform. 296 * 297 * @return 298 */ 299 public AABB getAABB(int childIndex) { 300 assert (childIndex >= 0 && childIndex < m_proxyCount); 301 return m_proxies[childIndex].aabb; 302 } 303 304 /** 305 * Compute the distance from this fixture. 306 * 307 * @param p a point in world coordinates. 308 * @return distance 309 */ 310 public float computeDistance(Vec2 p, int childIndex, Vec2 normalOut) { 311 return m_shape.computeDistanceToOut(m_body.getTransform(), p, childIndex, normalOut); 312 } 313 314 // We need separation create/destroy functions from the constructor/destructor because 315 // the destructor cannot access the allocator (no destructor arguments allowed by C++). 316 317 public void create(Body body, FixtureDef def) { 318 m_userData = def.userData; 319 m_friction = def.friction; 320 m_restitution = def.restitution; 321 322 m_body = body; 323 m_next = null; 324 325 326 m_filter.set(def.filter); 327 328 m_isSensor = def.isSensor; 329 330 m_shape = def.shape.clone(); 331 332 // Reserve proxy space 333 int childCount = m_shape.getChildCount(); 334 if (m_proxies == null) { 335 m_proxies = new FixtureProxy[childCount]; 336 for (int i = 0; i < childCount; i++) { 337 m_proxies[i] = new FixtureProxy(); 338 m_proxies[i].fixture = null; 339 m_proxies[i].proxyId = BroadPhase.NULL_PROXY; 340 } 341 } 342 343 if (m_proxies.length < childCount) { 344 FixtureProxy[] old = m_proxies; 345 int newLen = MathUtils.max(old.length * 2, childCount); 346 m_proxies = new FixtureProxy[newLen]; 347 System.arraycopy(old, 0, m_proxies, 0, old.length); 348 for (int i = 0; i < newLen; i++) { 349 if (i >= old.length) { 350 m_proxies[i] = new FixtureProxy(); 351 } 352 m_proxies[i].fixture = null; 353 m_proxies[i].proxyId = BroadPhase.NULL_PROXY; 354 } 355 } 356 m_proxyCount = 0; 357 358 m_density = def.density; 359 } 360 361 public void destroy() { 362 // The proxies must be destroyed before calling this. 363 assert (m_proxyCount == 0); 364 365 // Free the child shape. 366 m_shape = null; 367 m_proxies = null; 368 m_next = null; 369 370 // TODO pool shapes 371 // TODO pool fixtures 372 } 373 374 // These support body activation/deactivation. 375 public void createProxies(BroadPhase broadPhase, final Transform xf) { 376 assert (m_proxyCount == 0); 377 378 // Create proxies in the broad-phase. 379 m_proxyCount = m_shape.getChildCount(); 380 381 for (int i = 0; i < m_proxyCount; ++i) { 382 FixtureProxy proxy = m_proxies[i]; 383 m_shape.computeAABB(proxy.aabb, xf, i); 384 proxy.proxyId = broadPhase.createProxy(proxy.aabb, proxy); 385 proxy.fixture = this; 386 proxy.childIndex = i; 387 } 388 } 389 390 /** 391 * Internal method 392 * 393 * @param broadPhase 394 */ 395 public void destroyProxies(BroadPhase broadPhase) { 396 // Destroy proxies in the broad-phase. 397 for (int i = 0; i < m_proxyCount; ++i) { 398 FixtureProxy proxy = m_proxies[i]; 399 broadPhase.destroyProxy(proxy.proxyId); 400 proxy.proxyId = BroadPhase.NULL_PROXY; 401 } 402 403 m_proxyCount = 0; 404 } 405 406 private final AABB pool1 = new AABB(); 407 private final AABB pool2 = new AABB(); 408 private final Vec2 displacement = new Vec2(); 409 410 /** 411 * Internal method 412 * 413 * @param broadPhase 414 * @param xf1 415 * @param xf2 416 */ 417 protected void synchronize(BroadPhase broadPhase, final Transform transform1, 418 final Transform transform2) { 419 if (m_proxyCount == 0) { 420 return; 421 } 422 423 for (int i = 0; i < m_proxyCount; ++i) { 424 FixtureProxy proxy = m_proxies[i]; 425 426 // Compute an AABB that covers the swept shape (may miss some rotation effect). 427 final AABB aabb1 = pool1; 428 final AABB aab = pool2; 429 m_shape.computeAABB(aabb1, transform1, proxy.childIndex); 430 m_shape.computeAABB(aab, transform2, proxy.childIndex); 431 432 proxy.aabb.lowerBound.x = 433 aabb1.lowerBound.x < aab.lowerBound.x ? aabb1.lowerBound.x : aab.lowerBound.x; 434 proxy.aabb.lowerBound.y = 435 aabb1.lowerBound.y < aab.lowerBound.y ? aabb1.lowerBound.y : aab.lowerBound.y; 436 proxy.aabb.upperBound.x = 437 aabb1.upperBound.x > aab.upperBound.x ? aabb1.upperBound.x : aab.upperBound.x; 438 proxy.aabb.upperBound.y = 439 aabb1.upperBound.y > aab.upperBound.y ? aabb1.upperBound.y : aab.upperBound.y; 440 displacement.x = transform2.p.x - transform1.p.x; 441 displacement.y = transform2.p.y - transform1.p.y; 442 443 broadPhase.moveProxy(proxy.proxyId, proxy.aabb, displacement); 444 } 445 } 446 } 447