/// Main collision callback functor. static unsafe internal void CollisionCallback(IntPtr data, dGeomID o0, dGeomID o1) { if (o0.IsSpace || o1.IsSpace) { // Colliding a space with either a geom or another space. Tao.Ode.Ode.dSpaceCollide2(o0, o1, data, CollisionCallbackRunner); if (o0.IsSpace) { // Colliding all geoms internal to the space. Tao.Ode.Ode.dSpaceCollide(o0, data, CollisionCallbackRunner); } if (o1.IsSpace) { // Colliding all geoms internal to the space. Tao.Ode.Ode.dSpaceCollide(o1, data, CollisionCallbackRunner); } } else { // Colliding two geoms. // The following is a set of special cases where we might // want to skip collision detection (but not all are // enforced here for various reasons): // 1. Two static Solids (neither geom has a body) AND // neither Solid has a CollisionEventHandler AND there are // not global handlers: this is enforced. // 2. Two Shapes that are part of the same Solid (they // share a body): this is not enforced because ODE // already takes care of it. // 3. Two sleeping Solids (note: both must have bodies to // check this): this is enforced. (ODE should handle // this, but it doesn't.) // 4. Two Solids connected by a fixed Joint: this is // enforced. // 5. Two Solids connected by a Joint (besides ODE // contact joints, which should never generate more // contacts) with contacts disabled (note: both must have // bodies to check this): this is enforced. // 6. Solid0 is static, Solid1 is dynamic and is sleeping, // static-to-sleeping contacts are ignored by the // Simulator, and neither Solid has a // CollisionEventHandler AND there are no global handlers: // this is enforced. // 7. Solid1 is static, Solid0 is dynamic and is sleeping, // static-to-sleeping contacts are ignored by the // Simulator, and neither Solid has a // CollisionEventHandler AND there are no global handlers: // this is enforced. // 8. The two Solids' contact groups do not generate // contacts when they collide AND neither Solid has a // CollisionEventHandler AND there are no global handlers. // Get the geoms' ODE body IDs. dBodyID o0BodyID = o0.RigidBody; dBodyID o1BodyID = o1.RigidBody; bool solid0Static = PtrWrapper.IsEmpty(o0BodyID); // (mike) bool solid1Static = PtrWrapper.IsEmpty(o1BodyID); // {mike} // Check if both Solids are dynamic (i.e. have ODE bodies). bool bothHaveBodies = true; if (solid0Static || solid1Static) { bothHaveBodies = false; } // If the two Solids are connected by a common Joint, get // a pointer to that Joint. Joint commonJoint = null; if (bothHaveBodies) { int connExcluding = Tao.Ode.Ode.dAreConnectedExcluding(o0BodyID, o1BodyID, (int)Tao.Ode.Ode.dJointTypes.dJointTypeContact); if (connExcluding != 0) { // This will become non-NULL only if there exists an ODE // joint connecting the two bodies. commonJoint = GetCommonJoint(o0BodyID, o1BodyID); } } // Get pointers to the geoms' GeomData structures. GeomData geomData0 = GCHandle.FromIntPtr(Tao.Ode.Ode.dGeomGetData(o0)).Target as GeomData; GeomData geomData1 = GCHandle.FromIntPtr(Tao.Ode.Ode.dGeomGetData(o1)).Target as GeomData; // Get pointers to the geoms' ShapeData structures. ShapeData shape0 = geomData0.Shape; ShapeData shape1 = geomData1.Shape; // Get a pointer to the ODESimulator. OdeSimulator sim = GCHandle.FromIntPtr(data).Target as OdeSimulator; // Check if the two Solids' contact groups generate contacts // when they collide. bool makeContacts = sim.GroupsMakeContacts(shape0.ContactGroup, shape1.ContactGroup); // Find out whether the Simulator has static-to-sleeping // contacts disabled. bool ignoreStaticSleepingContacts = !sim.IsStaticSleepingContactsEnabled; // Get pointers to the geoms' Solids. Solid solid0 = geomData0.Solid; Solid solid1 = geomData1.Solid; // Get pointers to the two Solids' CollisionEventHandlers. // These will be NULL if the Solids don't use // CollisionEventHandlers. CollisionEventProcessor handler0 = solid0.CollisionEventHandler; CollisionEventProcessor handler1 = solid1.CollisionEventHandler; bool neitherHasEventHandler = !(handler0 != null || handler1 != null); bool hasNoGlobalHandler = sim.NumGlobalCollisionEventHandlers == 0; // Now do the actual tests to see if we should return early. // It is important here that we don't call dBodyIsEnabled on // a static body because that crashes ODE. bool case1 = neitherHasEventHandler && hasNoGlobalHandler && solid0Static && solid1Static; //bool case2= o0BodyID == o1BodyID; bool case3 = bothHaveBodies && Tao.Ode.Ode.dBodyIsEnabled(o0BodyID) == 0 && Tao.Ode.Ode.dBodyIsEnabled(o1BodyID) == 0; bool case4 = commonJoint != null && commonJoint.Type == JointType.Fixed; bool case5 = commonJoint != null && !commonJoint.ContactsEnabled; bool case6 = solid0Static && null != o1BodyID && o1BodyID.IsNotNull() && Tao.Ode.Ode.dBodyIsEnabled(o1BodyID) == 0 && ignoreStaticSleepingContacts && neitherHasEventHandler && hasNoGlobalHandler; bool case7 = solid1Static && null != o0BodyID && o0BodyID.IsNotNull() && Tao.Ode.Ode.dBodyIsEnabled(o0BodyID) == 0 && ignoreStaticSleepingContacts && neitherHasEventHandler && hasNoGlobalHandler; bool case8 = !makeContacts && neitherHasEventHandler && hasNoGlobalHandler; if (case1 || case3 || case4 || case5 || case6 || case7 || case8) { return; } // Now actually test for collision between the two geoms. // This is one of the more expensive operations. IntPtr theWorldID = sim.World; IntPtr theJointGroupID = sim.JointGroup; int contGeomSize = sizeof(Tao.Ode.Ode.dContactGeom); Tao.Ode.Ode.dContactGeom[] contactArray = new Tao.Ode.Ode.dContactGeom[sim.MaxContacts]; int numContacts = 0; /*try * {*/ numContacts = Tao.Ode.Ode.dCollide(o0, o1, sim.MaxContacts /*was 15*/, contactArray, contGeomSize); // Was big perfomance problem here (KleMiX) /*} * catch(Exception) * { * return; * }*/ // If the two objects didn't make any contacts, they weren't // touching, so just return. if (0 == numContacts) { return; } // If at least one of the Solids has a CollisionEventHandler, // send it a CollisionEvent. if (handler0 != null || handler1 != null || !hasNoGlobalHandler) { // Call the CollisionEventHandlers. Note that we only // use one contact point per collision: just the first one // in the contact array. The order of the Solids // passed to the event handlers is important: the first // one should be the one whose event handler is // getting called. CollisionEvent e = new CollisionEvent(); e.ThisSolid = solid0; e.OtherSolid = solid1; e.Position.X = contactArray[0].pos.X; e.Position.Y = contactArray[0].pos.Y; e.Position.Z = contactArray[0].pos.Z; e.Normal.X = contactArray[0].normal.X; e.Normal.Y = contactArray[0].normal.Y; e.Normal.Z = contactArray[0].normal.Z; e.Depth = contactArray[0].depth; if (handler0 != null) { handler0.HandleCollisionEvent(e); } if (handler1 != null) { // For the other Solid's CollisionEventHandler, we need // to invert the normal and swap the Solid pointers. e.Normal *= -1; e.ThisSolid = solid1; e.OtherSolid = solid0; handler1.HandleCollisionEvent(e); } sim.InternalRecordCollision(e); } if (makeContacts) { // Invalidate the "freely-spinning" parameters. ((OdeSolid)solid0).InternalSetFreelySpinning(false); ((OdeSolid)solid1).InternalSetFreelySpinning(false); for (int i = 0; i < numContacts; ++i) { Material m0 = shape0.Material; Material m1 = shape1.Material; Tao.Ode.Ode.dContact tempContact = new Tao.Ode.Ode.dContact(); tempContact.surface.mode = (int)Tao.Ode.Ode.dContactFlags.dContactBounce | (int)Tao.Ode.Ode.dContactFlags.dContactSoftERP; // Average the hardness of the two materials. float hardness = (m0.Hardness + m1.Hardness) * 0.5f; // Convert hardness to ERP. As hardness goes from // 0.0 to 1.0, ERP goes from min to max. tempContact.surface.soft_erp = hardness * (Defaults.Ode.MaxERP - Defaults.Ode.MinERP) + Defaults.Ode.MinERP; // Don't use contact CFM anymore. Just let it use // the global value set in the ODE world. //tempContact.surface.soft_cfm = // defaults::ode::minCFM; // As friction goes from 0.0 to 1.0, mu goes from 0.0 // to max, though it is set to dInfinity when // friction == 1.0. if (1.0 == m0.Friction && 1.0 == m1.Friction) { tempContact.surface.mu = float.PositiveInfinity; } else { tempContact.surface.mu = (float)Math.Sqrt(m0.Friction * m1.Friction) * Defaults.Ode.MaxFriction; } // Average the bounciness of the two materials. float bounciness = (m0.Bounciness + m1.Bounciness) * 0.5f; // ODE's bounce parameter, a.k.a. restitution. tempContact.surface.bounce = bounciness; // ODE's bounce_vel parameter is a threshold: // the relative velocity of the two objects must be // greater than this for bouncing to occur at all. tempContact.surface.bounce_vel = Defaults.BounceThreshold; tempContact.geom = contactArray[i]; //ODE.Joints.Contact contactJoint = new ODE.Joints.Contact(theWorldID, tempContact, theJointGroupID); IntPtr contactJoint = Tao.Ode.Ode.dJointCreateContact(theWorldID, theJointGroupID, ref tempContact); //contactJoint.Attach(o0BodyID, o1BodyID); Tao.Ode.Ode.dJointAttach(contactJoint, o0BodyID, o1BodyID); } } } }