public void ComputeMass(out MassData md) { // Calculate inertia tensor double ex2 = 4 * e.x * e.x; double ey2 = 4 * e.y * e.y; double ez2 = 4 * e.z * e.z; double mass = 8 * e.x * e.y * e.z * density; double x = 1 / 12.0 * mass * (ey2 + ez2); double y = 1 / 12.0 * mass * (ex2 + ez2); double z = 1 / 12.0 * mass * (ex2 + ey2); Mat3 I = Mat3.Diagonal(x, y, z); // Transform tensor to local space I = local.rotation * I * Mat3.Transpose(local.rotation); I += (Mat3.Identity * Vec3.Dot(local.position, local.position) - Mat3.OuterProduct(local.position, local.position)) * mass; md.center = local.position; md.inertia = I; md.mass = mass; }
// Resources: // http://www.randygaul.net/2014/05/22/deriving-obb-to-obb-intersection-sat/ // https://box2d.googlecode.com/files/GDC2007_ErinCatto.zip // https://box2d.googlecode.com/files/Box2D_Lite.zip public static void BoxtoBox(Manifold m, Box a, Box b) { Transform atx = a.body.GetTransform(); Transform btx = b.body.GetTransform(); Transform aL = a.local; Transform bL = b.local; atx = Transform.Mul(atx, aL); btx = Transform.Mul(btx, bL); Vec3 eA = a.e; Vec3 eB = b.e; // B's frame input A's space Mat3 C = Mat3.Transpose(atx.rotation) * btx.rotation; Mat3 absC = new Mat3(); bool parallel = false; double kCosTol = 1e-6; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { double val = Math.Abs(C[i][j]); var o = absC[i]; o[j] = val; absC[i] = o; if (val + kCosTol >= 1) { parallel = true; } } } // Vector from center A to center B input A's space Vec3 t = Transform.MulT(atx.rotation, btx.position - atx.position); // Query states double s; double aMax = -double.MaxValue; double bMax = -double.MaxValue; double eMax = -double.MaxValue; int aAxis = ~0; int bAxis = ~0; int eAxis = ~0; Vec3 nA = new Vec3(); Vec3 nB = new Vec3(); Vec3 nE = new Vec3(); // Face axis checks // a's x axis s = Math.Abs(t.x) - (eA.x + Vec3.Dot(absC.Column0(), eB)); if (TrackFaceAxis(ref aAxis, 0, s, ref aMax, atx.rotation.ex, ref nA)) { return; } // a's y axis s = Math.Abs(t.y) - (eA.y + Vec3.Dot(absC.Column1(), eB)); if (TrackFaceAxis(ref aAxis, 1, s, ref aMax, atx.rotation.ey, ref nA)) { return; } // a's z axis s = Math.Abs(t.z) - (eA.z + Vec3.Dot(absC.Column2(), eB)); if (TrackFaceAxis(ref aAxis, 2, s, ref aMax, atx.rotation.ez, ref nA)) { return; } // b's x axis s = Math.Abs(Vec3.Dot(t, C.ex)) - (eB.x + Vec3.Dot(absC.ex, eA)); if (TrackFaceAxis(ref bAxis, 3, s, ref bMax, btx.rotation.ex, ref nB)) { return; } // b's y axis s = Math.Abs(Vec3.Dot(t, C.ey)) - (eB.y + Vec3.Dot(absC.ey, eA)); if (TrackFaceAxis(ref bAxis, 4, s, ref bMax, btx.rotation.ey, ref nB)) { return; } // b's z axis s = Math.Abs(Vec3.Dot(t, C.ez)) - (eB.z + Vec3.Dot(absC.ez, eA)); if (TrackFaceAxis(ref bAxis, 5, s, ref bMax, btx.rotation.ez, ref nB)) { return; } if (!parallel) { // Edge axis checks double rA; double rB; // Cross( a.x, b.x ) rA = eA.y * absC[0][2] + eA.z * absC[0][1]; rB = eB.y * absC[2][0] + eB.z * absC[1][0]; s = Math.Abs(t.z * C[0][1] - t.y * C[0][2]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 6, s, ref eMax, new Vec3(0, -C[0][2], C[0][1]), ref nE)) { return; } // Cross( a.x, b.y ) rA = eA.y * absC[1][2] + eA.z * absC[1][1]; rB = eB.x * absC[2][0] + eB.z * absC[0][0]; s = Math.Abs(t.z * C[1][1] - t.y * C[1][2]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 7, s, ref eMax, new Vec3(0, -C[1][2], C[1][1]), ref nE)) { return; } // Cross( a.x, b.z ) rA = eA.y * absC[2][2] + eA.z * absC[2][1]; rB = eB.x * absC[1][0] + eB.y * absC[0][0]; s = Math.Abs(t.z * C[2][1] - t.y * C[2][2]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 8, s, ref eMax, new Vec3(0, -C[2][2], C[2][1]), ref nE)) { return; } // Cross( a.y, b.x ) rA = eA.x * absC[0][2] + eA.z * absC[0][0]; rB = eB.y * absC[2][1] + eB.z * absC[1][1]; s = Math.Abs(t.x * C[0][2] - t.z * C[0][0]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 9, s, ref eMax, new Vec3(C[0][2], 0, -C[0][0]), ref nE)) { return; } // Cross( a.y, b.y ) rA = eA.x * absC[1][2] + eA.z * absC[1][0]; rB = eB.x * absC[2][1] + eB.z * absC[0][1]; s = Math.Abs(t.x * C[1][2] - t.z * C[1][0]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 10, s, ref eMax, new Vec3(C[1][2], 0, -C[1][0]), ref nE)) { return; } // Cross( a.y, b.z ) rA = eA.x * absC[2][2] + eA.z * absC[2][0]; rB = eB.x * absC[1][1] + eB.y * absC[0][1]; s = Math.Abs(t.x * C[2][2] - t.z * C[2][0]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 11, s, ref eMax, new Vec3(C[2][2], 0, -C[2][0]), ref nE)) { return; } // Cross( a.z, b.x ) rA = eA.x * absC[0][1] + eA.y * absC[0][0]; rB = eB.y * absC[2][2] + eB.z * absC[1][2]; s = Math.Abs(t.y * C[0][0] - t.x * C[0][1]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 12, s, ref eMax, new Vec3(-C[0][1], C[0][0], 0), ref nE)) { return; } // Cross( a.z, b.y ) rA = eA.x * absC[1][1] + eA.y * absC[1][0]; rB = eB.x * absC[2][2] + eB.z * absC[0][2]; s = Math.Abs(t.y * C[1][0] - t.x * C[1][1]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 13, s, ref eMax, new Vec3(-C[1][1], C[1][0], 0), ref nE)) { return; } // Cross( a.z, b.z ) rA = eA.x * absC[2][1] + eA.y * absC[2][0]; rB = eB.x * absC[1][2] + eB.y * absC[0][2]; s = Math.Abs(t.y * C[2][0] - t.x * C[2][1]) - (rA + rB); if (TrackEdgeAxis(ref eAxis, 14, s, ref eMax, new Vec3(-C[2][1], C[2][0], 0), ref nE)) { return; } } // Artificial axis bias to improve frame coherence double kRelTol = 0.95; double kAbsTol = 0.01; int axis; double sMax; Vec3 n; double faceMax = Math.Max(aMax, bMax); if (kRelTol * eMax > faceMax + kAbsTol) { axis = eAxis; sMax = eMax; n = nE; } else { if (kRelTol * bMax > aMax + kAbsTol) { axis = bAxis; sMax = bMax; n = nB; } else { axis = aAxis; sMax = aMax; n = nA; } } if (Vec3.Dot(n, btx.position - atx.position) < 0) { n = -n; } if (axis < 6) { Transform rtx; Transform itx; Vec3 eR; Vec3 eI; bool flip; if (axis < 3) { rtx = atx; itx = btx; eR = eA; eI = eB; flip = false; } else { rtx = btx; itx = atx; eR = eB; eI = eA; flip = true; n = -n; } // Compute reference and incident edge information necessary for clipping ComputeIncidentFace(itx, eI, n, incident); Mat3 basis; Vec3 e; ComputeReferenceEdgesAndBasis(eR, rtx, n, axis, clipEdges, out basis, out e); // Clip the incident face against the reference face side planes int resultNum; resultNum = Clip(rtx.position, e, clipEdges, basis, incident, results, depths); if (resultNum != 0) { m.contactCount = resultNum; m.normal = flip ? -n : n; for (int i = 0; i < resultNum; ++i) { Contact c = m.contacts[i]; FeaturePair pair = results[i].f; if (flip) { Swap(ref pair.inI, ref pair.inR); Swap(ref pair.outI, ref pair.outR); } c.fp = results[i].f; c.position = results[i].v; c.penetration = depths[i]; } } } else { n = atx.rotation * n; if (Vec3.Dot(n, btx.position - atx.position) < 0) { n = -n; } Vec3 PA, QA; Vec3 PB, QB; SupportEdge(atx, eA, n, out PA, out QA); SupportEdge(btx, eB, -n, out PB, out QB); Vec3 CA, CB; EdgesContact(out CA, out CB, PA, QA, PB, QB); m.normal = n; m.contactCount = 1; Contact c = m.contacts[0]; FeaturePair pair = new FeaturePair(); pair.key = axis; c.fp = pair; c.penetration = sMax; c.position = (CA + CB) * (0.5); } }
//-------------------------------------------------------------------------------------------------- public static Vec3 MulT(Mat3 r, Vec3 v) { return(Mat3.Transpose(r) * v); }
//-------------------------------------------------------------------------------------------------- public static Vec3 MulT(Transform tx, Vec3 v) { return(Mat3.Transpose(tx.rotation) * (v - tx.position)); }
//-------------------------------------------------------------------------------------------------- public static Mat3 MulT(Mat3 r, Mat3 q) { return(Mat3.Transpose(r) * q); }
public void Solve() { // Apply gravity // Integrate velocities and create state buffers, calculate world inertia for (int i = 0; i < Bodies.Count; ++i) { Body body = Bodies[i]; VelocityState v = Velocities[i]; if ((body.Flags & BodyFlags.eDynamic) > 0) { body.ApplyLinearForce(Gravity * body.GravityScale); // Calculate world space intertia tensor Mat3 r = body.Tx.rotation; body.InvInertiaWorld = r * body.InvInertiaModel * Mat3.Transpose(r); // Integrate velocity body.LinearVelocity += (body.Force * body.InvMass) * Dt; body.AngularVelocity += (body.InvInertiaWorld * body.Torque) * Dt; // From Box2D! // 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) body.LinearVelocity *= 1 / (1 + Dt * body.LinearDamping); body.AngularVelocity *= 1 / (1 + Dt * body.AngularDamping); } Velocities[i] = new VelocityState { v = body.LinearVelocity, w = body.AngularVelocity }; } // Create contact solver, pass in state buffers, create buffers for contacts // Initialize velocity constraint for normal + friction and warm start ContactSolver.Initialize(this); ContactSolver.PreSolve(Dt); // Solve contacts for (int i = 0; i < Iterations; ++i) { ContactSolver.Solve(); } ContactSolver.ShutDown(); // Copy back state buffers // Integrate positions for (int i = 0; i < Bodies.Count; ++i) { Body body = Bodies[i]; VelocityState v = Velocities[i]; if ((body.Flags & BodyFlags.eStatic) > 0) { continue; } body.LinearVelocity = v.v; body.AngularVelocity = v.w; // Integrate position body.WorldCenter += body.LinearVelocity * Dt; body.Q.Integrate(body.AngularVelocity, Dt); body.Q = Quaternion.Normalize(body.Q); body.Tx.rotation = body.Q.ToMat3(); } if (AllowSleep) { // Find minimum sleep time of the entire island double minSleepTime = double.MaxValue; for (int i = 0; i < Bodies.Count; ++i) { Body body = Bodies[i]; if ((body.Flags & BodyFlags.eStatic) > 0) { continue; } double sqrLinVel = Vec3.Dot(body.LinearVelocity, body.LinearVelocity); double cbAngVel = Vec3.Dot(body.AngularVelocity, body.AngularVelocity); double linTol = Q3_SLEEP_LINEAR; double angTol = Q3_SLEEP_ANGULAR; if (sqrLinVel > linTol || cbAngVel > angTol) { minSleepTime = 0; body.SleepTime = 0; } else { body.SleepTime += Dt; minSleepTime = Math.Min(minSleepTime, body.SleepTime); } } // Put entire island to sleep so long as the minimum found sleep time // is below the threshold. If the minimum sleep time reaches below the // sleeping threshold, the entire island will be reformed next step // and sleep test will be tried again. if (minSleepTime > Q3_SLEEP_TIME) { for (int i = 0; i < Bodies.Count; ++i) { Bodies[i].SetToSleep(); } } } }