public override void SolveVelocityConstraints(b2SolverData data) { b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; // Cdot = v + cross(w, r) b2Vec2 Cdot = vB + b2Math.b2Cross(wB, m_rB); b2Vec2 impulse = b2Math.b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse)); b2Vec2 oldImpulse = m_impulse; m_impulse += impulse; float maxImpulse = data.step.dt * m_maxForce; if (m_impulse.LengthSquared() > maxImpulse * maxImpulse) { m_impulse *= maxImpulse / m_impulse.Length(); } impulse = m_impulse - oldImpulse; vB += m_invMassB * impulse; wB += m_invIB * b2Math.b2Cross(m_rB, impulse); data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public override void SolveVelocityConstraints(b2SolverData data) { b2Vec2 vA = data.velocities[m_indexA].v; float wA = data.velocities[m_indexA].w; b2Vec2 vB = data.velocities[m_indexB].v; float wB = data.velocities[m_indexB].w; float mA = m_invMassA, mB = m_invMassB; float iA = m_invIA, iB = m_invIB; float h = data.step.dt; // Solve angular friction { float Cdot = wB - wA; float impulse = -m_angularMass * Cdot; float oldImpulse = m_angularImpulse; float maxImpulse = h * m_maxTorque; m_angularImpulse = b2Math.b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse); impulse = m_angularImpulse - oldImpulse; wA -= iA * impulse; wB += iB * impulse; } // Solve linear friction { b2Vec2 Cdot = vB + b2Math.b2Cross(wB, m_rB) - vA - b2Math.b2Cross(wA, m_rA); b2Vec2 impulse = -b2Math.b2Mul(m_linearMass, Cdot); b2Vec2 oldImpulse = m_linearImpulse; m_linearImpulse += impulse; float maxImpulse = h * m_maxForce; if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) { m_linearImpulse.Normalize(); m_linearImpulse *= maxImpulse; } impulse = m_linearImpulse - oldImpulse; vA -= mA * impulse; wA -= iA * b2Math.b2Cross(m_rA, impulse); vB += mB * impulse; wB += iB * b2Math.b2Cross(m_rB, impulse); } data.velocities[m_indexA].v = vA; data.velocities[m_indexA].w = wA; data.velocities[m_indexB].v = vB; data.velocities[m_indexB].w = wB; }
public virtual void Set(b2Vec2[] vertices, int count) { m_vertexCount = count; // Copy vertices. for (int i = 0; i < m_vertexCount; ++i) { m_vertices[i] = vertices[i]; } // Compute normals. Ensure the edges have non-zero length. for (int i = 0; i < m_vertexCount; ++i) { int i1 = i; int i2 = i + 1 < m_vertexCount ? i + 1 : 0; b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; Debug.Assert(edge.LengthSquared() > b2Settings.b2_epsilon * b2Settings.b2_epsilon); m_normals[i] = b2Math.b2Cross(edge, 1.0f); m_normals[i].Normalize(); } #if DEBUG // Ensure the polygon is convex and the interior // is to the left of each edge. for (int i = 0; i < m_vertexCount; ++i) { int i1 = i; int i2 = i + 1 < m_vertexCount ? i + 1 : 0; b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; for (int j = 0; j < m_vertexCount; ++j) { // Don't check vertices on the current edge. if (j == i1 || j == i2) { continue; } b2Vec2 r = m_vertices[j] - m_vertices[i1]; // If this crashes, your polygon is non-convex, has colinear edges, // or the winding order is wrong. float s = b2Math.b2Cross(edge, r); if (s < 0f) { throw (new InvalidOperationException("ERROR: Please ensure your polygon is convex and has a CCW winding order")); } } } #endif // Compute the polygon centroid. m_centroid = ComputeCentroid(m_vertices, m_vertexCount); }
public static void Distance(b2DistanceOutput output, b2SimplexCache cache, b2DistanceInput input) { ++b2_gjkCalls; b2DistanceProxy proxyA = input.proxyA; b2DistanceProxy proxyB = input.proxyB; b2Transform transformA = input.transformA; b2Transform transformB = input.transformB; // Initialize the simplex b2Simplex simplex = s_simplex; simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB); // Get simplex vertices as an vector. b2SimplexVertex[] vertices = simplex.m_vertices; const int k_maxIters = 20; // These store the vertices of the last simplex so that we // can check for duplicates and preven cycling int[] saveA = s_saveA; int[] saveB = s_saveB; int saveCount = 0; b2Vec2 closestPoint = simplex.GetClosestPoint(); float distanceSqr1 = closestPoint.LengthSquared(); float distanceSqr2 = distanceSqr1; int i; b2Vec2 p; // Main iteration loop int iter = 0; while (iter < k_maxIters) { // Copy the simplex so that we can identify duplicates saveCount = simplex.m_count; for (i = 0; i < saveCount; i++) { saveA[i] = vertices[i].indexA; saveB[i] = vertices[i].indexB; } /*switch(simplex.m_count) * { * case 1: * break; * case 2: * simplex.Solve2(); * break; * case 3: * simplex.Solve3(); * break; * default: * b2Settings.b2Assert(false); * }*/ if (simplex.m_count == 1) { } else if (simplex.m_count == 2) { simplex.Solve2(); } else if (simplex.m_count == 3) { simplex.Solve3(); } else { b2Settings.b2Assert(false); } // If we have 3 points, then the origin is in the corresponding triangle. if (simplex.m_count == 3) { break; } // Compute the closest point. p = simplex.GetClosestPoint(); distanceSqr2 = p.LengthSquared(); // Ensure progress if (distanceSqr2 > distanceSqr1) { //break; } distanceSqr1 = distanceSqr2; // Get search direction. b2Vec2 d = simplex.GetSearchDirection(); // Ensure the search direction is numerically fit. if (d.LengthSquared() < 0.0f /*Number.MinValue * Number.MinValue*/) { // THe origin is probably contained by a line segment or triangle. // Thus the shapes are overlapped. // We can't return zero here even though there may be overlap. // In case the simplex is a point, segment or triangle it is very difficult // to determine if the origin is contained in the CSO or very close to it break; } // Compute a tentative new simplex vertex using support points b2SimplexVertex vertex = vertices[simplex.m_count]; vertex.indexA = (int)proxyA.GetSupport(b2Math.MulTMV(transformA.R, d.GetNegative())); vertex.wA = b2Math.MulX(transformA, proxyA.GetVertex(vertex.indexA)); vertex.indexB = (int)proxyB.GetSupport(b2Math.MulTMV(transformB.R, d)); vertex.wB = b2Math.MulX(transformB, proxyB.GetVertex(vertex.indexB)); vertex.w = b2Math.SubtractVV(vertex.wB, vertex.wA); // Iteration count is equated to the number of support point calls. ++iter; ++b2_gjkIters; // Check for duplicate support points. This is the main termination criteria. bool duplicate = false; for (i = 0; i < saveCount; i++) { if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i]) { duplicate = true; break; } } // If we found a duplicate support point we must exist to avoid cycling if (duplicate) { break; } // New vertex is ok and needed. ++simplex.m_count; } b2_gjkMaxIters = (int)b2Math.Max(b2_gjkMaxIters, iter); // Prepare output simplex.GetWitnessPoints(output.pointA, output.pointB); output.distance = b2Math.SubtractVV(output.pointA, output.pointB).Length(); output.iterations = iter; // Cache the simplex simplex.WriteCache(cache); // Apply radii if requested. if (input.useRadii) { float rA = proxyA.m_radius; float rB = proxyB.m_radius; if (output.distance > rA + rB && output.distance > float.MinValue) { // Shapes are still not overlapped. // Move the witness points to the outer surface. output.distance -= rA + rB; b2Vec2 normal = b2Math.SubtractVV(output.pointB, output.pointA); normal.Normalize(); output.pointA.x += rA * normal.x; output.pointA.y += rA * normal.y; output.pointB.x -= rB * normal.x; output.pointB.y -= rB * normal.y; } else { // Shapes are overlapped when radii are considered. // Move the witness points to the middle. p = new b2Vec2(); p.x = 0.5f * (output.pointA.x + output.pointB.x); p.y = 0.5f * (output.pointA.y + output.pointB.y); output.pointA.x = output.pointB.x = p.x; output.pointA.y = output.pointB.y = p.y; output.distance = 0.0f; } } }
public static void b2Distance(ref b2DistanceOutput output, ref b2SimplexCache cache, ref b2DistanceInput input) { ++b2DistanceProxy.b2_gjkCalls; b2DistanceProxy proxyA = input.proxyA.Copy(); b2DistanceProxy proxyB = input.proxyB.Copy(); b2Transform transformA = input.transformA; b2Transform transformB = input.transformB; // Initialize the simplex. b2Simplex simplex = new b2Simplex(); simplex.ReadCache(ref cache, ref proxyA, ref transformA, ref proxyB, ref transformB); // Get simplex vertices as an array. b2SimplexVertex[] vertices = new b2SimplexVertex[] { simplex.m_vertices[0], simplex.m_vertices[1], simplex.m_vertices[2] }; int k_maxIters = 20; // These store the vertices of the last simplex so that we // can check for duplicates and prevent cycling. int[] saveA = new int[3]; int[] saveB = new int[3]; int saveCount = 0; b2Vec2 closestPoint = simplex.GetClosestPoint(); float distanceSqr1 = closestPoint.LengthSquared(); float distanceSqr2 = distanceSqr1; // Console.WriteLine("Closest Point={0},{1}, distance={2}", closestPoint.x, closestPoint.y, distanceSqr1); // Main iteration loop. #region Main Iteration Loop int iter = 0; while (iter < k_maxIters) { // Copy simplex so we can identify duplicates. saveCount = simplex.m_count; for (int i = 0; i < saveCount; ++i) { saveA[i] = vertices[i].indexA; saveB[i] = vertices[i].indexB; } switch (simplex.m_count) { case 1: break; case 2: simplex.Solve2(); break; case 3: simplex.Solve3(); break; default: Debug.Assert(false); break; } // If we have 3 points, then the origin is in the corresponding triangle. if (simplex.m_count == 3) { break; } // Compute closest point. b2Vec2 p = simplex.GetClosestPoint(); distanceSqr2 = p.LengthSquared(); // Ensure progress if (distanceSqr2 >= distanceSqr1) { //break; } distanceSqr1 = distanceSqr2; // Get search direction. b2Vec2 d = simplex.GetSearchDirection(); // Ensure the search direction is numerically fit. if (d.LengthSquared() < b2Settings.b2_epsilon * b2Settings.b2_epsilon) { // The origin is probably contained by a line segment // or triangle. Thus the shapes are overlapped. // We can't return zero here even though there may be overlap. // In case the simplex is a point, segment, or triangle it is difficult // to determine if the origin is contained in the CSO or very close to it. break; } // Compute a tentative new simplex vertex using support points. b2SimplexVertex vertex = vertices[simplex.m_count]; vertex.indexA = proxyA.GetSupport(b2Math.b2MulT(transformA.q, -d)); vertex.wA = b2Math.b2Mul(transformA, proxyA.GetVertex(vertex.indexA)); // b2Vec2 wBLocal = new b2Vec2(); vertex.indexB = proxyB.GetSupport(b2Math.b2MulT(transformB.q, d)); vertex.wB = b2Math.b2Mul(transformB, proxyB.GetVertex(vertex.indexB)); vertex.w = vertex.wB - vertex.wA; vertices[simplex.m_count] = vertex; // Iteration count is equated to the number of support point calls. ++iter; ++b2DistanceProxy.b2_gjkIters; // Check for duplicate support points. This is the main termination criteria. bool duplicate = false; for (int i = 0; i < saveCount; ++i) { if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i]) { duplicate = true; break; } } // If we found a duplicate support point we must exit to avoid cycling. if (duplicate) { break; } // New vertex is ok and needed. ++simplex.m_count; } #endregion b2DistanceProxy.b2_gjkMaxIters = Math.Max(b2DistanceProxy.b2_gjkMaxIters, iter); // Prepare output. simplex.GetWitnessPoints(ref output.pointA, ref output.pointB); output.distance = b2Math.b2Distance(output.pointA, output.pointB); output.iterations = iter; // Cache the simplex. simplex.WriteCache(ref cache); // Apply radii if requested. if (input.useRadii) { float rA = proxyA.Radius; float rB = proxyB.Radius; if (output.distance > rA + rB && output.distance > b2Settings.b2_epsilon) { // Shapes are still not overlapped. // Move the witness points to the outer surface. output.distance -= rA + rB; b2Vec2 normal = output.pointB - output.pointA; normal.Normalize(); output.pointA += rA * normal; output.pointB -= rB * normal; } else { // Shapes are overlapped when radii are considered. // Move the witness points to the middle. b2Vec2 p = 0.5f * (output.pointA + output.pointB); output.pointA = p; output.pointB = p; output.distance = 0.0f; } } // Copy back the vertex changes because they are structs, but in C++ land they are reference values simplex.m_vertices[0] = vertices[0]; simplex.m_vertices[1] = vertices[1]; simplex.m_vertices[2] = vertices[2]; }
/// Create a convex hull from the given array of local points. /// The count must be in the range [3, b2_maxPolygonVertices]. /// @warning the points may be re-ordered, even if they form a convex polygon /// @warning collinear points are handled but not removed. Collinear points /// may lead to poor stacking behavior. public b2PolygonShape Set(b2Vec2[] vertices) { Debug.Assert(3 <= vertices.Length && vertices.Length <= Settings.b2_maxPolygonVertices); if (vertices.Length < 3) { SetAsBox(1.0f, 1.0f); return(this); } int n = Utils.b2Min(vertices.Length, Settings.b2_maxPolygonVertices); // Perform welding and copy vertices into local buffer. b2Vec2[] ps = Arrays.InitializeWithDefaultInstances <b2Vec2>(Settings.b2_maxPolygonVertices); int tempCount = 0; for (int i = 0; i < n; ++i) { b2Vec2 v = vertices[i]; bool unique = true; for (int j = 0; j < tempCount; ++j) { if (Utils.b2DistanceSquared(v, ps[j]) < ((0.5f * Settings.b2_linearSlop) * (0.5f * Settings.b2_linearSlop))) { unique = false; break; } } if (unique) { ps[tempCount++] = v; } } n = tempCount; if (n < 3) { // Polygon is degenerate. Debug.Assert(false); SetAsBox(1.0f, 1.0f); return(this); } // Create the convex hull using the Gift wrapping algorithm // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm // Find the right most point on the hull int i0 = 0; float x0 = ps[0].x; for (int i = 1; i < n; ++i) { float x = ps[i].x; if (x > x0 || (x == x0 && ps[i].y < ps[i0].y)) { i0 = i; x0 = x; } } int[] hull = new int[Settings.b2_maxPolygonVertices]; int m = 0; int ih = i0; for (;;) { Debug.Assert(m < Settings.b2_maxPolygonVertices); hull[m] = ih; int ie = 0; for (int j = 1; j < n; ++j) { if (ie == ih) { ie = j; continue; } b2Vec2 r = ps[ie] - ps[hull[m]]; b2Vec2 v = ps[j] - ps[hull[m]]; float c = Utils.b2Cross(r, v); if (c < 0.0f) { ie = j; } // Collinearity check if (c == 0.0f && v.LengthSquared() > r.LengthSquared()) { ie = j; } } ++m; ih = ie; if (ie == i0) { break; } } if (m < 3) { // Polygon is degenerate. Debug.Assert(false); SetAsBox(1.0f, 1.0f); return(this); } m_count = m; // Copy vertices. for (int i = 0; i < m; ++i) { m_vertices[i] = ps[hull[i]]; } // Compute normals. Ensure the edges have non-zero length. for (int i = 0; i < m; ++i) { int i1 = i; int i2 = i + 1 < m ? i + 1 : 0; b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; Debug.Assert(edge.LengthSquared() > float.Epsilon * float.Epsilon); m_normals[i] = Utils.b2Cross(edge, 1.0f); m_normals[i].Normalize(); } // Compute the polygon centroid. m_centroid = Utils.ComputeCentroid(m_vertices, m); return(this); }
private void SolveC3() { int count3 = m_count - 2; for (int i = 0; i < count3; ++i) { b2Vec2 p1 = m_ps[i]; b2Vec2 p2 = m_ps[i + 1]; b2Vec2 p3 = m_ps[i + 2]; float m1 = m_ims[i]; float m2 = m_ims[i + 1]; float m3 = m_ims[i + 2]; b2Vec2 d1 = p2 - p1; b2Vec2 d2 = p3 - p2; float L1sqr = d1.LengthSquared(); float L2sqr = d2.LengthSquared(); if (L1sqr * L2sqr == 0.0f) { continue; } float a = b2Math.b2Cross(d1, d2); float b = b2Math.b2Dot(d1, d2); float angle = b2Math.b2Atan2(a, b); b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew(); b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew(); b2Vec2 J1 = -Jd1; b2Vec2 J2 = Jd1 - Jd2; b2Vec2 J3 = Jd2; float mass = m1 * b2Math.b2Dot(J1, J1) + m2 * b2Math.b2Dot(J2, J2) + m3 * b2Math.b2Dot(J3, J3); if (mass == 0.0f) { continue; } mass = 1.0f / mass; float C = angle - m_as[i]; while (C > b2Settings.b2_pi) { angle -= 2f * (float)Math.PI; C = angle - m_as[i]; } while (C < -(float)Math.PI) { angle += 2.0f * (float)Math.PI; C = angle - m_as[i]; } float impulse = -m_k3 * mass * C; p1 += (m1 * impulse) * J1; p2 += (m2 * impulse) * J2; p3 += (m3 * impulse) * J3; m_ps[i] = p1; m_ps[i + 1] = p2; m_ps[i + 2] = p3; } }
/** * Copy vertices. This assumes the vertices define a convex polygon. * It is assumed that the exterior is the the right of each edge. */ public void SetAsVector(List <b2Vec2> vertices, int vertexCount = 0) { if (vertexCount == 0) { vertexCount = vertices.Count; } b2Settings.b2Assert(2 <= vertexCount); m_vertexCount = vertexCount; Reserve(vertexCount); int i; // Copy vertices for (i = 0; i < m_vertexCount; i++) { b2Vec2 v = vertices[i]; m_vertices[(m_vertexCount - 1) - i].SetV(v); //改为逆时针顺序添加 /*if(Application.platform==RuntimePlatform.FlashPlayer){ * v.y=-v.y; * m_vertices[i].SetV(v); * }else{ * m_vertices[(m_vertexCount-1)-i].SetV(v);//改为逆时针顺序添加 * }*/ } // Compute normals. Ensure the edges have non-zero length. for (i = 0; i < m_vertexCount; ++i) { int i1 = i; int i2 = i + 1 < m_vertexCount ? i + 1 : 0; b2Vec2 edge = b2Math.SubtractVV(m_vertices[i2], m_vertices[i1]); b2Settings.b2Assert(edge.LengthSquared() > float.MinValue /* * Number.MIN_VALUE*/); m_normals[i].SetV(b2Math.CrossVF(edge, 1.0f)); m_normals[i].Normalize(); } //#ifdef _DEBUG // Ensure the polygon is convex and the interior // is to the left of each edge. //for (int32 i = 0; i < m_vertexCount; ++i) //{ //int32 i1 = i; //int32 i2 = i + 1 < m_vertexCount ? i + 1 : 0; //b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; //for (int32 j = 0; j < m_vertexCount; ++j) //{ // Don't check vertices on the current edge. //if (j == i1 || j == i2) //{ //continue; //} // //b2Vec2 r = m_vertices[j] - m_vertices[i1]; // Your polygon is non-convex (it has an indentation) or // has colinear edges. //float32 s = b2Cross(edge, r); //b2Assert(s > 0.0f); //} //} //#endif // Compute the polygon centroid m_centroid = ComputeCentroid(m_vertices, (uint)m_vertexCount); }
/// Ray-cast against the proxies in the tree. This relies on the callback /// to perform a exact ray-cast in the case were the proxy contains a shape. /// The callback also performs the any collision filtering. This has performance /// roughly equal to k * log(n), where k is the number of collisions and n is the /// number of proxies in the tree. /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). /// @param callback a callback class that is called for each proxy that is hit by the ray. public void RayCast(b2BroadphaseRayCastCallback callback, b2RayCastInput input) { b2Vec2 p1 = new b2Vec2(input.p1); b2Vec2 p2 = new b2Vec2(input.p2); b2Vec2 r = p2 - p1; Debug.Assert(r.LengthSquared() > 0.0f); r.Normalize(); // v is perpendicular to the segment. b2Vec2 v = Utils.b2Cross(1.0f, r); b2Vec2 abs_v = Utils.b2Abs(v); // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) float maxFraction = input.maxFraction; // Build a bounding box for the segment. b2AABB segmentAABB = new b2AABB(); { b2Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.lowerBound = Utils.b2Min(p1, t); segmentAABB.upperBound = Utils.b2Max(p1, t); } Stack <int> stack = new Stack <int>(256); stack.Push(m_root); while (stack.Count > 0) { int nodeId = stack.Pop(); if (nodeId == Settings.b2_nullNode) { continue; } b2TreeNode node = m_nodes[nodeId]; if (Utils.b2TestOverlap(ref node.aabb, ref segmentAABB) == false) { continue; } // Separating axis for segment (Gino, p80). // |dot(v, p1 - c)| > dot(|v|, h) b2Vec2 c = node.aabb.GetCenter(); b2Vec2 h = node.aabb.GetExtents(); float separation = Utils.b2Abs(Utils.b2Dot(v, p1 - c)) - Utils.b2Dot(abs_v, h); if (separation > 0.0f) { continue; } if (node.IsLeaf()) { b2RayCastInput subInput = new b2RayCastInput(); subInput.p1 = input.p1; subInput.p2 = input.p2; subInput.maxFraction = maxFraction; float value = callback(subInput, nodeId); if (value == 0.0f) { // The client has terminated the ray cast. return; } if (value > 0.0f) { // Update segment bounding box. maxFraction = value; b2Vec2 t = p1 + maxFraction * (p2 - p1); segmentAABB.lowerBound = Utils.b2Min(p1, t); segmentAABB.upperBound = Utils.b2Max(p1, t); } } else { stack.Push(node.child1); stack.Push(node.child2); } } }