// arc utils public static IEnumerable <PolylinePoint> GetArcPoints(PolylinePoint a, PolylinePoint b, Vector3 normA, Vector3 normB, Vector3 center, float radius, int count) { count = Mathf.Max(2, count); // at least 2 PolylinePoint DirToPt(Vector3 dir, float t) { PolylinePoint p = t <= 0 ? a : (t >= 1 ? b : PolylinePoint.Lerp(a, b, t)); p.point = center + dir * radius; return(p); } yield return(DirToPt(normA, 0)); for (int i = 1; i < count - 1; i++) { float t = i / (count - 1f); yield return(DirToPt(Vector3.Slerp(normA, normB, t), t)); } yield return(DirToPt(normB, 1)); }
[MethodImpl(INLINE)] public static PolylinePoint WeightedSum(Vector4 w, PolylinePoint a, PolylinePoint b, PolylinePoint c, PolylinePoint d) => w.x * a + w.y * b + w.z * c + w.w * d;
public static PolylinePoint CubicBezier(PolylinePoint a, PolylinePoint b, PolylinePoint c, PolylinePoint d, float t) { if (t <= 0f) { return(a); } if (t >= 1f) { return(d); } return(WeightedSum(GetCubicBezierWeights(t), a, b, c, d)); }
public static IEnumerable <PolylinePoint> CubicBezierPointsSkipFirstMatchStyle(PolylinePoint style, Vector3 a, Vector3 b, Vector3 c, Vector3 d, int count) { // skip first point for (int i = 1; i < count - 1; i++) { float t = i / (count - 1f); PolylinePoint pp = style; pp.point = CubicBezier(a, b, c, d, t); yield return(pp); } PolylinePoint ppEnd = style; ppEnd.point = d; yield return(ppEnd); }
// bezier utils public static IEnumerable <PolylinePoint> CubicBezierPointsSkipFirst(PolylinePoint a, PolylinePoint b, PolylinePoint c, PolylinePoint d, int count) { // skip first point for (int i = 1; i < count - 1; i++) { float t = i / (count - 1f); yield return(CubicBezier(a, b, c, d, t)); } yield return(d); }
public static void GenPolylineMesh(Mesh mesh, IList <PolylinePoint> path, bool closed, PolylineJoins joins, bool useColors) { mesh.Clear(); // todo maybe not always do this you know? int pointCount = path.Count; if (pointCount < 2) { return; } if (pointCount == 2 && closed) { closed = false; } PolylinePoint firstPoint = path[0]; PolylinePoint lastPoint = path[path.Count - 1]; // if the last point is at the same place as the first and it's closed, ignore the last point if ((closed || pointCount == 2) && SamePosition(firstPoint.point, lastPoint.point)) { pointCount--; // ignore last point if (pointCount < 2) // check point count again { return; } lastPoint = path[path.Count - 2]; // second last point technically } // only mitered joints can be in the same submesh at the moment bool separateJoinMesh = joins.HasJoinMesh(); bool isSimpleJoin = joins.HasSimpleJoin(); // only used when join meshes exist int vertsPerPathPoint = separateJoinMesh ? 5 : 2; int trianglesPerSegment = separateJoinMesh ? 4 : 2; int vertexCount = pointCount * vertsPerPathPoint; int vertexCountTotal = vertexCount; int segmentCount = closed ? pointCount : pointCount - 1; int triangleCount = segmentCount * trianglesPerSegment; int triangleIndexCount = triangleCount * 3; // Joins mesh data int[] meshJoinsTriangles = default; int joinVertsPerJoin = default; if (separateJoinMesh) { joinVertsPerJoin = isSimpleJoin ? 3 : 5; int joinCount = closed ? pointCount : pointCount - 2; int joinTrianglesPerJoin = isSimpleJoin ? 1 : 3; int joinTriangleIndexCount = joinCount * joinTrianglesPerJoin * 3; int vertexCountJoins = joinCount * joinVertsPerJoin; vertexCountTotal += vertexCountJoins; meshJoinsTriangles = new int[joinTriangleIndexCount]; } Color[] meshColors = useColors ? new Color[vertexCountTotal] : null; Vector3[] meshVertices = new Vector3[vertexCountTotal]; #if UNITY_2019_3_OR_NEWER Vector4[] meshUv0 = new Vector4[vertexCountTotal]; // UVs for masking. z contains endpoint status, w is thickness Vector3[] meshUv1Prevs = new Vector3[vertexCountTotal]; Vector3[] meshUv2Nexts = new Vector3[vertexCountTotal]; #else // List<> is the only supported vec3 UV assignment method prior to Unity 2019.3 List <Vector4> meshUv0 = new List <Vector4>(new Vector4[vertexCountTotal]); List <Vector3> meshUv1Prevs = new List <Vector3>(new Vector3[vertexCountTotal]); List <Vector3> meshUv2Nexts = new List <Vector3>(new Vector3[vertexCountTotal]); #endif int[] meshTriangles = new int[triangleIndexCount]; // indices used per triangle int iv0, iv1, iv2 = 0, iv3 = 0, iv4 = 0; int ivj0 = 0, ivj1 = 0, ivj2 = 0, ivj3 = 0, ivj4 = 0; int triId = 0; int triIdJoin = 0; for (int i = 0; i < pointCount; i++) { bool isLast = i == pointCount - 1; bool isFirst = i == 0; bool makeJoin = closed || (!isLast && !isFirst); bool isEndpoint = closed == false && (isFirst || isLast); float uvEndpointValue = isEndpoint ? (isFirst ? -1 : 1) : 0; void SetUv0(int id, float x, float y) => meshUv0[id] = new Vector4(x, y, uvEndpointValue, path[i].thickness); // Indices & verts Vector3 vert = path[i].point; Color color = useColors ? path[i].color : default; iv0 = i * vertsPerPathPoint; if (separateJoinMesh) { iv1 = iv0 + 1; // "prev" outer iv2 = iv0 + 2; // "next" outer iv3 = iv0 + 3; // "prev" inner iv4 = iv0 + 4; // "next" inner meshVertices[iv0] = vert; meshVertices[iv1] = vert; meshVertices[iv2] = vert; meshVertices[iv3] = vert; meshVertices[iv4] = vert; if (useColors) { meshColors[iv0] = color; meshColors[iv1] = color; meshColors[iv2] = color; meshColors[iv3] = color; meshColors[iv4] = color; } // joins mesh if (makeJoin) { int joinIndex = (closed ? i : i - 1); // Skip first if open ivj0 = joinIndex * joinVertsPerJoin + vertexCount; ivj1 = ivj0 + 1; ivj2 = ivj0 + 2; ivj3 = ivj0 + 3; ivj4 = ivj0 + 4; meshVertices[ivj0] = vert; meshVertices[ivj1] = vert; meshVertices[ivj2] = vert; if (useColors) { meshColors[ivj0] = color; meshColors[ivj1] = color; meshColors[ivj2] = color; } if (isSimpleJoin == false) { meshVertices[ivj3] = vert; meshVertices[ivj4] = vert; if (useColors) { meshColors[ivj3] = color; meshColors[ivj4] = color; } } } } else { iv1 = iv0 + 1; // Inner vert meshVertices[iv0] = vert; meshVertices[iv1] = vert; if (useColors) { meshColors[iv0] = color; meshColors[iv1] = color; } } // Setting up next/previous positions Vector3 prevPos; Vector3 nextPos; if (i == 0) { prevPos = closed ? lastPoint.point : (firstPoint.point * 2 - path[1].point); // Mirror second point nextPos = path[i + 1].point; } else if (i == pointCount - 1) { prevPos = path[i - 1].point; nextPos = closed ? firstPoint.point : (path[pointCount - 1].point * 2 - path[pointCount - 2].point); // Mirror second last point } else { prevPos = path[i - 1].point; nextPos = path[i + 1].point; } void SetPrevNext(int atIndex) { meshUv1Prevs[atIndex] = prevPos; meshUv2Nexts[atIndex] = nextPos; } SetPrevNext(iv0); SetPrevNext(iv1); if (separateJoinMesh) { SetPrevNext(iv2); SetPrevNext(iv3); SetPrevNext(iv4); if (makeJoin) { SetPrevNext(ivj0); SetPrevNext(ivj1); SetPrevNext(ivj2); if (isSimpleJoin == false) { SetPrevNext(ivj3); SetPrevNext(ivj4); } } } if (separateJoinMesh) { SetUv0(iv0, 0, 0); SetUv0(iv1, -1, -1); SetUv0(iv2, -1, 1); SetUv0(iv3, 1, -1); SetUv0(iv4, 1, 1); if (makeJoin) { SetUv0(ivj0, 0, 0); if (isSimpleJoin) { SetUv0(ivj1, 1, -1); SetUv0(ivj2, 1, 1); } else { SetUv0(ivj1, 1, -1); SetUv0(ivj2, -1, -1); SetUv0(ivj3, -1, 1); SetUv0(ivj4, 1, 1); } } } else { SetUv0(iv0, -1, i); SetUv0(iv1, 1, i); } if (isLast == false || closed) { // clockwise order void AddQuad(int a, int b, int c, int d) { meshTriangles[triId++] = a; meshTriangles[triId++] = b; meshTriangles[triId++] = c; meshTriangles[triId++] = c; meshTriangles[triId++] = d; meshTriangles[triId++] = a; } if (separateJoinMesh) { int rootCenter = iv0; int rootOuter = iv2; int rootInner = iv4; int nextCenter = isLast ? 0 : rootCenter + vertsPerPathPoint; int nextOuter = nextCenter + 1; int nextInner = nextCenter + 3; AddQuad(rootCenter, rootOuter, nextOuter, nextCenter); AddQuad(nextCenter, nextInner, rootInner, rootCenter); if (makeJoin) { meshJoinsTriangles[triIdJoin++] = ivj0; meshJoinsTriangles[triIdJoin++] = ivj1; meshJoinsTriangles[triIdJoin++] = ivj2; if (isSimpleJoin == false) { meshJoinsTriangles[triIdJoin++] = ivj2; meshJoinsTriangles[triIdJoin++] = ivj3; meshJoinsTriangles[triIdJoin++] = ivj0; meshJoinsTriangles[triIdJoin++] = ivj0; meshJoinsTriangles[triIdJoin++] = ivj3; meshJoinsTriangles[triIdJoin++] = ivj4; } } } else { int rootOuter = iv0; int rootInner = iv1; int nextOuter = isLast ? 0 : rootOuter + vertsPerPathPoint; int nextInner = nextOuter + 1; AddQuad(rootInner, rootOuter, nextOuter, nextInner); } } } // assign to segments mesh mesh.vertices = meshVertices; mesh.subMeshCount = 2; mesh.SetTriangles(meshTriangles, 0); mesh.SetTriangles(meshJoinsTriangles, 1); mesh.SetUVs(0, meshUv0); mesh.SetUVs(1, meshUv1Prevs); mesh.SetUVs(2, meshUv2Nexts); if (useColors) { mesh.colors = meshColors; } }