using KdTree.Math; using System.Collections; using System.Collections.Generic; using UnityEngine; public class Pulse : MonoBehaviour { private static Vector3[] randomSpherePoints = null; private static KdTree.KdTree<float, int> randomSphereKd = new KdTree.KdTree<float, int>(3, new SphereMath()); private static Dictionary<int, List<Vector3>> separatedPoints = new Dictionary<int, List<Vector3>>(); private class SphereMath : FloatMath { public override float MinValue => -1; public override float MaxValue => 1; public override float DistanceSquaredBetweenPoints(float[] a, float[] b) { Vector3 normA = new Vector3(a[0], a[1], a[2]); Vector3 normB = new Vector3(b[0], b[1], b[2]); return Mathf.Atan2(Vector3.Cross(normA, normB).magnitude, Vector3.Dot(normA, normB)); } } private static int randomSpherePointsCount = 300; private static void GenerateRandomPoints() { if (randomSpherePoints != null) return; randomSpherePoints = new Vector3[randomSpherePointsCount]; for (int i = 0; i < randomSpherePointsCount; i++) { bool ok = false; while (!ok) { Vector3 r = UnityEngine.Random.insideUnitSphere; if (r.sqrMagnitude > 0.000001f) { ok = true; r.Normalize(); randomSpherePoints[i] = r; randomSphereKd.Add(new float[] { r.x, r.y, r.z }, 0); } } } } /* private class KdTreePoints : IEnumerable<Vector3> { private class KdTreeEnumerator : IEnumerator<Vector3> { public Vector3 Current => new Vector3(underlying.Current.Point[0], underlying.Current.Point[1], underlying.Current.Point[2]); object IEnumerator.Current => Current; public void Dispose() => underlying.Dispose(); public bool MoveNext() => underlying.MoveNext(); public void Reset() => underlying.Reset(); private IEnumerator<KdTree.KdTreeNode<float, int>> underlying; public KdTreeEnumerator(KdTree.KdTree<float, int> tree) { this.underlying = tree.GetEnumerator(); } } private KdTree.KdTree<float, int> tree; public KdTreePoints(KdTree.KdTree<float, int> tree) { this.tree = tree; } public IEnumerator<Vector3> GetEnumerator() => new KdTreeEnumerator(tree); IEnumerator IEnumerable.GetEnumerator() => new KdTreeEnumerator(tree); } */ private const float R1_SPHERE_AREA = 4 * Mathf.PI; private static float ArcLengthOfArea(float area) { // area = 2πd^2(1−cos(l / d)), where d = 1 is the sphere radius, and l is the arclength (disk radius) // area = 2π(1-cos(l)) // area = 2π-2πcos(l) // 2π-area=2πcos(l) // (2π-area)/2π=cos(l) // 1-(area/2π)=cos(l) // l = acos(1-area/2π) return Mathf.Acos(1 - area / (Mathf.PI * 2)); } private static IEnumerable<Vector3> GetNumRandomPointsOnSphere(int numPoints) { if (separatedPoints.ContainsKey(numPoints)) return separatedPoints[numPoints]; GenerateRandomPoints(); HashSet<Vector3> remaining = new HashSet<Vector3>(randomSpherePoints); List<Vector3> points = new List<Vector3>(); float areaPerPoint = R1_SPHERE_AREA / numPoints; float arclengthPerPoint = ArcLengthOfArea(areaPerPoint); while (points.Count < numPoints && remaining.Count > 0) { int random = Random.Range(0, remaining.Count); foreach (Vector3 r in remaining) { if (random == 0) { points.Add(r); remaining.Remove(r); foreach (KdTree.KdTreeNode<float, int> close in randomSphereKd.RadialSearch(new float[] { r.x, r.y, r.z }, arclengthPerPoint)) { remaining.Remove(new Vector3(close.Point[0], close.Point[1], close.Point[2])); } break; } else { random--; } } } separatedPoints[numPoints] = points; return separatedPoints[numPoints]; } public float Lobes = 10; public float TimeScale = 1; public float Amount = 0.6f; private Mesh mesh; private Vector3[] originalPoints; private Vector3[] originalNormals; private Vector3[] points; // Start is called before the first frame update void Start() { MeshFilter mf = GetComponent<MeshFilter>(); //mesh = GameObject.Instantiate(mf.mesh); //mf.mesh = mesh; mesh = mf.mesh; mesh.RecalculateNormals(); originalPoints = mesh.vertices; originalNormals = mesh.normals; points = new Vector3[originalPoints.Length]; } // Update is called once per frame void Update() { for (int i = 0; i < originalPoints.Length; i++) { Vector3 o = originalPoints[i]; Vector3 n = originalNormals[i]; float angleUp = Mathf.Atan2(new Vector2(o.x, o.z).magnitude, o.y); float expand = Mathf.Sin(Mathf.Atan2(o.x, o.z) * Mathf.Round(Lobes * angleUp) * Mathf.Sin(angleUp * Lobes) + Time.realtimeSinceStartup * Mathf.PI * TimeScale) * Amount; points[i] = o + n * expand; } mesh.vertices = points; mesh.RecalculateNormals(); mesh.RecalculateBounds(); } }