public boolean processGestureEvent(MTGestureEvent ge) { DragEvent de = (DragEvent) ge; try { Body body = (Body) comp.getUserData("box2d"); MouseJoint mouseJoint; Vector3D to = new Vector3D(de.getTo()); // Un-scale position from mt4j to box2d PhysicsHelper.scaleDown(to, scale); switch (de.getId()) { case DragEvent.GESTURE_STARTED: comp.sendToFront(); body.wakeUp(); body.setXForm(new Vec2(to.x, to.y), body.getAngle()); mouseJoint = PhysicsHelper.createDragJoint(world, body, to.x, to.y); comp.setUserData(comp.getID(), mouseJoint); break; case DragEvent.GESTURE_UPDATED: mouseJoint = (MouseJoint) comp.getUserData(comp.getID()); if (mouseJoint != null) { boolean onCorrectGameSide = ((MTComponent) de.getTarget()).containsPointGlobal(de.getTo()); // System.out.println(((MTComponent)de.getTargetComponent()).getName() + " Contains // " + to + " -> " + contains); if (onCorrectGameSide) { mouseJoint.setTarget(new Vec2(to.x, to.y)); } } break; case DragEvent.GESTURE_ENDED: mouseJoint = (MouseJoint) comp.getUserData(comp.getID()); if (mouseJoint != null) { comp.setUserData(comp.getID(), null); // Only destroy the joint if it isnt already (go through joint list and check) for (Joint joint = world.getJointList(); joint != null; joint = joint.getNext()) { JointType type = joint.getType(); switch (type) { case MOUSE_JOINT: MouseJoint mj = (MouseJoint) joint; if (body.equals(mj.getBody1()) || body.equals(mj.getBody2())) { if (mj.equals(mouseJoint)) { world.destroyJoint(mj); } } break; default: break; } } } mouseJoint = null; break; default: break; } } catch (Exception e) { System.err.println(e.getMessage()); } return false; }
public void step(float dt, int iterations) { if (joint != null) { Vec2 force = joint.getReactionForce(); float forceAngle = PhysicsUtils.angle(force); float forceMag = force.length(); if (forceMag > reactionThreshold) { /* * First, check for direction. */ if (angleLo == angleHi || (forceAngle >= angleLo && forceAngle <= angleHi)) { // Ok, this one's done with. Kill it. joint.getBody1().getWorld().destroyJoint(joint); joint = null; joint.getBody1().getWorld().unregisterPostStep(this); } } } }
/** * create a joint to constrain bodies together. No reference to the definition is retained. This * may cause the connected bodies to cease colliding. * * @warning This function is locked during callbacks. * @param def * @return */ public Joint createJoint(JointDef def) { assert (isLocked() == false); if (isLocked()) { return null; } Joint j = Joint.create(this, def); // Connect to the world list. j.m_prev = null; j.m_next = m_jointList; if (m_jointList != null) { m_jointList.m_prev = j; } m_jointList = j; ++m_jointCount; // Connect to the bodies' doubly linked lists. j.m_edgeA.joint = j; j.m_edgeA.other = j.m_bodyB; j.m_edgeA.prev = null; j.m_edgeA.next = j.m_bodyA.m_jointList; if (j.m_bodyA.m_jointList != null) { j.m_bodyA.m_jointList.prev = j.m_edgeA; } j.m_bodyA.m_jointList = j.m_edgeA; j.m_edgeB.joint = j; j.m_edgeB.other = j.m_bodyA; j.m_edgeB.prev = null; j.m_edgeB.next = j.m_bodyB.m_jointList; if (j.m_bodyB.m_jointList != null) { j.m_bodyB.m_jointList.prev = j.m_edgeB; } j.m_bodyB.m_jointList = j.m_edgeB; Body bodyA = def.bodyA; Body bodyB = def.bodyB; // If the joint prevents collisions, then flag any contacts for filtering. if (def.collideConnected == false) { ContactEdge edge = bodyB.getContactList(); while (edge != null) { if (edge.other == bodyA) { // Flag the contact for filtering at the next time step (where either // body is awake). edge.contact.flagForFiltering(); } edge = edge.next; } } // Note: creating a joint doesn't wake the bodies. return j; }
private void drawJoint(Joint joint) { Body bodyA = joint.getBodyA(); Body bodyB = joint.getBodyB(); Transform xf1 = bodyA.getTransform(); Transform xf2 = bodyB.getTransform(); Vec2 x1 = xf1.p; Vec2 x2 = xf2.p; Vec2 p1 = pool.popVec2(); Vec2 p2 = pool.popVec2(); joint.getAnchorA(p1); joint.getAnchorB(p2); color.set(0.5f, 0.8f, 0.8f); switch (joint.getType()) { // TODO djm write after writing joints case DISTANCE: m_debugDraw.drawSegment(p1, p2, color); break; case PULLEY: { PulleyJoint pulley = (PulleyJoint) joint; Vec2 s1 = pulley.getGroundAnchorA(); Vec2 s2 = pulley.getGroundAnchorB(); m_debugDraw.drawSegment(s1, p1, color); m_debugDraw.drawSegment(s2, p2, color); m_debugDraw.drawSegment(s1, s2, color); } break; case CONSTANT_VOLUME: case MOUSE: // don't draw this break; default: m_debugDraw.drawSegment(x1, p1, color); m_debugDraw.drawSegment(p1, p2, color); m_debugDraw.drawSegment(x2, p2, color); } pool.pushVec2(2); }
private void solve(TimeStep step) { m_profile.solveInit = 0; m_profile.solveVelocity = 0; m_profile.solvePosition = 0; // Size the island for the worst case. island.init( m_bodyCount, m_contactManager.m_contactCount, m_jointCount, m_contactManager.m_contactListener); // Clear all the island flags. for (Body b = m_bodyList; b != null; b = b.m_next) { b.m_flags &= ~Body.e_islandFlag; } for (Contact c = m_contactManager.m_contactList; c != null; c = c.m_next) { c.m_flags &= ~Contact.ISLAND_FLAG; } for (Joint j = m_jointList; j != null; j = j.m_next) { j.m_islandFlag = false; } // Build and simulate all awake islands. int stackSize = m_bodyCount; if (stack.length < stackSize) { stack = new Body[stackSize]; } for (Body seed = m_bodyList; seed != null; seed = seed.m_next) { if ((seed.m_flags & Body.e_islandFlag) == Body.e_islandFlag) { continue; } if (seed.isAwake() == false || seed.isActive() == false) { continue; } // The seed can be dynamic or kinematic. if (seed.getType() == BodyType.STATIC) { continue; } // Reset island and stack. island.clear(); int stackCount = 0; stack[stackCount++] = seed; seed.m_flags |= Body.e_islandFlag; // Perform a depth first search (DFS) on the constraint graph. while (stackCount > 0) { // Grab the next body off the stack and add it to the island. Body b = stack[--stackCount]; assert (b.isActive() == true); island.add(b); // Make sure the body is awake. b.setAwake(true); // To keep islands as small as possible, we don't // propagate islands across static bodies. if (b.getType() == BodyType.STATIC) { continue; } // Search all contacts connected to this body. for (ContactEdge ce = b.m_contactList; ce != null; ce = ce.next) { Contact contact = ce.contact; // Has this contact already been added to an island? if ((contact.m_flags & Contact.ISLAND_FLAG) == Contact.ISLAND_FLAG) { continue; } // Is this contact solid and touching? if (contact.isEnabled() == false || contact.isTouching() == false) { continue; } // Skip sensors. boolean sensorA = contact.m_fixtureA.m_isSensor; boolean sensorB = contact.m_fixtureB.m_isSensor; if (sensorA || sensorB) { continue; } island.add(contact); contact.m_flags |= Contact.ISLAND_FLAG; Body other = ce.other; // Was the other body already added to this island? if ((other.m_flags & Body.e_islandFlag) == Body.e_islandFlag) { continue; } assert (stackCount < stackSize); stack[stackCount++] = other; other.m_flags |= Body.e_islandFlag; } // Search all joints connect to this body. for (JointEdge je = b.m_jointList; je != null; je = je.next) { if (je.joint.m_islandFlag == true) { continue; } Body other = je.other; // Don't simulate joints connected to inactive bodies. if (other.isActive() == false) { continue; } island.add(je.joint); je.joint.m_islandFlag = true; if ((other.m_flags & Body.e_islandFlag) == Body.e_islandFlag) { continue; } assert (stackCount < stackSize); stack[stackCount++] = other; other.m_flags |= Body.e_islandFlag; } } island.solve(islandProfile, step, m_gravity, m_allowSleep); m_profile.solveInit += islandProfile.solveInit; m_profile.solveVelocity += islandProfile.solveVelocity; m_profile.solvePosition += islandProfile.solvePosition; // Post solve cleanup. for (int i = 0; i < island.m_bodyCount; ++i) { // Allow static bodies to participate in other islands. Body b = island.m_bodies[i]; if (b.getType() == BodyType.STATIC) { b.m_flags &= ~Body.e_islandFlag; } } } broadphaseTimer.reset(); // Synchronize fixtures, check for out of range bodies. for (Body b = m_bodyList; b != null; b = b.getNext()) { // If a body was not in an island then it did not move. if ((b.m_flags & Body.e_islandFlag) == 0) { continue; } if (b.getType() == BodyType.STATIC) { continue; } // Update fixtures (for broad-phase). b.synchronizeFixtures(); } // Look for new contacts. m_contactManager.findNewContacts(); m_profile.broadphase = broadphaseTimer.getMilliseconds(); }
/** Call this to draw shapes and other debug draw data. */ public void drawDebugData() { if (m_debugDraw == null) { return; } int flags = m_debugDraw.getFlags(); if ((flags & DebugDraw.e_shapeBit) == DebugDraw.e_shapeBit) { for (Body b = m_bodyList; b != null; b = b.getNext()) { xf.set(b.getTransform()); for (Fixture f = b.getFixtureList(); f != null; f = f.getNext()) { if (b.isActive() == false) { color.set(0.5f, 0.5f, 0.3f); drawShape(f, xf, color); } else if (b.getType() == BodyType.STATIC) { color.set(0.5f, 0.9f, 0.3f); drawShape(f, xf, color); } else if (b.getType() == BodyType.KINEMATIC) { color.set(0.5f, 0.5f, 0.9f); drawShape(f, xf, color); } else if (b.isAwake() == false) { color.set(0.5f, 0.5f, 0.5f); drawShape(f, xf, color); } else { color.set(0.9f, 0.7f, 0.7f); drawShape(f, xf, color); } } } } if ((flags & DebugDraw.e_jointBit) == DebugDraw.e_jointBit) { for (Joint j = m_jointList; j != null; j = j.getNext()) { drawJoint(j); } } if ((flags & DebugDraw.e_pairBit) == DebugDraw.e_pairBit) { color.set(0.3f, 0.9f, 0.9f); for (Contact c = m_contactManager.m_contactList; c != null; c = c.getNext()) { // Fixture fixtureA = c.getFixtureA(); // Fixture fixtureB = c.getFixtureB(); // // fixtureA.getAABB(childIndex).getCenterToOut(cA); // fixtureB.getAABB().getCenterToOut(cB); // // m_debugDraw.drawSegment(cA, cB, color); } } if ((flags & DebugDraw.e_aabbBit) == DebugDraw.e_aabbBit) { color.set(0.9f, 0.3f, 0.9f); for (Body b = m_bodyList; b != null; b = b.getNext()) { if (b.isActive() == false) { continue; } for (Fixture f = b.getFixtureList(); f != null; f = f.getNext()) { for (int i = 0; i < f.m_proxyCount; ++i) { FixtureProxy proxy = f.m_proxies[i]; AABB aabb = m_contactManager.m_broadPhase.getFatAABB(proxy.proxyId); Vec2[] vs = avs.get(4); vs[0].set(aabb.lowerBound.x, aabb.lowerBound.y); vs[1].set(aabb.upperBound.x, aabb.lowerBound.y); vs[2].set(aabb.upperBound.x, aabb.upperBound.y); vs[3].set(aabb.lowerBound.x, aabb.upperBound.y); m_debugDraw.drawPolygon(vs, 4, color); } } } } if ((flags & DebugDraw.e_centerOfMassBit) == DebugDraw.e_centerOfMassBit) { for (Body b = m_bodyList; b != null; b = b.getNext()) { xf.set(b.getTransform()); xf.p.set(b.getWorldCenter()); m_debugDraw.drawTransform(xf); } } if ((flags & DebugDraw.e_dynamicTreeBit) == DebugDraw.e_dynamicTreeBit) { m_contactManager.m_broadPhase.drawTree(m_debugDraw); } }
/** * destroy a joint. This may cause the connected bodies to begin colliding. * * @warning This function is locked during callbacks. * @param joint */ public void destroyJoint(Joint j) { assert (isLocked() == false); if (isLocked()) { return; } boolean collideConnected = j.m_collideConnected; // Remove from the doubly linked list. if (j.m_prev != null) { j.m_prev.m_next = j.m_next; } if (j.m_next != null) { j.m_next.m_prev = j.m_prev; } if (j == m_jointList) { m_jointList = j.m_next; } // Disconnect from island graph. Body bodyA = j.m_bodyA; Body bodyB = j.m_bodyB; // Wake up connected bodies. bodyA.setAwake(true); bodyB.setAwake(true); // Remove from body 1. if (j.m_edgeA.prev != null) { j.m_edgeA.prev.next = j.m_edgeA.next; } if (j.m_edgeA.next != null) { j.m_edgeA.next.prev = j.m_edgeA.prev; } if (j.m_edgeA == bodyA.m_jointList) { bodyA.m_jointList = j.m_edgeA.next; } j.m_edgeA.prev = null; j.m_edgeA.next = null; // Remove from body 2 if (j.m_edgeB.prev != null) { j.m_edgeB.prev.next = j.m_edgeB.next; } if (j.m_edgeB.next != null) { j.m_edgeB.next.prev = j.m_edgeB.prev; } if (j.m_edgeB == bodyB.m_jointList) { bodyB.m_jointList = j.m_edgeB.next; } j.m_edgeB.prev = null; j.m_edgeB.next = null; Joint.destroy(j); assert (m_jointCount > 0); --m_jointCount; // If the joint prevents collisions, then flag any contacts for filtering. if (collideConnected == false) { ContactEdge edge = bodyB.getContactList(); while (edge != null) { if (edge.other == bodyA) { // Flag the contact for filtering at the next time step (where either // body is awake). edge.contact.flagForFiltering(); } edge = edge.next; } } }
// ewjordan: I've added a Destroy method because although // these usually just deallocate memory, it is possible that // Erin may alter them to do more nontrivial things, and we // should be prepared for this possibility. // Note: this now happens in ConstantVolumeJoint, because // it contains distance joints that also need to be destroyed. public static void destroy(final Joint j) { j.destructor(); return; }