public LibTessDotNet.Tess Tesselate(Color color, bool withAntialiasing, float antialiasingDistance = 0.1f, float antialiasingAlpha = 0.1f) { var tess = new LibTessDotNet.Tess(); if (withAntialiasing) { AddContours(tess, Outline(antialiasingDistance), new Color(color.r, color.g, color.b, antialiasingAlpha)); } AddContours(tess, this, color); tess.Tessellate(LibTessDotNet.WindingRule.NonZero, LibTessDotNet.ElementType.Polygons, 3, VertexCombine); return(tess); }
/*public LibTessDotNet.Tess Tesselate(Color color, bool withAntialiasing) { * var tess = new LibTessDotNet.Tess(); * * for (int i = 0, count = points.Count; i < count; ++i) { * var path = points[i]; * var contour = new LibTessDotNet.ContourVertex[path.Count]; * * for (int j = 0, pathCount = path.Count; j < pathCount; ++j) { * contour[j].Position = path[j].ToVectorTess(); * contour[j].Data = color; * } * * tess.AddContour(contour); * } * * tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3, VertexCombine); * * return tess; * }*/ static void AddContours(LibTessDotNet.Tess tess, Shape shape, Color color) { for (int i = 0, count = shape.points.Count; i < count; ++i) { var path = shape.points[i]; var contour = new LibTessDotNet.ContourVertex[path.Count]; for (int j = 0, pathCount = path.Count; j < pathCount; ++j) { contour[j].Position = path[j].ToVectorTess(); contour[j].Data = color; } tess.AddContour(contour); } }
static void Main(string[] args) { // Example input data in the form of a star that intersects itself. var inputData = new float[] { 0.0f, 3.0f, -1.0f, 0.0f, 1.6f, 1.9f, -1.6f, 1.9f, 1.0f, 0.0f }; // Create an instance of the tessellator. Can be reused. var tess = new LibTessDotNet.Tess(); // Construct the contour from inputData. // A polygon can be composed of multiple contours which are all tessellated at the same time. int numPoints = inputData.Length / 2; var contour = new LibTessDotNet.ContourVertex[numPoints]; for (int i = 0; i < numPoints; i++) { // NOTE : Z is here for convenience if you want to keep a 3D vertex position throughout the tessellation process but only X and Y are important. contour[i].Position = new LibTessDotNet.Vec3 { X = inputData[i * 2], Y = inputData[i * 2 + 1], Z = 0.0f }; // Data can contain any per-vertex data, here a constant color. contour[i].Data = Color.Azure; } // Add the contour with a specific orientation, use "Original" if you want to keep the input orientation. tess.AddContour(contour, LibTessDotNet.ContourOrientation.Clockwise); // Tessellate! // The winding rule determines how the different contours are combined together. // See http://www.glprogramming.com/red/chapter11.html (section "Winding Numbers and Winding Rules") for more information. // If you want triangles as output, you need to use "Polygons" type as output and 3 vertices per polygon. tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3, VertexCombine); // Same call but the last callback is optional. Data will be null because no interpolated data would have been generated. //tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); // Some vertices will have null Data in this case. Console.WriteLine("Output triangles:"); int numTriangles = tess.ElementCount; for (int i = 0; i < numTriangles; i++) { var v0 = tess.Vertices[tess.Elements[i * 3]].Position; var v1 = tess.Vertices[tess.Elements[i * 3 + 1]].Position; var v2 = tess.Vertices[tess.Elements[i * 3 + 2]].Position; Console.WriteLine("#{0} ({1:F1},{2:F1}) ({3:F1},{4:F1}) ({5:F1},{6:F1})", i, v0.X, v0.Y, v1.X, v1.Y, v2.X, v2.Y); } Console.ReadLine(); }
public static bool CreatePolygon(List <List <Vector2> > inputShapes, SVGPaintable paintable, SVGMatrix matrix, out SVGShape layer, out SVGShape antialiasingLayer, bool isStroke = false, bool antialiasing = false) { layer = new SVGShape(); antialiasingLayer = new SVGShape(); if (inputShapes == null || inputShapes.Count == 0) { return(false); } List <List <Vector2> > simplifiedShapes = new List <List <Vector2> >(); PolyFillType fillType = PolyFillType.pftNonZero; if (paintable.fillRule == SVGFillRule.EvenOdd) { fillType = PolyFillType.pftEvenOdd; } simplifiedShapes = SVGGeom.SimplifyPolygons(inputShapes, fillType); if (simplifiedShapes == null || simplifiedShapes.Count == 0) { return(false); } AddInputShape(simplifiedShapes); Rect bounds = GetRect(simplifiedShapes); Rect viewport = paintable.viewport; if (!isStroke) { switch (paintable.GetPaintType()) { case SVGPaintMethod.SolidFill: { layer.type = SVGShapeType.FILL; Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; case SVGPaintMethod.LinearGradientFill: { layer.type = SVGShapeType.FILL; SVGLinearGradientBrush linearGradBrush = paintable.GetLinearGradientBrush(bounds, matrix, viewport); paintable.svgFill = linearGradBrush.fill; } break; case SVGPaintMethod.RadialGradientFill: { layer.type = SVGShapeType.FILL; SVGRadialGradientBrush radialGradBrush = paintable.GetRadialGradientBrush(bounds, matrix, viewport); paintable.svgFill = radialGradBrush.fill; } break; case SVGPaintMethod.ConicalGradientFill: { layer.type = SVGShapeType.FILL; SVGConicalGradientBrush conicalGradBrush = paintable.GetConicalGradientBrush(bounds, matrix, viewport); paintable.svgFill = conicalGradBrush.fill; } break; case SVGPaintMethod.PathDraw: { layer.type = SVGShapeType.STROKE; Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; default: break; } } else { layer.type = SVGShapeType.STROKE; Color color = paintable.strokeColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color, FILL_BLEND.OPAQUE, FILL_TYPE.SOLID); if (color.a != 1f) { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } paintable.svgFill.color = color; } // Use LibTessDotNet if (true) { LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; int pathLength; for (int i = 0; i < simplifiedShapes.Count; i++) { if (simplifiedShapes[i] == null) { continue; } pathLength = simplifiedShapes[i].Count; path = new LibTessDotNet.ContourVertex[pathLength]; Vector2 position; for (int j = 0; j < pathLength; j++) { position = simplifiedShapes[i][j]; path[j].Position = new LibTessDotNet.Vec3 { X = position.x, Y = position.y, Z = 0f }; } tesselation.AddContour(path, SVGImporter.LibTessDotNet.ContourOrientation.Clockwise); } tesselation.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); int meshVertexCount = tesselation.Vertices.Length; if (meshVertexCount == 0) { return(false); } int numTriangles = tesselation.ElementCount; layer.triangles = new int[numTriangles * 3]; layer.vertices = new Vector2[meshVertexCount]; for (int i = 0; i < numTriangles; i++) { layer.triangles[i * 3] = tesselation.Elements[i * 3]; layer.triangles[i * 3 + 1] = tesselation.Elements[i * 3 + 1]; layer.triangles[i * 3 + 2] = tesselation.Elements[i * 3 + 2]; } for (int i = 0; i < meshVertexCount; i++) { layer.vertices[i] = new Vector2(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y) * SVGAssetImport.meshScale; } } /* * else { * // Use Triangle.net library * SVGImporter.TriangleNet.Mesh triangleMesh = new SVGImporter.TriangleNet.Mesh(); * SVGImporter.TriangleNet.Geometry.InputGeometry triangleInput = new SVGImporter.TriangleNet.Geometry.InputGeometry(); * * int pathLength, m = 0; * for(int i = 0; i < simplifiedShapes.Count; i++) * { * if(simplifiedShapes[i] == null) * continue; * * pathLength = simplifiedShapes[i].Count; * Vector2 position; * for(int j = 0; j < pathLength; j++) * { * triangleInput.AddPoint(simplifiedShapes[i][j].x, simplifiedShapes[i][j].y); * } * } * * triangleMesh.Triangulate(triangleInput); * * int totalVertices = triangleMesh.vertices.Count; * layer.vertices = new Vector2[totalVertices]; * for(int i = 0; i < totalVertices; i++) * { * layer.vertices[i].x = (float)triangleMesh.vertices[i].x * SVGAssetImport.meshScale; * layer.vertices[i].y = (float)triangleMesh.vertices[i].y * SVGAssetImport.meshScale; * } * * int totalTriangles = triangleMesh.triangles.Count; * layer.triangles = new int[totalTriangles * 3]; * int ti = 0; * for(int i = 0; i < totalTriangles; i++) * { * ti = i * 3; * layer.triangles[ti] = triangleMesh.triangles[i].P0; * layer.triangles[ti + 1] = triangleMesh.triangles[i].P1; * layer.triangles[ti + 2] = triangleMesh.triangles[i].P2; * } * } */ layer.fill = paintable.svgFill; layer.fill.opacity = paintable.opacity; if (layer.fill.opacity < 1f && layer.fill.blend == FILL_BLEND.OPAQUE) { layer.fill.blend = FILL_BLEND.ALPHA_BLENDED; } if (layer.fill.fillType == FILL_TYPE.GRADIENT && layer.fill.gradientColors != null) { layer.fill.color = Color.white; } else if (layer.fill.fillType == FILL_TYPE.TEXTURE) { layer.fill.color = Color.white; } viewport.x *= SVGAssetImport.meshScale; viewport.y *= SVGAssetImport.meshScale; viewport.size *= SVGAssetImport.meshScale; layer.fill.viewport = viewport; SVGMatrix scaleMatrix = SVGMatrix.identity.Scale(SVGAssetImport.meshScale); layer.fill.transform = scaleMatrix.Multiply(layer.fill.transform); layer.fill.transform = layer.fill.transform.Multiply(scaleMatrix.Inverse()); Vector2 boundsMin = bounds.min * SVGAssetImport.meshScale; Vector2 boundsMax = bounds.max * SVGAssetImport.meshScale; layer.bounds = new Rect(boundsMin.x, boundsMin.y, boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y); if (antialiasing) { if (CreateAntialiasing(simplifiedShapes, out antialiasingLayer, Color.white, -1f, ClosePathRule.ALWAYS)) { int verticesLength = antialiasingLayer.vertices.Length; for (int i = 0; i < verticesLength; i++) { antialiasingLayer.vertices[i] *= SVGAssetImport.meshScale; } antialiasingLayer.type = SVGShapeType.ANTIALIASING; antialiasingLayer.RecalculateBounds(); antialiasingLayer.fill = layer.fill.Clone(); antialiasingLayer.fill.blend = FILL_BLEND.ALPHA_BLENDED; } } return(true); }
public static void TesselateStroke(List<List<Vector2>> inputShapes, Color32 color, out List<List<Vector2>> simplifiedShapes, out Vector3[] vertices, out int[] triangles, out Color32[] colors32) { simplifiedShapes = null; vertices = null; triangles = null; colors32 = null; if(inputShapes == null || inputShapes.Count == 0) return; int i, j; simplifiedShapes = new List<List<Vector2>>(); PolyFillType fillType = PolyFillType.pftNonZero; for(i = 0; i < inputShapes.Count; i++) { if(inputShapes[i] == null || inputShapes.Count == 0) continue; List<List<Vector2>> output = SVGGeom.SimplifyPolygon(inputShapes[i], fillType); if(output == null || output.Count == 0) { simplifiedShapes.Add(inputShapes[i]); } else { simplifiedShapes.AddRange(output); } } LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; for(i = 0; i < simplifiedShapes.Count; i++) { if(simplifiedShapes[i] == null || simplifiedShapes[i].Count < 2) continue; path = new LibTessDotNet.ContourVertex[simplifiedShapes[i].Count]; for(j = 0; j < simplifiedShapes[i].Count; j++) { path[j].Position = new LibTessDotNet.Vec3{X = simplifiedShapes[i][j].x, Y = simplifiedShapes[i][j].y, Z = 0f }; } tesselation.AddContour(path); } tesselation.Tessellate(LibTessDotNet.WindingRule.Positive, LibTessDotNet.ElementType.Polygons, 3); if(tesselation.Vertices == null || tesselation.Vertices.Length == 0) return; Mesh mesh = new Mesh(); int numVertices = tesselation.Vertices.Length; int numTriangles = tesselation.ElementCount * 3; triangles = new int[numTriangles]; vertices = new Vector3[numVertices]; colors32 = new Color32[numVertices]; for(i = 0; i < numVertices; i++) { vertices[i] = new Vector3(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y, 0f); colors32[i] = color; } for (i = 0; i < numTriangles; i += 3) { triangles[i] = tesselation.Elements[i]; triangles[i + 1] = tesselation.Elements[i + 1]; triangles[i + 2] = tesselation.Elements[i + 2]; } }
public static Mesh CreatePolygon(List <List <Vector2> > inputShapes, SVGPaintable paintable, SVGMatrix matrix, out Mesh antialiasingMesh) { antialiasingMesh = null; if (inputShapes == null || inputShapes.Count == 0) { return(null); } List <List <Vector2> > simplifiedShapes = new List <List <Vector2> >(); PolyFillType fillType = PolyFillType.pftNonZero; if (paintable.fillRule == SVGFillRule.EvenOdd) { fillType = PolyFillType.pftEvenOdd; } simplifiedShapes = SVGGeom.SimplifyPolygons(inputShapes, fillType); if (simplifiedShapes == null || simplifiedShapes.Count == 0) { return(null); } AddInputShape(simplifiedShapes); Rect bounds = GetRect(simplifiedShapes); switch (paintable.GetPaintType()) { case SVGPaintMethod.SolidGradientFill: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; case SVGPaintMethod.LinearGradientFill: { SVGLinearGradientBrush linearGradBrush = paintable.GetLinearGradientBrush(bounds, matrix); paintable.svgFill = linearGradBrush.fill; } break; case SVGPaintMethod.RadialGradientFill: { SVGRadialGradientBrush radialGradBrush = paintable.GetRadialGradientBrush(bounds, matrix); paintable.svgFill = radialGradBrush.fill; } break; case SVGPaintMethod.ConicalGradientFill: { SVGConicalGradientBrush conicalGradBrush = paintable.GetConicalGradientBrush(bounds, matrix); paintable.svgFill = conicalGradBrush.fill; } break; case SVGPaintMethod.PathDraw: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; default: break; } LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; int pathLength; for (int i = 0; i < simplifiedShapes.Count; i++) { if (simplifiedShapes[i] == null) { continue; } pathLength = simplifiedShapes[i].Count; path = new LibTessDotNet.ContourVertex[pathLength]; Vector2 position; for (int j = 0; j < pathLength; j++) { position = simplifiedShapes[i][j]; path[j].Position = new LibTessDotNet.Vec3 { X = position.x, Y = position.y, Z = 0f }; } tesselation.AddContour(path); } tesselation.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); Mesh mesh = new Mesh(); int meshVertexCount = tesselation.Vertices.Length; Vector3[] vertices = new Vector3[meshVertexCount]; Vector2[] uv = null; Vector2[] uv2 = null; for (int i = 0; i < meshVertexCount; i++) { vertices[i] = new Vector3(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y, 0f); } int numTriangles = tesselation.ElementCount; int[] triangles = new int[numTriangles * 3]; for (int i = 0; i < numTriangles; i++) { triangles[i * 3] = tesselation.Elements[i * 3]; triangles[i * 3 + 1] = tesselation.Elements[i * 3 + 1]; triangles[i * 3 + 2] = tesselation.Elements[i * 3 + 2]; } SVGFill svgFill = paintable.svgFill; Color32 fillColor = Color.white; if (svgFill.fillType != FILL_TYPE.GRADIENT && svgFill.gradientColors == null) { fillColor = svgFill.color; } antialiasingMesh = CreateAntialiasing(simplifiedShapes, fillColor, -SVGAssetImport.antialiasingWidth, false, SVGImporter.Utils.ClosePathRule.ALWAYS); Color32[] colors32 = new Color32[meshVertexCount]; for (int i = 0; i < meshVertexCount; i++) { colors32 [i].r = fillColor.r; colors32 [i].g = fillColor.g; colors32 [i].b = fillColor.b; colors32 [i].a = fillColor.a; } if (antialiasingMesh != null) { Vector3[] antialiasingVertices = antialiasingMesh.vertices; Vector2[] antialiasingUV = antialiasingMesh.uv; Vector2[] antialiasingUV2 = antialiasingMesh.uv2; WriteUVGradientCoordinates(ref antialiasingUV, antialiasingVertices, paintable, bounds); WriteUVGradientIndexType(ref antialiasingUV2, antialiasingVertices.Length, paintable); antialiasingMesh.uv = antialiasingUV; antialiasingMesh.uv2 = antialiasingUV2; } WriteUVGradientCoordinates(ref uv, vertices, paintable, bounds); WriteUVGradientIndexType(ref uv2, meshVertexCount, paintable); mesh.vertices = vertices; mesh.triangles = triangles; if (colors32 != null) { mesh.colors32 = colors32; } if (uv != null) { mesh.uv = uv; } if (uv2 != null) { mesh.uv2 = uv2; } return(mesh); }
public static Mesh CreatePolygon(List<List<Vector2>> inputShapes, SVGPaintable paintable, SVGMatrix matrix, out Mesh antialiasingMesh) { antialiasingMesh = null; if(inputShapes == null || inputShapes.Count == 0) { return null; } List<List<Vector2>> simplifiedShapes = new List<List<Vector2>>(); PolyFillType fillType = PolyFillType.pftNonZero; if(paintable.fillRule == SVGFillRule.EvenOdd) { fillType = PolyFillType.pftEvenOdd; } simplifiedShapes = SVGGeom.SimplifyPolygons(inputShapes, fillType); if(simplifiedShapes == null || simplifiedShapes.Count == 0) return null; AddInputShape(simplifiedShapes); Rect bounds = GetRect(simplifiedShapes); switch (paintable.GetPaintType()) { case SVGPaintMethod.SolidGradientFill: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if(colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if(color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; case SVGPaintMethod.LinearGradientFill: { SVGLinearGradientBrush linearGradBrush = paintable.GetLinearGradientBrush(bounds, matrix); paintable.svgFill = linearGradBrush.fill; } break; case SVGPaintMethod.RadialGradientFill: { SVGRadialGradientBrush radialGradBrush = paintable.GetRadialGradientBrush(bounds, matrix); paintable.svgFill = radialGradBrush.fill; } break; case SVGPaintMethod.ConicalGradientFill: { SVGConicalGradientBrush conicalGradBrush = paintable.GetConicalGradientBrush(bounds, matrix); paintable.svgFill = conicalGradBrush.fill; } break; case SVGPaintMethod.PathDraw: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if(colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if(color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; default: break; } LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; int pathLength; for(int i = 0; i < simplifiedShapes.Count; i++) { if(simplifiedShapes[i] == null) continue; pathLength = simplifiedShapes[i].Count; path = new LibTessDotNet.ContourVertex[pathLength]; Vector2 position; for(int j = 0; j < pathLength; j++) { position = simplifiedShapes[i][j]; path[j].Position = new LibTessDotNet.Vec3{X = position.x, Y = position.y, Z = 0f}; } tesselation.AddContour(path); } tesselation.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); Mesh mesh = new Mesh(); int meshVertexCount = tesselation.Vertices.Length; Vector3[] vertices = new Vector3[meshVertexCount]; Vector2[] uv = null; Vector2[] uv2 = null; for(int i = 0; i < meshVertexCount; i++) { vertices[i] = new Vector3(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y, 0f); } int numTriangles = tesselation.ElementCount; int[] triangles = new int[numTriangles * 3]; for (int i = 0; i < numTriangles; i++) { triangles[i * 3] = tesselation.Elements[i * 3]; triangles[i * 3 + 1] = tesselation.Elements[i * 3 + 1]; triangles[i * 3 + 2] = tesselation.Elements[i * 3 + 2]; } SVGFill svgFill = paintable.svgFill; Color32 fillColor = Color.white; if (svgFill.fillType != FILL_TYPE.GRADIENT && svgFill.gradientColors == null) fillColor = svgFill.color; antialiasingMesh = CreateAntialiasing(simplifiedShapes, fillColor, -SVGAssetImport.antialiasingWidth, false, SVGImporter.Utils.ClosePathRule.ALWAYS); Color32[] colors32 = new Color32[meshVertexCount]; for (int i = 0; i < meshVertexCount; i++) { colors32 [i].r = fillColor.r; colors32 [i].g = fillColor.g; colors32 [i].b = fillColor.b; colors32 [i].a = fillColor.a; } if(antialiasingMesh != null) { Vector3[] antialiasingVertices = antialiasingMesh.vertices; Vector2[] antialiasingUV = antialiasingMesh.uv; Vector2[] antialiasingUV2 = antialiasingMesh.uv2; WriteUVGradientCoordinates(ref antialiasingUV, antialiasingVertices, paintable, bounds); WriteUVGradientIndexType(ref antialiasingUV2, antialiasingVertices.Length, paintable); antialiasingMesh.uv = antialiasingUV; antialiasingMesh.uv2 = antialiasingUV2; } WriteUVGradientCoordinates(ref uv, vertices, paintable, bounds); WriteUVGradientIndexType(ref uv2, meshVertexCount, paintable); mesh.vertices = vertices; mesh.triangles = triangles; if(colors32 != null) mesh.colors32 = colors32; if(uv != null) mesh.uv = uv; if(uv2 != null) mesh.uv2 = uv2; return mesh; }
public List <PointF[]> Triangulate(ClipperLib.PolyTree solution) { List <PointF[]> triangles = new List <PointF[]>(); var tess = new LibTessDotNet.Tess(); tess.NoEmptyPolygons = true; // Transformation function from ClipperLip Point to LibTess contour vertex Func <ClipperLib.IntPoint, LibTessDotNet.ContourVertex> xfToContourVertex = (p) => new LibTessDotNet.ContourVertex() { Position = new LibTessDotNet.Vec3 { X = p.X, Y = p.Y, Z = 0 } }; // Add a contour for each part of the solution tree ClipperLib.PolyNode node = solution.GetFirst(); while (node != null) { // Only interested in closed paths if (!node.IsOpen) { // Add a new countor. Holes are automatically generated. var vertices = node.Contour.Select(xfToContourVertex).ToArray(); tess.AddContour(vertices); } node = node.GetNext(); } // Do the tessellation tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); // Extract the triangles int numTriangles = tess.ElementCount; for (int i = 0; i < numTriangles; i++) { var v0 = tess.Vertices[tess.Elements[i * 3 + 0]].Position; var v1 = tess.Vertices[tess.Elements[i * 3 + 1]].Position; var v2 = tess.Vertices[tess.Elements[i * 3 + 2]].Position; List <PointF> triangle = new List <PointF>() { new PointF(v0.X, v0.Y), new PointF(v1.X, v1.Y), new PointF(v2.X, v2.Y), }; // Assre each triangle needs to be CCW float cross = Geometry.Math.Cross(triangle[0], triangle[1], triangle[2]); if (cross > 0) { triangle.Reverse(); } triangles.Add(triangle.ToArray()); } return(triangles); }
public static void TesselateStroke(List <List <Vector2> > inputShapes, Color32 color, out List <List <Vector2> > simplifiedShapes, out Vector3[] vertices, out int[] triangles, out Color32[] colors32) { simplifiedShapes = null; vertices = null; triangles = null; colors32 = null; if (inputShapes == null || inputShapes.Count == 0) { return; } int i, j; simplifiedShapes = new List <List <Vector2> >(); PolyFillType fillType = PolyFillType.pftNonZero; for (i = 0; i < inputShapes.Count; i++) { if (inputShapes[i] == null || inputShapes.Count == 0) { continue; } List <List <Vector2> > output = SVGGeom.SimplifyPolygon(inputShapes[i], fillType); if (output == null || output.Count == 0) { simplifiedShapes.Add(inputShapes[i]); } else { simplifiedShapes.AddRange(output); } } LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; for (i = 0; i < simplifiedShapes.Count; i++) { if (simplifiedShapes[i] == null || simplifiedShapes[i].Count < 2) { continue; } path = new LibTessDotNet.ContourVertex[simplifiedShapes[i].Count]; for (j = 0; j < simplifiedShapes[i].Count; j++) { path[j].Position = new LibTessDotNet.Vec3 { X = simplifiedShapes[i][j].x, Y = simplifiedShapes[i][j].y, Z = 0f }; } tesselation.AddContour(path); } tesselation.Tessellate(LibTessDotNet.WindingRule.Positive, LibTessDotNet.ElementType.Polygons, 3); if (tesselation.Vertices == null || tesselation.Vertices.Length == 0) { return; } int numVertices = tesselation.Vertices.Length; int numTriangles = tesselation.ElementCount * 3; triangles = new int[numTriangles]; vertices = new Vector3[numVertices]; colors32 = new Color32[numVertices]; for (i = 0; i < numVertices; i++) { vertices[i] = new Vector3(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y, 0f); colors32[i] = color; } for (i = 0; i < numTriangles; i += 3) { triangles[i] = tesselation.Elements[i]; triangles[i + 1] = tesselation.Elements[i + 1]; triangles[i + 2] = tesselation.Elements[i + 2]; } }
/// <summary> /// Creates the double-sided submesh based on vertices and triangles. /// </summary> public void CreateMesh() { //clear mesh definitions if (subMesh) { subMesh.Clear(); } //get components MeshFilter subFilter = null; MeshFilter[] subFilters = GetComponentsInChildren <MeshFilter>(true); //find corresponding MeshFilter for (int i = 0; i < subFilters.Length; i++) { if (subFilters[i].sharedMesh == subMesh) { subFilter = subFilters[i]; break; } } var tess = new LibTessDotNet.Tess(); // Construct the contour from inputData. // A polygon can be composed of multiple contours which are all tessellated at the same time. int numPoints = list.Count; var contour = new LibTessDotNet.ContourVertex[numPoints]; for (int i = 0; i < numPoints; i++) { // NOTE : Z is here for convenience if you want to keep a 3D vertex position throughout the tessellation process but only X and Y are important. contour[i].Position = new LibTessDotNet.Vec3 { X = list[i].x, Y = list[i].y, Z = list[i].z }; } // Add the contour with a specific orientation, use "Original" if you want to keep the input orientation. tess.AddContour(contour, LibTessDotNet.ContourOrientation.Clockwise); // Tessellate! // The winding rule determines how the different contours are combined together. // See http://www.glprogramming.com/red/chapter11.html (section "Winding Numbers and Winding Rules") for more information. // If you want triangles as output, you need to use "Polygons" type as output and 3 vertices per polygon. tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); Vector3[] vertex = new Vector3[tess.VertexCount]; for (int i = 0; i < tess.VertexCount; i++) { vertex[i] = new Vector3(tess.Vertices[i].Position.X, tess.Vertices[i].Position.Y, tess.Vertices[i].Position.Z); } Vector2[] uvs = new Vector2[vertex.Length]; for (int i = 0; i < vertex.Length; i++) { if ((i % 2) == 0) { uvs[i] = new Vector2(0, 0); } else { uvs[i] = new Vector2(1, 1); } } int[] tris = tess.Elements.Concat(tess.Elements.Reverse()).ToArray(); // //get vertex positions of current submesh // Vector3[] vertex = new Vector3[current.Count]; // for (int i = 0; i < current.Count; i++) // vertex[i] = list[current[i]]; // // //don't continue without meshfilter or not enough points // if (!subFilter || vertex.Length < 3) return; // // //set uvs of vertices // Vector2[] uvs = new Vector2[vertex.Length]; // for (int i = 0; i < vertex.Length; i++) // { // if ((i % 2) == 0) // uvs[i] = new Vector2(0, 0); // else // uvs[i] = new Vector2(1, 1); // } //assign data to mesh subMesh.vertices = vertex; subMesh.uv = uvs; subMesh.triangles = tris; //recalculate and optimize subMesh.RecalculateNormals(); subMesh.RecalculateBounds(); subMesh.Optimize(); //assign mesh to filter subFilter.mesh = subMesh; }
/// <summary> /// Updates the mesh with new vertex positions. /// </summary> public void UpdateMesh(Vector3[] verts) { //convert passed in vertices to relative positioning MeshFilter myFilter = GetComponent <MeshFilter>(); for (int i = 0; i < verts.Length; i++) { verts[i] = transform.InverseTransformPoint(verts[i]); } //assign vertices //myFilter.sharedMesh.vertices = verts; var tess = new LibTessDotNet.Tess(); // Construct the contour from inputData. // A polygon can be composed of multiple contours which are all tessellated at the same time. int numPoints = list.Count; var contour = new LibTessDotNet.ContourVertex[numPoints]; for (int i = 0; i < numPoints; i++) { // NOTE : Z is here for convenience if you want to keep a 3D vertex position throughout the tessellation process but only X and Y are important. contour[i].Position = new LibTessDotNet.Vec3 { X = list[i].x, Y = list[i].y, Z = list[i].z }; } // Add the contour with a specific orientation, use "Original" if you want to keep the input orientation. tess.AddContour(contour, LibTessDotNet.ContourOrientation.Clockwise); // Tessellate! // The winding rule determines how the different contours are combined together. // See http://www.glprogramming.com/red/chapter11.html (section "Winding Numbers and Winding Rules") for more information. // If you want triangles as output, you need to use "Polygons" type as output and 3 vertices per polygon. tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); Vector3[] vertex = new Vector3[tess.VertexCount]; for (int i = 0; i < tess.VertexCount; i++) { vertex[i] = new Vector3(tess.Vertices[i].Position.X, tess.Vertices[i].Position.Y, tess.Vertices[i].Position.Z); } Vector2[] uvs = new Vector2[vertex.Length]; for (int i = 0; i < vertex.Length; i++) { if ((i % 2) == 0) { uvs[i] = new Vector2(0, 0); } else { uvs[i] = new Vector2(1, 1); } } int[] tris = tess.Elements.Concat(tess.Elements.Reverse()).ToArray(); myFilter.sharedMesh.Clear(); myFilter.sharedMesh.vertices = vertex; myFilter.sharedMesh.uv = uvs; myFilter.sharedMesh.triangles = tris; }
public static bool CreatePolygon(List <List <Vector2> > inputShapes, SVGPaintable paintable, SVGMatrix matrix, out SVGLayer layer, bool isStroke = false) { layer = new SVGLayer(); if (inputShapes == null || inputShapes.Count == 0) { return(false); } List <List <Vector2> > simplifiedShapes = new List <List <Vector2> >(); PolyFillType fillType = PolyFillType.pftNonZero; if (paintable.fillRule == SVGFillRule.EvenOdd) { fillType = PolyFillType.pftEvenOdd; } simplifiedShapes = SVGGeom.SimplifyPolygons(inputShapes, fillType); if (simplifiedShapes == null || simplifiedShapes.Count == 0) { return(false); } AddInputShape(simplifiedShapes); Rect bounds = GetRect(simplifiedShapes); Rect viewport = paintable.viewport; if (!isStroke) { switch (paintable.GetPaintType()) { case SVGPaintMethod.SolidFill: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.fillOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; case SVGPaintMethod.LinearGradientFill: { SVGLinearGradientBrush linearGradBrush = paintable.GetLinearGradientBrush(bounds, matrix, viewport); paintable.svgFill = linearGradBrush.fill; } break; case SVGPaintMethod.RadialGradientFill: { SVGRadialGradientBrush radialGradBrush = paintable.GetRadialGradientBrush(bounds, matrix, viewport); paintable.svgFill = radialGradBrush.fill; } break; case SVGPaintMethod.ConicalGradientFill: { SVGConicalGradientBrush conicalGradBrush = paintable.GetConicalGradientBrush(bounds, matrix, viewport); paintable.svgFill = conicalGradBrush.fill; } break; case SVGPaintMethod.PathDraw: { Color color = Color.black; SVGColorType colorType = paintable.fillColor.Value.colorType; if (colorType == SVGColorType.Unknown || colorType == SVGColorType.None) { color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } else { color = paintable.fillColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color); } paintable.svgFill.fillType = FILL_TYPE.SOLID; if (color.a == 1) { paintable.svgFill.blend = FILL_BLEND.OPAQUE; } else { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } } break; default: break; } } else { Color color = paintable.strokeColor.Value.color; color.a *= paintable.strokeOpacity; paintable.svgFill = new SVGFill(color, FILL_BLEND.OPAQUE, FILL_TYPE.SOLID); if (color.a != 1f) { paintable.svgFill.blend = FILL_BLEND.ALPHA_BLENDED; } paintable.svgFill.color = color; } LibTessDotNet.Tess tesselation = new LibTessDotNet.Tess(); LibTessDotNet.ContourVertex[] path; int pathLength; for (int i = 0; i < simplifiedShapes.Count; i++) { if (simplifiedShapes[i] == null) { continue; } pathLength = simplifiedShapes[i].Count; path = new LibTessDotNet.ContourVertex[pathLength]; Vector2 position; for (int j = 0; j < pathLength; j++) { position = simplifiedShapes[i][j]; path[j].Position = new LibTessDotNet.Vec3 { X = position.x, Y = position.y, Z = 0f }; } tesselation.AddContour(path, SVGImporter.LibTessDotNet.ContourOrientation.Clockwise); } tesselation.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); int meshVertexCount = tesselation.Vertices.Length; layer.vertices = new Vector2[meshVertexCount]; for (int i = 0; i < meshVertexCount; i++) { layer.vertices[i] = new Vector2(tesselation.Vertices[i].Position.X, tesselation.Vertices[i].Position.Y) * SVGAssetImport.meshScale; } int numTriangles = tesselation.ElementCount; layer.triangles = new int[numTriangles * 3]; for (int i = 0; i < numTriangles; i++) { layer.triangles[i * 3] = tesselation.Elements[i * 3]; layer.triangles[i * 3 + 1] = tesselation.Elements[i * 3 + 1]; layer.triangles[i * 3 + 2] = tesselation.Elements[i * 3 + 2]; } layer.fill = paintable.svgFill; layer.fill.opacity = paintable.opacity; if (layer.fill.opacity < 1f && layer.fill.blend == FILL_BLEND.OPAQUE) { layer.fill.blend = FILL_BLEND.ALPHA_BLENDED; } if (layer.fill.fillType == FILL_TYPE.GRADIENT && layer.fill.gradientColors != null) { layer.fill.color = Color.white; } else if (layer.fill.fillType == FILL_TYPE.TEXTURE) { layer.fill.color = Color.white; } viewport.x *= SVGAssetImport.meshScale; viewport.y *= SVGAssetImport.meshScale; viewport.size *= SVGAssetImport.meshScale; layer.fill.viewport = viewport; if (layer.fill.transform != null) { SVGMatrix scaleMatrix = SVGMatrix.Identity().Scale(SVGAssetImport.meshScale); layer.fill.transform = scaleMatrix.Multiply(layer.fill.transform); layer.fill.transform = layer.fill.transform.Multiply(scaleMatrix.Inverse()); } Vector2 boundsMin = bounds.min * SVGAssetImport.meshScale; Vector2 boundsMax = bounds.max * SVGAssetImport.meshScale; layer.bounds = new Rect(boundsMin.x, boundsMin.y, boundsMax.x - boundsMin.x, boundsMax.y - boundsMin.y); return(true); }