/// <summary> /// Creates a breakable body. You would want to remove collinear points before using this. /// </summary> /// <param name="world">The world.</param> /// <param name="vertices">The vertices.</param> /// <param name="density">The density.</param> /// <param name="position">The position.</param> /// <returns></returns> public static BreakableBody CreateBreakableBody(World world, Vertices vertices, float density, Vector2 position, object userData) { List <Vertices> triangles = EarclipDecomposer.ConvexPartition(vertices); BreakableBody breakableBody = new BreakableBody(triangles, world, density, userData); breakableBody.MainBody.Position = position; world.AddBreakableBody(breakableBody); return(breakableBody); }
public static List <Fixture> AttachSolidArc(float density, float radians, int sides, float radius, Vector2 position, float angle, Body body) { Vertices arc = PolygonTools.CreateArc(radians, sides, radius); arc.Rotate((MathHelper.Pi - radians) / 2 + angle); arc.Translate(ref position); //Close the arc arc.Add(arc[0]); List <Vertices> triangles = EarclipDecomposer.ConvexPartition(arc); return(AttachCompoundPolygon(triangles, density, body)); }
public static TexVertOutput TexToVert(World world, Texture2D texture, float mass, bool useCentroid, float scale) { Vertices verts; TexVertOutput output = new TexVertOutput(); // Creates an array for every pixel in the texture uint[] data = new uint[texture.Width * texture.Height]; texture.GetData(data); verts = PolygonTools.CreatePolygon(data, texture.Width, false); Vector2 centroid = Vector2.Zero; // Origin needs to be altered so it uses the origin of the verts // rather than the texture's centre. if (useCentroid) { centroid = -verts.GetCentroid(); verts.Translate(ref centroid); } else { centroid = ConvertUnits.ToSimUnits(new Vector2(texture.Width, texture.Height) * 0.5f); } float simScale = ConvertUnits.ToSimUnits(scale); Vector2 Scale = new Vector2(simScale, simScale); verts.Scale(ref Scale); verts = SimplifyTools.ReduceByDistance(verts, ConvertUnits.ToSimUnits(4f)); Body body = BodyFactory.CreateCompoundPolygon(world, EarclipDecomposer.ConvexPartition(verts), mass); body.BodyType = BodyType.Dynamic; if (!useCentroid) { body.LocalCenter = centroid; } output.Body = body; output.Origin = ConvertUnits.ToDisplayUnits(centroid); return(output); }
public void NextStep(Vector2 position) { switch (_step) { case 0: _finished = false; _statusMessage = "Attaching fixture to body. Choose body..."; _step++; break; case 1: _foundBody = CommonHelpers.FindBody(position, _world); if (_foundBody == null) { _statusMessage = "Cant find body in this position. Choose Body..."; break; } _statusMessage = "Body has been selected. Choose fixture position..."; _step++; break; case 2: Vector2 offset = CommonHelpers.CalculateLocalPoint(position, _foundBody); Vertices _tempShapeVertices = new Vertices(_initialShapeVertices); _tempShapeVertices.Rotate(-_foundBody.Rotation); _tempShapeVertices.Rotate(_prototypeBody.Rotation); _tempShapeVertices.Translate(ref offset); List <Vertices> decomposedVerts = EarclipDecomposer.ConvexPartition(_tempShapeVertices); _resultShapeList = new List <Shape>(decomposedVerts.Count); foreach (Vertices vertices in decomposedVerts) { if (vertices.Count == 2) { _resultShapeList.Add(new EdgeShape(vertices[0], vertices[1])); } else { _resultShapeList.Add(new PolygonShape(vertices, _prototypeBody.Density == null ? 1f : (float)_prototypeBody.Density)); } } _statusMessage = "Fixture has been created."; _finished = true; _step = 0; break; } }
public static Body CreateGear(World world, float radius, int numberOfTeeth, float tipPercentage, float toothHeight, float density, object userData) { Vertices gearPolygon = PolygonTools.CreateGear(radius, numberOfTeeth, tipPercentage, toothHeight); //Gears can in some cases be convex if (!gearPolygon.IsConvex()) { //Decompose the gear: List <Vertices> list = EarclipDecomposer.ConvexPartition(gearPolygon); return(CreateCompoundPolygon(world, list, density, userData)); } return(CreatePolygon(world, gearPolygon, density, userData)); }
/// <summary> /// Creates a rounded rectangle. /// Note: Automatically decomposes the capsule if it contains too many vertices (controlled by Settings.MaxPolygonVertices) /// </summary> /// <param name="world">The world.</param> /// <param name="width">The width.</param> /// <param name="height">The height.</param> /// <param name="xRadius">The x radius.</param> /// <param name="yRadius">The y radius.</param> /// <param name="segments">The segments.</param> /// <param name="density">The density.</param> /// <param name="position">The position.</param> /// <returns></returns> public static Body CreateRoundedRectangle(World world, float width, float height, float xRadius, float yRadius, int segments, float density, Vector2 position, object userData) { Vertices verts = PolygonTools.CreateRoundedRectangle(width, height, xRadius, yRadius, segments); //There are too many vertices in the capsule. We decompose it. if (verts.Count >= Settings.MaxPolygonVertices) { List <Vertices> vertList = EarclipDecomposer.ConvexPartition(verts); Body body = CreateCompoundPolygon(world, vertList, density, userData); body.Position = position; return(body); } return(CreatePolygon(world, verts, density)); }
/// <summary> /// Convert a closed path into a polygon. /// Convex decomposition is automatically performed. /// </summary> /// <param name="path">The path.</param> /// <param name="body">The body.</param> /// <param name="density">The density.</param> /// <param name="subdivisions">The subdivisions.</param> public static void ConvertPathToPolygon(Path path, FSBody body, float density, int subdivisions) { if (!path.Closed) { throw new Exception("The path must be closed to convert to a polygon."); } List <FVector2> verts = path.GetVertices(subdivisions); List <Vertices> decomposedVerts = EarclipDecomposer.ConvexPartition(new Vertices(verts)); //List<Vertices> decomposedVerts = BayazitDecomposer.ConvexPartition(new Vertices(verts)); foreach (Vertices item in decomposedVerts) { body.CreateFixture(new PolygonShape(item, density)); } }
public void AddShadowForObject(Texture2D texture, Vector2 position) { uint[] data = new uint[texture.Width * texture.Height]; texture.GetData <uint>(data); foreach (var poly in EarclipDecomposer.ConvexPartition(PolygonTools.CreatePolygon(data, texture.Width))) { if (poly.Count >= 3) { var array = poly.ToArray(); var hull = ShadowHull.CreateConvex(ref array); hull.Position = position; _krypton.Hulls.Add(hull); } } }
protected override MeshCollider Deserialize(IntermediateReader input, Microsoft.Xna.Framework.Content.ContentSerializerAttribute format, MeshCollider existingInstance) { MeshCollider collider = new MeshCollider(); ContentSerializerAttribute attr = new ContentSerializerAttribute(); attr.ElementName = "id"; // SET ID int id = input.ReadObject <int>(attr); Type type = typeof(UnityObject); FieldInfo fld = type.GetField("_id", BindingFlags.Instance | BindingFlags.NonPublic); fld.SetValue(collider, id); attr.ElementName = "isTrigger"; collider.isTrigger = input.ReadObject <bool>(attr); attr.ElementName = "triangles"; short[] triangles = input.ReadObject <short[]>(attr); attr.ElementName = "vertices"; Microsoft.Xna.Framework.Vector3[] vertices = input.ReadObject <Microsoft.Xna.Framework.Vector3[]>(attr); List <Triangle> tris = new List <Triangle>(); for (int i = 0; i < triangles.Length; i += 3) { if (vertices[triangles[i]].Y + vertices[triangles[i + 1]].Y + vertices[triangles[i + 2]].Y > 1) { Debug.Log(" Warning: " + ToString() + " has non zero Y in collider"); } Triangle tri = new Triangle( vertices[triangles[i]].X, vertices[triangles[i]].Z, vertices[triangles[i + 2]].X, vertices[triangles[i + 2]].Z, vertices[triangles[i + 1]].X, vertices[triangles[i + 1]].Z ); tris.Add(tri); } collider.vertices = EarclipDecomposer.PolygonizeTriangles(tris, int.MaxValue, 0); return(collider); }
public static List <Fixture> CreateGear(World world, float radius, int numberOfTeeth, float tipPercentage, float toothHeight, float density) { Vertices gearPolygon = PolygonTools.CreateGear(radius, numberOfTeeth, tipPercentage, toothHeight); //Gears can in some cases be convex if (!gearPolygon.IsConvex()) { //Decompose the gear: List <Vertices> list = EarclipDecomposer.ConvexPartition(gearPolygon); return(CreateCompoundPolygon(world, list, density)); } List <Fixture> fixtures = new List <Fixture>(); fixtures.Add(CreatePolygon(world, gearPolygon, density)); return(fixtures); }
/// <summary> /// Creates a rounded rectangle. /// Note: Automatically decomposes the capsule if it contains too many vertices (controlled by Settings.MaxPolygonVertices) /// </summary> /// <param name="world">The world.</param> /// <param name="width">The width.</param> /// <param name="height">The height.</param> /// <param name="xRadius">The x radius.</param> /// <param name="yRadius">The y radius.</param> /// <param name="segments">The segments.</param> /// <param name="density">The density.</param> /// <param name="position">The position.</param> /// <returns></returns> public static List <Fixture> CreateRoundedRectangle(World world, float width, float height, float xRadius, float yRadius, int segments, float density, Vector2 position) { Vertices verts = PolygonTools.CreateRoundedRectangle(width, height, xRadius, yRadius, segments); //There are too many vertices in the capsule. We decompose it. if (verts.Count >= Settings.MaxPolygonVertices) { List <Vertices> vertList = EarclipDecomposer.ConvexPartition(verts); List <Fixture> fixtureList = CreateCompoundPolygon(world, vertList, density); fixtureList[0].Body.Position = position; return(fixtureList); } return(new List <Fixture> { CreatePolygon(world, verts, density) }); }
public override void LoadContent() { if (!isInitialized) { polygons = new List <Vector2[]>(); vertices = new List <VertexPositionColor[]>(); Vertices tempVertices = new Vertices(WorldPoints); List <Vertices> tempVerticesList = EarclipDecomposer.ConvexPartition(tempVertices); int index = 0; foreach (Vertices v in tempVerticesList) { polygons.Add(new Vector2[v.Count]); for (int i = 0; i < polygons[index].Length; i++) { polygons.ElementAt(index)[i] = v.ElementAt(i); } index++; } for (int i = 0; i < polygons.Count; i++) { vertices.Add(new VertexPositionColor[polygons[i].Length * 3]); for (int i2 = 1; i2 < polygons[i].Length - 1; i2++) { vertices.ElementAt(i)[(i2 - 1) * 3].Position = new Vector3(polygons.ElementAt(i)[0], 0.0f); vertices.ElementAt(i)[(i2 - 1) * 3].Color = color; vertices.ElementAt(i)[(i2 - 1) * 3 + 1].Position = new Vector3(polygons.ElementAt(i)[i2], 0.0f); vertices.ElementAt(i)[(i2 - 1) * 3 + 1].Color = color; vertices.ElementAt(i)[(i2 - 1) * 3 + 2].Position = new Vector3(polygons.ElementAt(i)[i2 + 1], 0.0f); vertices.ElementAt(i)[(i2 - 1) * 3 + 2].Color = color; } } isInitialized = true; } }
/// <summary> /// Creates a capsule. /// Note: Automatically decomposes the capsule if it contains too many vertices (controlled by Settings.MaxPolygonVertices) /// </summary> /// <param name="world">The world.</param> /// <param name="height">The height.</param> /// <param name="topRadius">The top radius.</param> /// <param name="topEdges">The top edges.</param> /// <param name="bottomRadius">The bottom radius.</param> /// <param name="bottomEdges">The bottom edges.</param> /// <param name="density">The density.</param> /// <param name="position">The position.</param> /// <returns></returns> public static List <Fixture> CreateCapsule(World world, float height, float topRadius, int topEdges, float bottomRadius, int bottomEdges, float density, Vector2 position) { Vertices verts = PolygonTools.CreateCapsule(height, topRadius, topEdges, bottomRadius, bottomEdges); //There are too many vertices in the capsule. We decompose it. if (verts.Count >= Settings.MaxPolygonVertices) { List <Vertices> vertList = EarclipDecomposer.ConvexPartition(verts); List <Fixture> fixtureList = CreateCompoundPolygon(world, vertList, density); fixtureList[0].Body.Position = position; return(fixtureList); } return(new List <Fixture> { CreatePolygon(world, verts, density) }); }
private void MakePolygon() { Vector2 avgLoc = new Vector2(); foreach (Vector2 vert in polyPoints) { avgLoc += vert; } avgLoc /= polyPoints.Count; Vertices verts = new Vertices(); foreach (Vector2 v in polyPoints) { verts.Add(v - avgLoc); } Body b = new Body(game.farseerManager.world); List <Fixture> composition = FixtureFactory.AttachCompoundPolygon(EarclipDecomposer.ConvexPartition(verts), 1, b); b.Position = avgLoc; foreach (Fixture triangle in composition) { FarseerTextures.ApplyTexture(triangle, FarseerTextures.TextureType.Normal); } if (composition.Count > 0) { FormManager.Property.setPendingObjects(new List <object>() { composition[0].Body }); } polyPoints.Clear(); }
private void UpdatePolys() { World.Clear(); _sw.Start(); _aabb = new AABB { LowerBound = new Vector2(0, 0), UpperBound = new Vector2(_terrainTex.Width, _terrainTex.Height), }; _polys = MarchingSquares.DetectSquares(_aabb, _gridSize, _gridSize, Eval, _level, _combine); _sw.Stop(); _time = _sw.Elapsed.TotalMilliseconds; _sw.Reset(); for (int i = 0; i < _polys.Count; i++) { Vertices poly = _polys[i]; poly.Translate(ref _translate); poly.Scale(ref _scale); poly.ForceCounterClockWise(); if (!poly.IsConvex()) { List <Vertices> verts = EarclipDecomposer.ConvexPartition(poly); for (int j = 0; j < verts.Count; j++) { Vertices v = verts[j]; FixtureFactory.CreatePolygon(World, v, 1); } } else { FixtureFactory.CreatePolygon(World, poly, 1); } } }
/// <summary> /// Creates a capsule. /// Note: Automatically decomposes the capsule if it contains too many vertices (controlled by Settings.MaxPolygonVertices) /// </summary> /// <param name="world">The world.</param> /// <param name="height">The height.</param> /// <param name="topRadius">The top radius.</param> /// <param name="topEdges">The top edges.</param> /// <param name="bottomRadius">The bottom radius.</param> /// <param name="bottomEdges">The bottom edges.</param> /// <param name="density">The density.</param> /// <param name="position">The position.</param> /// <returns></returns> public static Body CreateCapsule(World world, float height, float topRadius, int topEdges, float bottomRadius, int bottomEdges, float density, Vector2 position, object userData) { Vertices verts = PolygonTools.CreateCapsule(height, topRadius, topEdges, bottomRadius, bottomEdges); Body body; //There are too many vertices in the capsule. We decompose it. if (verts.Count >= Settings.MaxPolygonVertices) { List <Vertices> vertList = EarclipDecomposer.ConvexPartition(verts); body = CreateCompoundPolygon(world, vertList, density, userData); body.Position = position; return(body); } body = CreatePolygon(world, verts, density, userData); body.Position = position; return(body); }
public Texture2D TextureFromVertices(Vertices vertices, MaterialType type, Color color, float materialScale) { // copy vertices Vertices verts = new Vertices(vertices); // scale to display units (i.e. pixels) for rendering to texture Vector2 scale = ConvertUnits.ToDisplayUnits(Vector2.One); verts.Scale(ref scale); // translate the boundingbox center to the texture center // because we use an orthographic projection for rendering later AABB vertsBounds = verts.GetCollisionBox(); verts.Translate(-vertsBounds.Center); List <Vertices> decomposedVerts; if (!verts.IsConvex()) { decomposedVerts = EarclipDecomposer.ConvexPartition(verts); } else { decomposedVerts = new List <Vertices>(); decomposedVerts.Add(verts); } List <VertexPositionColorTexture[]> verticesFill = new List <VertexPositionColorTexture[]>(decomposedVerts.Count); materialScale /= _materials[type].Width; for (int i = 0; i < decomposedVerts.Count; ++i) { verticesFill.Add(new VertexPositionColorTexture[3 * (decomposedVerts[i].Count - 2)]); for (int j = 0; j < decomposedVerts[i].Count - 2; ++j) { // fill vertices verticesFill[i][3 * j].Position = new Vector3(decomposedVerts[i][0], 0f); verticesFill[i][3 * j + 1].Position = new Vector3(decomposedVerts[i].NextVertex(j), 0f); verticesFill[i][3 * j + 2].Position = new Vector3(decomposedVerts[i].NextVertex(j + 1), 0f); verticesFill[i][3 * j].TextureCoordinate = decomposedVerts[i][0] * materialScale; verticesFill[i][3 * j + 1].TextureCoordinate = decomposedVerts[i].NextVertex(j) * materialScale; verticesFill[i][3 * j + 2].TextureCoordinate = decomposedVerts[i].NextVertex(j + 1) * materialScale; verticesFill[i][3 * j].Color = verticesFill[i][3 * j + 1].Color = verticesFill[i][3 * j + 2].Color = color; } } // calculate outline VertexPositionColor[] verticesOutline = new VertexPositionColor[2 * verts.Count]; for (int i = 0; i < verts.Count; ++i) { verticesOutline[2 * i].Position = new Vector3(verts[i], 0f); verticesOutline[2 * i + 1].Position = new Vector3(verts.NextVertex(i), 0f); verticesOutline[2 * i].Color = verticesOutline[2 * i + 1].Color = Color.Black; } Vector2 vertsSize = new Vector2(vertsBounds.UpperBound.X - vertsBounds.LowerBound.X, vertsBounds.UpperBound.Y - vertsBounds.LowerBound.Y); return(RenderTexture((int)vertsSize.X, (int)vertsSize.Y, _materials[type], verticesFill, verticesOutline)); }
public static Texture2D PolygonTexture(Vertices vertices, string pattern, Color mainColor, Color patternColor, Color outlineColor, float materialScale) { if (_assetCreator != null) { if (!_materials.ContainsKey(pattern)) { pattern = "blank"; } // copy vertices Vertices scaledVertices = new Vertices(vertices); // scale to display units (i.e. pixels) for rendering to texture Vector2 scale = ConvertUnits.ToDisplayUnits(Vector2.One); scaledVertices.Scale(ref scale); // translate the boundingbox center to the texture center // because we use an orthographic projection for rendering later AABB verticesBounds = scaledVertices.GetCollisionBox(); scaledVertices.Translate(-verticesBounds.Center); List <Vertices> decomposedVertices; if (!scaledVertices.IsConvex()) { decomposedVertices = EarclipDecomposer.ConvexPartition(scaledVertices); } else { decomposedVertices = new List <Vertices>(); decomposedVertices.Add(scaledVertices); } List <VertexPositionColorTexture[]> verticesFill = new List <VertexPositionColorTexture[]>(decomposedVertices.Count); materialScale /= _materials[pattern].Width; for (int i = 0; i < decomposedVertices.Count; i++) { verticesFill.Add(new VertexPositionColorTexture[3 * (decomposedVertices[i].Count - 2)]); for (int j = 0; j < decomposedVertices[i].Count - 2; j++) { // fill vertices verticesFill[i][3 * j].Position = new Vector3(decomposedVertices[i][0], 0f); verticesFill[i][3 * j + 1].Position = new Vector3(decomposedVertices[i].NextVertex(j), 0f); verticesFill[i][3 * j + 2].Position = new Vector3(decomposedVertices[i].NextVertex(j + 1), 0f); verticesFill[i][3 * j].TextureCoordinate = decomposedVertices[i][0] * materialScale; verticesFill[i][3 * j + 1].TextureCoordinate = decomposedVertices[i].NextVertex(j) * materialScale; verticesFill[i][3 * j + 2].TextureCoordinate = decomposedVertices[i].NextVertex(j + 1) * materialScale; verticesFill[i][3 * j].Color = verticesFill[i][3 * j + 1].Color = verticesFill[i][3 * j + 2].Color = mainColor; } } // calculate outline VertexPositionColor[] verticesOutline = new VertexPositionColor[2 * scaledVertices.Count]; for (int i = 0; i < scaledVertices.Count; i++) { verticesOutline[2 * i].Position = new Vector3(scaledVertices[i], 0f); verticesOutline[2 * i + 1].Position = new Vector3(scaledVertices.NextVertex(i), 0f); verticesOutline[2 * i].Color = verticesOutline[2 * i + 1].Color = outlineColor; } Vector2 vertsSize = new Vector2(verticesBounds.UpperBound.X - verticesBounds.LowerBound.X, verticesBounds.UpperBound.Y - verticesBounds.LowerBound.Y); if (pattern == "blank") { return(_assetCreator.RenderTexture((int)vertsSize.X, (int)vertsSize.Y, null, Color.Transparent, verticesFill, verticesOutline)); } else { return(_assetCreator.RenderTexture((int)vertsSize.X, (int)vertsSize.Y, _materials[pattern], patternColor, verticesFill, verticesOutline)); } } return(null); }
public static void ComputeProperties(this Rigidbody2D body, IList <Collider2D> shapes, ref MassData[] massData) { var totalMass = body.mass; var totalArea = 0.0f; // Calculate initial properties for (var i = 0; i < massData.Length; i++) { var shape = shapes[i]; massData[i].shape = shape; var xf = shape.transform; massData[i].xf = xf; Vector2 scale = xf.lossyScale; scale.Set(Mathf.Abs(scale.x), Mathf.Abs(scale.y)); if (!shape.enabled) { massData[i].area = 0; continue; } if (shape is CircleCollider2D) { var circle = shape as CircleCollider2D; var rad = circle.radius * Mathf.Max(scale.x, scale.y); float area = Mathf.PI * rad * rad; massData[i].area = area; massData[i].centroid = circle.offset; totalArea += area; } else if (shape is BoxCollider2D) { var box = shape as BoxCollider2D; var area = 0f; var I = 0.0f; var center = Vector2.zero; var s = Vector2.zero; const int l = 4; var points = new Vector2[4]; var ex = Vector2.Scale(box.size, scale) / 2f; points[0] = box.offset + ex; points[1] = box.offset + new Vector2(-ex.x, ex.y); points[2] = box.offset - ex; points[3] = box.offset + new Vector2(ex.x, -ex.y); massData[i].points = points; massData[i].depths = new float[4]; // This code would put the reference point inside the polygon for (var j = 0; j < l; ++j) { s += points[i]; } s *= 1.0f / l; const float k_inv3 = 1.0f / 3.0f; for (var j = 0; j < l; ++j) { // Triangle vertices var e1 = points[j] - s; var e2 = j + 1 < l ? points[j + 1] - s : points[0] - s; var D = Cross(e1, e2); float triangleArea = 0.5f * D; area += triangleArea; // Area weighted centroid center += triangleArea * k_inv3 * (e1 + e2); float ex1 = e1.x, ey1 = e1.y; float ex2 = e2.x, ey2 = e2.y; var intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2; var inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2; I += 0.25f * k_inv3 * D * (intx2 + inty2); } // Area massData[i].area = area; totalArea += area; // Center of mass center *= 1.0f / area; massData[i].centroid = center + s; // For inertia calc massData[i].center = center; massData[i].i = I; } else if (shape is EdgeCollider2D) { // Edges have no area, therefore they don't have mass or // contribute to center of mass massData[i].area = 0; } else if (shape is PolygonCollider2D) { var poly = shape as PolygonCollider2D; var pathL = poly.pathCount; for (int k = 0; k < pathL; ++k) { var decomposed = EarclipDecomposer.ConvexPartition(poly.GetPath(k)); var l = decomposed.Count; // Resize array System.Array.Resize(ref massData, massData.Length + (l - 1)); // Compute each path as a seperate set of mass data for (var j = 0; j < l; ++j) { if (decomposed[j].Count < 3) { // Degenerate case continue; } ComputePropertiesPoly(poly, decomposed[j], massData, scale, j, i, ref totalArea); } // Increase iterator i += l - 1; } } } for (var i = 0; i < massData.Length; i++) { var shape = massData[i].shape; if (!shape || !shape.enabled) { massData[i].mass = 0; massData[i].inertia = 0; continue; } massData[i].mass = (massData[i].area * totalMass) / totalArea; if (shape is CircleCollider2D) { var circle = shape as CircleCollider2D; // inertia about the local origin massData[i].inertia = massData[i].mass * (0.5f * circle.radius * circle.radius + Vector2.Dot(circle.offset, circle.offset)); } else if (shape is BoxCollider2D || shape is PolygonCollider2D) { var density = massData[i].mass / massData[i].area; // Inertia tensor relative to the local origin (point s). massData[i].inertia = density * massData[i].i; // Shift to center of mass then to original body origin. massData[i].inertia += massData[i].mass * (Vector2.Dot(massData[i].centroid, massData[i].centroid) - Vector2.Dot(massData[i].center, massData[i].center)); } } }
public static List <Vertices> ConvexPartition(Vertices vertices, TriangulationAlgorithm algorithm, bool discardAndFixInvalid = true, float tolerance = 0.001f) { if (vertices.Count <= 3) { return new List <Vertices> { vertices } } ; List <Vertices> results = null; switch (algorithm) { case TriangulationAlgorithm.Earclip: #pragma warning disable 162 // ReSharper disable three ConditionIsAlwaysTrueOrFalse if (Settings.SkipSanityChecks) { Debug.Assert(!vertices.IsCounterClockWise(), "The Ear-clip algorithm expects the polygon to be clockwise."); } else { if (vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = EarclipDecomposer.ConvexPartition(temp, tolerance); } else { results = EarclipDecomposer.ConvexPartition(vertices, tolerance); } } break; case TriangulationAlgorithm.Bayazit: if (Settings.SkipSanityChecks) { Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly."); } else { if (!vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = BayazitDecomposer.ConvexPartition(temp); } else { results = BayazitDecomposer.ConvexPartition(vertices); } } break; case TriangulationAlgorithm.Flipcode: if (Settings.SkipSanityChecks) { Debug.Assert(vertices.IsCounterClockWise(), "The polygon is not counter clockwise. This is needed for Bayazit to work correctly."); } #pragma warning restore 162 else { if (!vertices.IsCounterClockWise()) { Vertices temp = new Vertices(vertices); temp.Reverse(); results = FlipcodeDecomposer.ConvexPartition(temp); } else { results = FlipcodeDecomposer.ConvexPartition(vertices); } } break; case TriangulationAlgorithm.Seidel: results = SeidelDecomposer.ConvexPartition(vertices, tolerance); break; case TriangulationAlgorithm.SeidelTrapezoids: results = SeidelDecomposer.ConvexPartitionTrapezoid(vertices, tolerance); break; case TriangulationAlgorithm.Delauny: results = CDTDecomposer.ConvexPartition(vertices); break; default: throw new ArgumentOutOfRangeException(nameof(algorithm)); } if (discardAndFixInvalid) { for (int i = results.Count - 1; i >= 0; i--) { Vertices polygon = results[i]; if (!ValidatePolygon(polygon)) { results.RemoveAt(i); } } } return(results); }
/// <summary> /// Old relic before the soft time /// </summary> public override void CreateBody() { uint[] data = new uint[_cellTexture.Width * _cellTexture.Height]; _cellTexture.GetData(data); //Find the vertices that makes up the outline of the shape in the texture Vertices textureVertices = PolygonTools.CreatePolygon(data, _cellTexture.Width, false); //The tool return vertices as they were found in the texture. //We need to find the real center (centroid) of the vertices for 2 reasons: //1. To translate the vertices so the polygon is centered around the centroid. Vector2 centroid = -textureVertices.GetCentroid(); textureVertices.Translate(ref centroid); //2. To draw the texture the correct place. _origin = -centroid; _spriteDict[PlayerSprites.Cell].Origin = _origin; float scale = _spriteDict[PlayerSprites.Cell].Scale.X; foreach (PlayerSprites sprite in Enum.GetValues(typeof(PlayerSprites))) { if (sprite == PlayerSprites.Cell) { continue; } else { _spriteDict[sprite].Position -= _origin * scale; } } //We simplify the vertices found in the texture. textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f); //Since it is a concave polygon, we need to partition it into several smaller convex polygons List <Vertices> list = EarclipDecomposer.ConvexPartition(textureVertices); //scale the vertices from graphics space to sim space Vector2 vertScale = ConvertUnits.ToSimUnits(new Vector2(1)) * scale; foreach (Vertices vertices in list) { vertices.Scale(ref vertScale); } //Create a single body with multiple fixtures Body = BodyFactory.CreateCompoundPolygon(PlayWindow.World, list, 1f, BodyType.Dynamic); Body.Position = ConvertUnits.ToSimUnits(Position); Body.BodyType = BodyType.Dynamic; Body.CollisionCategories = Category.Cat10; Body.CollidesWith = Category.All; Body.OnCollision += ObjectCollision; Body.Friction = 0.1f; Body.FixedRotation = true; Body.Mass = 2f; Body.LinearDamping = 1; }
public Texture2D TextureFromVertices(Vertices vertices, Texture2D material, Color fillColor, bool hasOutline, Color outlineColor, float materialScale) { Vertices verts = new Vertices(vertices); AABB vertsBounds = verts.GetCollisionBox(); verts.Translate(-vertsBounds.Center); List <Vertices> decomposedVerts; if (!verts.IsConvex()) { decomposedVerts = EarclipDecomposer.ConvexPartition(verts); } else { decomposedVerts = new List <Vertices>() { verts }; } //fill List <VertexPositionColorTexture[]> verticesFill = new List <VertexPositionColorTexture[]>(decomposedVerts.Count); for (int i = 0; i < decomposedVerts.Count; i++) { verticesFill.Add(new VertexPositionColorTexture[3 * (decomposedVerts[i].Count - 2)]); for (int j = 0; j < decomposedVerts[i].Count - 2; j++) { verticesFill[i][3 * j].Position = new Vector3(decomposedVerts[i][0], 0f); verticesFill[i][3 * j + 1].Position = new Vector3(decomposedVerts[i].NextVertex(j), 0f); verticesFill[i][3 * j + 2].Position = new Vector3(decomposedVerts[i].NextVertex(j + 1), 0f); verticesFill[i][3 * j].TextureCoordinate = decomposedVerts[i][0] * materialScale; verticesFill[i][3 * j + 1].TextureCoordinate = decomposedVerts[i].NextVertex(j) * materialScale; verticesFill[i][3 * j + 2].TextureCoordinate = decomposedVerts[i].NextVertex(j + 1) * materialScale; verticesFill[i][3 * j].Color = verticesFill[i][3 * j + 1].Color = verticesFill[i][3 * j + 2].Color = fillColor; } } //outline VertexPositionColor[] verticesOutline; if (!hasOutline) { verticesOutline = new VertexPositionColor[0]; } else { verticesOutline = new VertexPositionColor[2 * verts.Count]; for (int i = 0; i < verts.Count; i++) { verticesOutline[2 * i].Position = new Vector3(verts[i], 0f); verticesOutline[2 * i + 1].Position = new Vector3(verts.NextVertex(i), 0f); verticesOutline[2 * i].Color = verticesOutline[2 * i + 1].Color = outlineColor; } } Vector2 vertsSize = new Vector2(vertsBounds.UpperBound.X - vertsBounds.LowerBound.X, vertsBounds.UpperBound.Y - vertsBounds.LowerBound.Y); return(RenderTexture((int)System.Math.Ceiling(vertsSize.X), (int)System.Math.Ceiling(vertsSize.Y), material, verticesFill, hasOutline, verticesOutline)); }