Example #1
0
        [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();
        }
Example #3
0
        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);
        }
Example #4
0
        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);
        }
Example #5
0
        [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);
            }
        }
Example #6
0
        [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
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        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;
            }
        }
Example #12
0
        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);
        }
Example #13
0
        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);
 }
Example #16
0
        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);
        }
Example #18
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);
        }
Example #19
0
        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));
                }
            }
        }
Example #20
0
        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);
                }
            }
        }
Example #21
0
        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;
        }