[OvldGenCallTarget] public static void Rectangle([OvldDefault(nameof(BlendMode))] ShapesBlendMode blendMode, [OvldDefault("false")] bool hollow, [OvldDefault("Vector3.zero")] Vector3 pos, [OvldDefault("Quaternion.identity")] Quaternion rot, Rect rect, [OvldDefault(nameof(Color))] Color color, [OvldDefault("0f")] float thickness = 0f, [OvldDefault("default")] Vector4 cornerRadii = default) { bool rounded = ShapesMath.MaxComp(cornerRadii) >= 0.0001f; if (hollow && thickness * 2 >= Mathf.Min(rect.width, rect.height)) { hollow = false; } Material mat = ShapesMaterialUtils.GetRectMaterial(hollow, rounded)[blendMode]; mat.SetColor(ShapesMaterialUtils.propColor, color); mat.SetVector(ShapesMaterialUtils.propRect, rect.ToVector4()); if (rounded) { mat.SetVector(ShapesMaterialUtils.propCornerRadii, cornerRadii); } if (hollow) { mat.SetFloat(ShapesMaterialUtils.propThickness, thickness); mat.SetInt(ShapesMaterialUtils.propScaleMode, (int)ScaleMode); } DrawMesh(pos, rot, ShapesMeshUtils.QuadMesh, mat); }
static void GenerateDiscMesh(Mesh mesh, int segmentsPerFullTurn, bool hasSector, bool hasInnerRadius, float radius, float radiusInner, float angRadiansStart, float angRadiansEnd) { float gizmoAngStart = hasSector ? angRadiansStart : 0f; float gizmoAngEnd = hasSector ? angRadiansEnd : ShapesMath.TAU; float turnSpan = Mathf.Abs(gizmoAngEnd - gizmoAngStart) / ShapesMath.TAU; int segmentCount = Mathf.Max(1, Mathf.RoundToInt(turnSpan * segmentsPerFullTurn)); float gizmoOutermostRadius = Mathf.Max(radius, radiusInner); float apothemOuter = Mathf.Cos(0.5f * Mathf.Abs(gizmoAngEnd - gizmoAngStart) / segmentCount) * gizmoOutermostRadius; float gizmoRadiusOuter = gizmoOutermostRadius * 2 - apothemOuter; // Adjust by apothem to fit better! float gizmoRadiusInner = hasInnerRadius ? Mathf.Min(radius, radiusInner) : 0f; // Generate mesh int triangleCount = segmentCount * 2 * 2; // 2(trisperquad) * 2(doublesided) int vertCount = (segmentCount + 1) * 2; int[] triIndices = new int[triangleCount * 3]; Vector3[] vertices = new Vector3[vertCount]; Vector3[] normals = new Vector3[vertCount]; for (int i = 0; i < segmentCount + 1; i++) { float t = i / (float)segmentCount; float ang = Mathf.Lerp(gizmoAngStart, gizmoAngEnd, t); Vector2 dir = ShapesMath.AngToDir(ang); int iRoot = i * 2; int iInner = iRoot + 1; vertices[iRoot] = dir * gizmoRadiusOuter; vertices[iInner] = dir * gizmoRadiusInner; normals[iRoot] = Vector3.forward; normals[iInner] = Vector3.forward; } int tri = 0; for (int i = 0; i < segmentCount; i++) { int iRoot = i * 2; int iInner = iRoot + 1; int iNextOuter = iRoot + 2; int iNextInner = iRoot + 3; void DblTri(int a, int b, int c) { triIndices[tri++] = a; triIndices[tri++] = b; triIndices[tri++] = c; triIndices[tri++] = c; triIndices[tri++] = b; triIndices[tri++] = a; } DblTri(iRoot, iNextInner, iNextOuter); DblTri(iRoot, iInner, iNextInner); } mesh.vertices = vertices; mesh.normals = normals; mesh.triangles = triIndices; mesh.RecalculateBounds(); }
public static BasicMeshData GenerateCone(int divs, bool generateCap) { BasicMeshData mesh = new BasicMeshData(); mesh.verts.Add(Vector3.forward); for (int i = 1; i < divs + 1; i++) { float t = i / (float)divs; int iNext = i == divs ? 1 : i + 1; mesh.verts.Add(ShapesMath.AngToDir(t * ShapesMath.TAU)); mesh.tris.Add(0); // vertex 0 is the tip mesh.tris.Add(i); mesh.tris.Add(iNext); if (generateCap && i > 1 && i < divs) { mesh.tris.Add(1); // vertex 1 is the root edge vert mesh.tris.Add(iNext); mesh.tris.Add(i); } } mesh.normals = mesh.verts.Select(v => v).ToList(); // already normalized return(mesh); }
public static BasicMeshData GenerateTorus(int divsMinor, int divsMajor, float rMinor = 1, float rMajor = 1) { BasicMeshData mesh = new BasicMeshData(); mesh.normals = new List <Vector3>(); for (int iMaj = 0; iMaj < divsMajor; iMaj++) { float tMaj = iMaj / (float)divsMajor; Vector2 dirMaj = ShapesMath.AngToDir(tMaj * ShapesMath.TAU); for (int iMin = 0; iMin < divsMinor; iMin++) { float tMin = iMin / (float)divsMinor; Vector2 dirMinLocal = ShapesMath.AngToDir(tMin * ShapesMath.TAU); Vector3 dirMin = (Vector3)dirMaj * dirMinLocal.x + new Vector3(0, 0, dirMinLocal.y); mesh.normals.Add(dirMin); mesh.verts.Add((Vector3)dirMaj * rMajor + dirMin * rMinor); int maj0min0 = iMaj * divsMinor + iMin; int maj1min0 = (iMaj + 1) % divsMajor * divsMinor + iMin; int maj0min1 = iMaj * divsMinor + (iMin + 1) % divsMinor; int maj1min1 = (iMaj + 1) % divsMajor * divsMinor + (iMin + 1) % divsMinor; mesh.tris.Add(maj0min1); mesh.tris.Add(maj0min0); mesh.tris.Add(maj1min1); mesh.tris.Add(maj1min0); mesh.tris.Add(maj1min1); mesh.tris.Add(maj0min0); } } return(mesh); }
[OvldGenCallTarget] static void Rectangle_Internal([OvldDefault(nameof(BlendMode))] ShapesBlendMode blendMode, [OvldDefault("false")] bool hollow, Rect rect, [OvldDefault(nameof(Color))] Color color, [OvldDefault(nameof(Thickness))] float thickness, [OvldDefault("default")] Vector4 cornerRadii) { bool rounded = ShapesMath.MaxComp(cornerRadii) >= 0.0001f; // positive vibes only if (rect.width < 0) { rect.x -= rect.width *= -1; } if (rect.height < 0) { rect.y -= rect.height *= -1; } using (new IMDrawer(mpbRect, ShapesMaterialUtils.GetRectMaterial(hollow, rounded)[blendMode], ShapesMeshUtils.QuadMesh[0])) { MetaMpb.ApplyColorOrFill(mpbRect, color); MetaMpb.ApplyDashSettings(mpbRect, thickness); mpbRect.rect.Add(rect.ToVector4()); mpbRect.cornerRadii.Add(cornerRadii); mpbRect.thickness.Add(thickness); mpbRect.thicknessSpace.Add((int)Draw.ThicknessSpace); mpbRect.scaleMode.Add((int)ScaleMode); } }
[OvldGenCallTarget] static void Rectangle([OvldDefault(nameof(BlendMode))] ShapesBlendMode blendMode, [OvldDefault("false")] bool hollow, [OvldDefault("Vector3.zero")] Vector3 pos, [OvldDefault("Quaternion.identity")] Quaternion rot, Rect rect, [OvldDefault(nameof(Color))] Color color, [OvldDefault("0f")] float thickness = 0f, [OvldDefault("default")] Vector4 cornerRadii = default) { bool rounded = ShapesMath.MaxComp(cornerRadii) >= 0.0001f; // positive vibes only if (rect.width < 0) { rect.x -= rect.width *= -1; } if (rect.height < 0) { rect.y -= rect.height *= -1; } if (hollow && thickness * 2 >= Mathf.Min(rect.width, rect.height)) { hollow = false; } using (new IMDrawer(mpbRectangle, ShapesMaterialUtils.GetRectMaterial(hollow, rounded)[blendMode], ShapesMeshUtils.QuadMesh[0], pos, rot)) { mpbRectangle.color.Add(color); mpbRectangle.rect.Add(rect.ToVector4()); mpbRectangle.cornerRadii.Add(cornerRadii); mpbRectangle.thickness.Add(thickness); mpbRectangle.scaleMode.Add((int)ScaleMode); } }
Vector2 GetDiscPosition(float t) { float ang = t * ShapesMath.TAU; // base angle ang += ShapesMath.TAU * Time.time * 0.25f; // add constant spin rate ang += Mathf.Cos(ang * 2 + Time.time * ShapesMath.TAU * 0.5f) * 0.16f; // add wave offsets~ return(ShapesMath.AngToDir(ang)); // convert angle to a direction/position }
public Vector2 GetShake(float speed, float amp) { float shakeVal = ShapesMath.Frac(Time.time * speed); float shakeX = shakeAnimX.Evaluate(shakeVal); float shakeY = shakeAnimY.Evaluate(shakeVal); return(new Vector2(shakeX, shakeY) * amp); }
public static BasicMeshData GenerateCylinder(int divs) { BasicMeshData mesh = new BasicMeshData(); mesh.normals = new List <Vector3>(); for (int z = 0; z < 2; z++) { for (int i = 0; i < divs; i++) { float t = i / (float)divs; Vector3 v = ShapesMath.AngToDir(t * ShapesMath.TAU); mesh.normals.Add(v); v.z = z; mesh.verts.Add(v); } } // sides for (int i = 0; i < divs; i++) { int low0 = i; int top0 = divs + i; int low1 = (i + 1) % divs; int top1 = divs + (i + 1) % divs; mesh.tris.Add(low0); mesh.tris.Add(low1); mesh.tris.Add(top1); mesh.tris.Add(top1); mesh.tris.Add(top0); mesh.tris.Add(low0); } // cap bottom for (int i = 1; i < divs - 1; i++) { mesh.tris.Add(0); mesh.tris.Add((i + 1) % divs); mesh.tris.Add(i); } // cap top for (int i = 1; i < divs - 1; i++) { mesh.tris.Add(divs + 0); mesh.tris.Add(divs + i); mesh.tris.Add(divs + (i + 1) % divs); } return(mesh); }
public static void DrawRoundedArcOutline(Vector2 origin, float radius, float thickness, float outlineThickness, float angStart, float angEnd) { // inner / outer float innerRadius = radius - thickness / 2; float outerRadius = radius + thickness / 2; const float aaMargin = 0.01f; Draw.Arc(origin, innerRadius, outlineThickness, angStart - aaMargin, angEnd + aaMargin); Draw.Arc(origin, outerRadius, outlineThickness, angStart - aaMargin, angEnd + aaMargin); // rounded caps Vector2 originBottom = origin + ShapesMath.AngToDir(angStart) * radius; Vector2 originTop = origin + ShapesMath.AngToDir(angEnd) * radius; Draw.Arc(originBottom, thickness / 2, outlineThickness, angStart, angStart - ShapesMath.TAU / 2); Draw.Arc(originTop, thickness / 2, outlineThickness, angEnd, angEnd + ShapesMath.TAU / 2); }
void DrawText() { using (new CenterVertical()) { GUI.color = colMain; if (newVersionAvailable != null) { using (ShapesUI.Horizontal) { using (new Center()) { float t = (float)EditorApplication.timeSinceStartup; float wave = ShapesMath.SmoothCos01(Mathf.PingPong(t, 0.5f) * 2); wave = Mathf.Lerp(0.5f, 1f, wave); GUI.color = Color.Lerp(Color.white, colF15, wave); LinkLabel(newVersionAvailable + " now available", ShapesInfo.LINK_CHANGELOG); GUI.color = Color.white; } } } GUILayout.Label($"Shapes {ShapesInfo.Version}", TitleStyle); using (ShapesUI.Horizontal) { using (new Center()) { int year = Mathf.Max(DateTime.Now.Year, 2020); // just in case your computer clock is wonky~ GUILayout.Label($"© {year}", LabelStyle, GUILayout.ExpandWidth(false)); LinkLabel("Freya Holmér", ShapesInfo.LINK_TWITTER); } } GUI.color = Color.white; GUILayout.Space(8); GUILayout.Label("made possible thanks to\nthe wonderful supporters on", LabelCentered, GUILayout.ExpandWidth(true)); using (ShapesUI.Horizontal) { using (new Center()) { LinkLabel("Patreon", ShapesInfo.LINK_PATREON); } } GUI.color = colF15; GUILayout.Label("♥", TitleStyle, GUILayout.ExpandWidth(true)); GUI.color = Color.white; } }
public void DrawBar(FpsController fpsController, float barRadius) { float barThickness = fpsController.ammoBarThickness; float ammoBarOutlineThickness = fpsController.ammoBarOutlineThickness; float angRadMin = -fpsController.ammoBarAngularSpanRad / 2; float angRadMax = fpsController.ammoBarAngularSpanRad / 2; // draw bullets Draw.LineEndCaps = LineEndCap.Round; float innerRadius = barRadius - barThickness / 2; float bulletThickness = (innerRadius * fpsController.ammoBarAngularSpanRad / totalBullets) * bulletThicknessScale; for (int i = 0; i < totalBullets; i++) { float t = i / (totalBullets - 1f); float angRad = Mathf.Lerp(angRadMin, angRadMax, t); Vector2 dir = ShapesMath.AngToDir(angRad); Vector2 origin = dir * barRadius; Vector2 offset = dir * (barThickness / 2f - ammoBarOutlineThickness * 1.5f); float alpha = 1; bool hasBeenFired = i >= bullets; if (hasBeenFired && Application.isPlaying) { float timePassed = Time.time - bulletFireTimes[i]; float tFade = Mathf.Clamp01(timePassed / bulletDisappearTime); alpha = 1f - tFade; origin = GetBulletEjectPos(origin, tFade); float angle = timePassed * (bulletEjectAngSpeed + Mathf.Cos(i * 92372.8f) * ejectRotSpeedVariance); offset = ShapesMath.Rotate(offset, angle); } Vector2 a = origin + offset; Vector2 b = origin - offset; Draw.Line(a, b, bulletThickness, new Color(1, 1, 1, alpha)); } FpsController.DrawRoundedArcOutline(Vector2.zero, barRadius, barThickness, ammoBarOutlineThickness, angRadMin, angRadMax); }
readonly Queue <Matrix4x4> mtxQueue = new Queue <Matrix4x4>(); // queue of pending branches to draw // Draws a line, moves forward, and then queues up new postions to draw from per new branch void BranchFrom(Matrix4x4 mtx) { if (currentLineCount++ >= lineCount) { return; // stop recursion if we hit our limit } Draw.Matrix = mtx; float lineLength = Mathf.Lerp(branchLengthMin, branchLengthMax, Random.value); // random branch length Vector3 offset = new Vector3(0, lineLength, 0); // offset along the local Y axis Draw.Line(Vector3.zero, offset); Draw.Translate(offset); // moves the drawing matrix in its local space // create a random number of branches from the current position int branchCount = Random.Range(branchesMin, branchesMax + 1); for (int i = 0; i < branchCount; i++) { using (Draw.MatrixScope) { // saves the current matrix state, and restores it at the end of this scope float angDeviation = Mathf.Lerp(-maxAngDeviation, maxAngDeviation, ShapesMath.RandomGaussian()); // random angular deviation if (use3D) { Draw.Rotate(angDeviation, ShapesMath.GetRandomPerpendicularVector(Vector3.up)); // rotates the current drawing matrix on a random axis } else { Draw.Rotate(angDeviation); // rotates the current drawing matrix on the Z axis } mtxQueue.Enqueue(Draw.Matrix); // save the drawing matrix to draw a branch with later } } while (mtxQueue.Count > 0) // process all positions in the queue { BranchFrom(mtxQueue.Dequeue()); // draw new branches at the positions saved in the queue } }
void DrawShapes() { Vector2 center = position.size / 2; float fitRadius = Mathf.Min(position.width, position.height) / 2 - 8; // set doot positions float t = (float)EditorApplication.timeSinceStartup / 2; foreach (Doot doot in doots) { float ang = doot.angSpeed * t * ShapesMath.TAU + doot.angOffset; Vector2 dir = ShapesMath.AngToDir(ang); doot.pos = dir * (fitRadius * doot.radialOffset); } // mouse doot~ Vector2 mouseRawPos = Event.current.mousePosition - center; float maxRadius = fitRadius * DOOT_MAX_RADIUS; Vector2 mouseTargetPos = Vector2.ClampMagnitude(mouseRawPos, maxRadius); doots[0].pos = Vector2.Lerp(doots[0].pos, mouseTargetPos, mouseDootT); bool mouseOver = mouseOverWindow == this; mouseDootT = Mathf.Lerp(mouseDootT, mouseOver ? 1f : 0f, 0.05f); // save state Matrix4x4 prevMtx = Draw.Matrix; ShapesBlendMode prevBlendMode = Draw.BlendMode; ThicknessSpace prevDiscRadiusSpace = Draw.DiscRadiusSpace; ThicknessSpace prevLineThicknessSpace = Draw.LineThicknessSpace; LineGeometry prevLineGeometry = Draw.LineGeometry; ThicknessSpace prevRingThicknessSpace = Draw.RingThicknessSpace; // draw setup Draw.Matrix = Matrix4x4.TRS(new Vector3(center.x, center.y, 1f), Quaternion.identity, Vector3.one); Draw.BlendMode = ShapesBlendMode.Transparent; Draw.DiscRadiusSpace = ThicknessSpace.Meters; Draw.LineThicknessSpace = ThicknessSpace.Meters; Draw.LineGeometry = LineGeometry.Flat2D; Draw.RingThicknessSpace = ThicknessSpace.Meters; // Drawing Draw.RingGradientRadial(Vector3.zero, fitRadius, fitRadius * 0.1f, Color.black, new Color(0, 0, 0, 0)); Draw.Disc(Vector3.zero, fitRadius, Color.black); // edge noodles const int noodCount = 64; for (int i = 0; i < noodCount; i++) { float tDir = i / ((float)noodCount); float tAng = ShapesMath.TAU * tDir; Vector2 dir = ShapesMath.AngToDir(tAng); if (Mathf.Abs(dir.y) > 0.75f) { continue; } Vector2 root = dir * fitRadius; float distToNearest = float.MaxValue; for (int j = 0; j < doots.Length; j++) { distToNearest = Mathf.Min(distToNearest, Vector2.Distance(doots[j].pos, root)); } float distMod = Mathf.InverseLerp(fitRadius * 0.5f, fitRadius * 0.1f, distToNearest); float noodMaxOffset = fitRadius * (1 + 0.1f * distMod); Draw.Line(root, dir * noodMaxOffset, fitRadius * Mathf.Lerp(0.07f, 0.04f, distMod)); } // ring Draw.Ring(Vector3.zero, fitRadius, fitRadius * 0.0125f, colMain); // connecting lines for (int i = 0; i < doots.Length; i++) { Vector2 a = doots[i].pos; for (int j = i; j < doots.Length; j++) { Vector2 b = doots[j].pos; float dist = Vector2.Distance(a, b); float rangeValue = Mathf.InverseLerp(fitRadius * 1f, fitRadius * 0.02f, dist); if (rangeValue > 0) { Color col = Color.Lerp(colFade, colMain, rangeValue); Draw.Line(a, b, fitRadius * 0.015f * rangeValue, LineEndCap.Round, col); } } } // doots~ foreach (Doot doot in doots) { Draw.BlendMode = ShapesBlendMode.Transparent; Draw.Disc(doot.pos, fitRadius * 0.025f, Color.black); Draw.Disc(doot.pos, fitRadius * 0.015f, colMain); Draw.BlendMode = ShapesBlendMode.Additive; Color innerColor = colMain; innerColor.a = 0.25f; Color outerColor = Color.clear; Draw.DiscGradientRadial(doot.pos, fitRadius * 0.18f, innerColor, outerColor); } Draw.BlendMode = ShapesBlendMode.Multiplicative; Draw.DiscGradientRadial(Vector3.zero, fitRadius * 0.5f, Color.black, Color.clear); // restore state Draw.Matrix = prevMtx; Draw.BlendMode = prevBlendMode; Draw.DiscRadiusSpace = prevDiscRadiusSpace; Draw.LineThicknessSpace = prevLineThicknessSpace; Draw.LineGeometry = prevLineGeometry; Draw.RingThicknessSpace = prevRingThicknessSpace; }
public static Color GetHandleColor(Color shapeColor) { return(ShapesMath.Luminance(shapeColor) > 0.7f ? Color.black : Color.white); }
public static BasicMeshData GenerateUVSphere(int divsLong, int divsLat) { BasicMeshData mesh = new BasicMeshData(); mesh.normals = new List <Vector3>(); int vertCount = divsLong * divsLat; int triCount = divsLong * (divsLat - 1) * 2 - divsLong * 2; // subtracting is to remove quads at the pole Vector3[] verts = new Vector3[vertCount]; int iVert = 0; // generate verts for (int iLo = 0; iLo < divsLong; iLo++) { float tLong = iLo / (float)divsLong; float angLong = tLong * ShapesMath.TAU; Vector2 dirXZ = ShapesMath.AngToDir(angLong); Vector3 dirLong = new Vector3(dirXZ.x, 0f, dirXZ.y); for (int iLa = 0; iLa < divsLat; iLa++) { float tLat = iLa / (divsLat - 1f); float angLat = Mathf.Lerp(-0.25f, 0.25f, tLat) * ShapesMath.TAU; Vector2 dirProj = ShapesMath.AngToDir(angLat); verts[iVert++] = dirLong * dirProj.x + Vector3.up * dirProj.y; } } // generate tris int[] tris = new int[triCount * 3]; int iTri = 0; for (int iLo = 0; iLo < divsLong; iLo++) { for (int iLa = 0; iLa < divsLat - 1; iLa++) { int iRoot = iLo * divsLat + iLa; int iRootNext = (iRoot + divsLat) % vertCount; if (iLa < divsLat - 2) // skip first and last (triangles at the poles) { tris[iTri++] = iRoot; tris[iTri++] = iRoot + 1; tris[iTri++] = iRootNext + 1; } if (iLa > 0) { tris[iTri++] = iRootNext + 1; tris[iTri++] = iRootNext; tris[iTri++] = iRoot; } } } mesh.verts.AddRange(verts); mesh.tris.AddRange(tris); mesh.normals.AddRange(verts); mesh.RemoveDuplicateVertices(); return(mesh); }
static bool generatingClockwisePolygon; // assigned in GenPolygonMesh, used by EarClipPoint public static void GenPolygonMesh(Mesh mesh, List <Vector2> path, PolygonTriangulation triangulation) { // kinda have to do this, the algorithm relies on knowing this generatingClockwisePolygon = ShapesMath.PolygonSignedArea(path) > 0; mesh.Clear(); // todo maybe not always do this you know? int pointCount = path.Count; if (pointCount < 2) { return; } int triangleCount = pointCount - 2; int triangleIndexCount = triangleCount * 3; int[] meshTriangles = new int[triangleIndexCount]; if (triangulation == PolygonTriangulation.FastConvexOnly) { int tri = 0; for (int i = 0; i < triangleCount; i++) { meshTriangles[tri++] = i + 2; meshTriangles[tri++] = i + 1; meshTriangles[tri++] = 0; } } else { List <EarClipPoint> pointsLeft = new List <EarClipPoint>(pointCount); for (int i = 0; i < pointCount; i++) { pointsLeft.Add(new EarClipPoint(i, new Vector2(path[i].x, path[i].y))); } for (int i = 0; i < pointCount; i++) // update prev/next connections { EarClipPoint p = pointsLeft[i]; p.prev = pointsLeft[(i + pointCount - 1) % pointCount]; p.next = pointsLeft[(i + 1) % pointCount]; } int tri = 0; int countLeft; int safeguard = 1000000; while ((countLeft = pointsLeft.Count) >= 3 && (safeguard-- > 0)) { //for( int k = 0; k < pointsLeft.Count * 2; k++ ) { if (countLeft == 3) { // final triangle meshTriangles[tri++] = pointsLeft[2].vertIndex; meshTriangles[tri++] = pointsLeft[1].vertIndex; meshTriangles[tri++] = pointsLeft[0].vertIndex; break; } // iterate until we find a convex vertex bool foundConvex = false; for (int i = 0; i < countLeft; i++) { EarClipPoint p = pointsLeft[i]; if (p.ReflexState == ReflexState.Convex) { // it's convex! now make sure there are no reflex points inside bool canClipEar = true; int idPrev = (i + countLeft - 1) % countLeft; int idNext = (i + 1) % countLeft; for (int j = 0; j < countLeft; j++) { if (j == i) { continue; // skip self } if (j == idPrev) { continue; // skip next } if (j == idNext) { continue; // skip prev } if (pointsLeft[j].ReflexState == ReflexState.Reflex) { // found a reflex point, make sure it's outside the triangle if (ShapesMath.PointInsideTriangle(p.next.pt, p.pt, p.prev.pt, pointsLeft[j].pt, 0f, -0.0001f, 0f)) { canClipEar = false; // it's inside, rip break; } } } if (canClipEar) { meshTriangles[tri++] = p.next.vertIndex; meshTriangles[tri++] = p.vertIndex; meshTriangles[tri++] = p.prev.vertIndex; p.next.MarkReflexUnknown(); p.prev.MarkReflexUnknown(); (p.next.prev, p.prev.next) = (p.prev, p.next); // update prev/next pointsLeft.RemoveAt(i); foundConvex = true; break; // stop search for more convex edges, restart loop } } } // no convex found?? if (foundConvex == false) { Debug.LogError("Invalid polygon triangulation - no convex edges found. Your polygon is likely self-intersecting"); goto breakBoth; } } breakBoth: if (safeguard < 1) { Debug.LogError("Polygon triangulation failed, please report a bug (Shapes/Report Bug) with this exact case included"); } } // assign to segments mesh List <Vector3> verts3D = new List <Vector3>(pointCount); for (int i = 0; i < pointCount; i++) { verts3D.Add(path[i]); } mesh.SetVertices(verts3D); mesh.subMeshCount = 1; mesh.SetTriangles(meshTriangles, 0); }
public static BasicMeshData GenerateCapsule(int divs) { BasicMeshData mesh = new BasicMeshData(); mesh.normals = new List <Vector3>(); int sides = divs * 4; for (int z = 0; z < 2; z++) { for (int i = 0; i < sides; i++) { float t = i / (float)sides; Vector3 v = ShapesMath.AngToDir(t * ShapesMath.TAU); mesh.normals.Add(v); v.z = z; mesh.verts.Add(v); } } // sides for (int i = 0; i < sides; i++) { int low0 = i; int top0 = sides + i; int low1 = (i + 1) % sides; int top1 = sides + (i + 1) % sides; mesh.tris.Add(low0); mesh.tris.Add(low1); mesh.tris.Add(top1); mesh.tris.Add(top1); mesh.tris.Add(top0); mesh.tris.Add(low0); } // round caps! int n = divs + 1; Vector3[] octaBaseVerts = { Vector3.right, Vector3.up, Vector3.left, Vector3.down }; for (int z = 0; z < 2; z++) { // half-octahedron for (int s = 0; s < 4; s++) { Vector3 v0 = z == 0 ? Vector3.back : Vector3.forward; // reverse depending on z Vector3 v1 = octaBaseVerts[s]; Vector3 v2 = octaBaseVerts[(s + 1) % 4]; Vector3[] verts = BarycentricVertices(n, v0, v1, v2).ToArray(); mesh.normals.AddRange(verts); if (z == 0) { mesh.tris.AddRange(BarycentricTriangulation(n, mesh.verts.Count).Reverse()); mesh.verts.AddRange(verts.Select(x => x)); } else { mesh.tris.AddRange(BarycentricTriangulation(n, mesh.verts.Count)); mesh.verts.AddRange(verts.Select(x => x + Vector3.forward)); } } } mesh.RemoveDuplicateVertices(); return(mesh); }
public void DrawCompass(Vector3 worldDir) { // prepare all variables Vector2 compArcOrigin = position + Vector2.down * bendRadius; float angUiMin = ShapesMath.TAU * 0.25f - (width / 2) / bendRadius; float angUiMax = ShapesMath.TAU * 0.25f + (width / 2) / bendRadius; Vector2 dirWorld = new Vector2(worldDir.x, worldDir.z).normalized; float lookAng = ShapesMath.DirToAng(dirWorld); float angWorldMin = lookAng + fieldOfView / 2; float angWorldMax = lookAng - fieldOfView / 2; Vector2 labelPos = compArcOrigin + Vector2.up * (bendRadius) + lookAngLabelOffset * 0.1f; string lookLabel = Mathf.RoundToInt(-lookAng * Mathf.Rad2Deg + 180f) + "°"; // prepare draw state Draw.LineEndCaps = LineEndCap.Square; Draw.Thickness = lineThickness; // draw the horizontal line/arc of the compass Draw.Arc(compArcOrigin, bendRadius, lineThickness, angUiMin, angUiMax, ArcEndCap.Round); // draw the look angle label Draw.FontSize = fontSizeLookLabel; Draw.Text(labelPos, 0f, lookLabel, TextAlign.Center); // triangle arrow Vector2 trianglePos = compArcOrigin + Vector2.up * (bendRadius + 0.01f); Draw.RegularPolygon(trianglePos, 3, triangleNootSize, -ShapesMath.TAU / 4); // draw ticks int tickCount = (ticksPerQuarterTurn - 1) * 4; for (int i = 0; i < tickCount; i++) { float t = i / ((float)tickCount); float ang = ShapesMath.TAU * t; bool cardinal = i % (tickCount / 4) == 0; string label = null; if (cardinal) { int angInt = Mathf.RoundToInt((1f - t) * 4); label = directionLabels[angInt % 4]; } float tCompass = ShapesMath.InverseLerpAngleRad(angWorldMax, angWorldMin, ang); if (tCompass < 1f && tCompass > 0f) { DrawTick(ang, cardinal ? 0.8f : 0.5f, label); } } void DrawTick(float worldAng, float size, string label = null) { float tCompass = ShapesMath.InverseLerpAngleRad(angWorldMax, angWorldMin, worldAng); float uiAng = Mathf.Lerp(angUiMin, angUiMax, tCompass); Vector2 uiDir = ShapesMath.AngToDir(uiAng); Vector2 a = compArcOrigin + uiDir * bendRadius; Vector2 b = compArcOrigin + uiDir * (bendRadius - size * tickSize); float fade = Mathf.InverseLerp(0, tickEdgeFadeFraction, (1f - Mathf.Abs(tCompass * 2 - 1))); Draw.Line(a, b, LineEndCap.None, new Color(1, 1, 1, fade)); if (label != null) { Draw.FontSize = fontSizeTickLabel; Draw.Text(b - uiDir * tickLabelOffset, uiAng - ShapesMath.TAU / 4f, label, TextAlign.Center, new Color(1, 1, 1, fade)); } } }
public void DrawCompass(Vector3 worldDir) { Vector2 compArcOrigin = position + Vector2.down * bendRadius; float angUiMin = ShapesMath.TAU * 0.25f - (width / 2) / bendRadius; float angUiMax = ShapesMath.TAU * 0.25f + (width / 2) / bendRadius; Vector2 dirWorld = new Vector2(worldDir.x, worldDir.z).normalized; float lookAng = ShapesMath.DirToAng(dirWorld); float angWorldMin = lookAng + fieldOfView / 2; float angWorldMax = lookAng - fieldOfView / 2; Draw.Arc(compArcOrigin, bendRadius, lineThickness, angUiMin, angUiMax, ArcEndCap.Round); void CompassArcNoot(float worldAng, float size, string label = null) { float tCompass = ShapesMath.InverseLerpAngleRad(angWorldMax, angWorldMin, worldAng); float uiAng = Mathf.Lerp(angUiMin, angUiMax, tCompass); Vector2 uiDir = ShapesMath.AngToDir(uiAng); Vector2 a = compArcOrigin + uiDir * bendRadius; Vector2 b = compArcOrigin + uiDir * (bendRadius - size * tickSize); float fade = Mathf.InverseLerp(0, tickEdgeFadeFraction, (1f - Mathf.Abs(tCompass * 2 - 1))); Draw.Line(a, b, LineEndCap.None, new Color(1, 1, 1, fade)); if (label != null) { Draw.FontSize = fontSizeTickLabel; Draw.Text(b - uiDir * tickLabelOffset, uiAng - ShapesMath.TAU / 4f, label, TextAlign.Center, new Color(1, 1, 1, fade)); } } Draw.LineEndCaps = LineEndCap.Square; Draw.LineThickness = lineThickness; Vector2 trianglePos = compArcOrigin + Vector2.up * (bendRadius + 0.01f); Vector2 labelPos = compArcOrigin + Vector2.up * (bendRadius) + lookAngLabelOffset * 0.1f; string lookLabel = Mathf.RoundToInt(-lookAng * Mathf.Rad2Deg + 180f) + "°"; Draw.FontSize = fontSizeLookLabel; Draw.Text(labelPos, 0f, lookLabel, TextAlign.Center); Vector2 triA = trianglePos + ShapesMath.AngToDir(-ShapesMath.TAU / 4) * triangleNootSize; Vector2 triB = trianglePos + ShapesMath.AngToDir(-ShapesMath.TAU / 4 + ShapesMath.TAU / 3) * triangleNootSize; Vector2 triC = trianglePos + ShapesMath.AngToDir(-ShapesMath.TAU / 4 + 2 * ShapesMath.TAU / 3) * triangleNootSize; Draw.Triangle(triA, triB, triC); int tickCount = (ticksPerQuarterTurn - 1) * 4; for (int i = 0; i < tickCount; i++) { float t = i / ((float)tickCount); float ang = ShapesMath.TAU * t; bool cardinal = i % (tickCount / 4) == 0; string label = null; if (cardinal) { int angInt = Mathf.RoundToInt((1f - t) * 4); switch (angInt) { case 0: case 4: label = "S"; break; case 1: label = "W"; break; case 2: label = "N"; break; case 3: label = "E"; break; } } float tCompass = ShapesMath.InverseLerpAngleRad(angWorldMax, angWorldMin, ang); if (tCompass < 1f && tCompass > 0f) { CompassArcNoot(ang, cardinal ? 0.8f : 0.5f, label); } } }
public void DrawBar(FpsController fpsController, float barRadius) { // get some data float barThickness = fpsController.ammoBarThickness; float ammoBarOutlineThickness = fpsController.ammoBarOutlineThickness; float angRadMin = -fpsController.ammoBarAngularSpanRad / 2; float angRadMax = fpsController.ammoBarAngularSpanRad / 2; float angRadMinLeft = angRadMin + ShapesMath.TAU / 2; float angRadMaxLeft = angRadMax + ShapesMath.TAU / 2; float outerRadius = barRadius + barThickness / 2; float chargeAnim = chargeFillCurve.Evaluate(charge); // charge bar shake: float chargeMag = animChargeShakeMagnitude.Evaluate(chargeAnim) * chargeShakeMagnitude; Vector2 origin = fpsController.GetShake(chargeShakeSpeed, chargeMag); // do shake here float chargeAngRad = Mathf.Lerp(angRadMaxLeft, angRadMinLeft, chargeAnim); Color chargeColor = chargeFillGradient.Evaluate(chargeAnim); Draw.Arc(origin, fpsController.ammoBarRadius, barThickness, angRadMaxLeft, chargeAngRad, chargeColor); Vector2 movingLeftPos = origin + ShapesMath.AngToDir(chargeAngRad) * barRadius; Vector2 bottomLeftPos = origin + ShapesMath.AngToDir(angRadMaxLeft) * barRadius; // bottom fill Draw.Disc(bottomLeftPos, barThickness / 2f, chargeColor); // ticks const int tickCount = 7; Draw.LineEndCaps = LineEndCap.None; for (int i = 0; i < tickCount; i++) { float t = i / (tickCount - 1f); float angRad = Mathf.Lerp(angRadMaxLeft, angRadMinLeft, t); Vector2 dir = ShapesMath.AngToDir(angRad); Vector2 a = origin + dir * outerRadius; bool lorge = i % 3 == 0; Vector2 b = a + dir * (lorge ? tickSizeLorge : tickSizeSmol); Draw.Line(a, b, tickTickness, tickColor); // scale based on distance to real value float chargeDelta = t - chargeAnim; float growRange = chargeDelta < 0 ? fontGrowRangePrev : fontGrowRangeNext; float tFontScale = 1f - ShapesMath.SmoothCos01(Mathf.Clamp01(Mathf.Abs(chargeDelta) / growRange)); float fontScale = ShapesMath.Eerp(fontSize, fontSizeLorge, tFontScale); Draw.FontSize = fontScale; Vector2 labelPos = a + dir * percentLabelOffset; string pct = Mathf.RoundToInt(t * 100) + "%"; Draw.Text(labelPos, angRad + ShapesMath.TAU / 2, pct, TextAlign.Right); } // moving dot Draw.Disc(movingLeftPos, barThickness / 2f + ammoBarOutlineThickness / 2f); Draw.Disc(movingLeftPos, barThickness / 2f - ammoBarOutlineThickness / 2f, chargeColor); FpsController.DrawRoundedArcOutline(origin, barRadius, barThickness, ammoBarOutlineThickness, angRadMinLeft, angRadMaxLeft); Draw.LineEndCaps = LineEndCap.Round; // glow Draw.BlendMode = ShapesBlendMode.Additive; Draw.DiscGradientRadial(movingLeftPos, barThickness * 2, chargeColor, Color.clear); Draw.BlendMode = ShapesBlendMode.Transparent; }