Hi there,
I’ve the following algorithm to generate ripples on a sphere’s surface. Technically, it works. However, it’s extremely slow, and I was wondering about how to best optimize it (apart from removing the obvious overhead of the dictionary).
Would it be better if I applied the algorithm to a plane and projected it somehow to the surface? Or is the elimination of the dictionaries be enough.
public class Ripple2D : MonoBehaviour
{
private Vector3[] _vertices;
private Edge[] _edges;
private Mesh mesh;
private Vector3 center;
private Dictionary<Vector3, RipplePoint> _ripples = new Dictionary<Vector3, RipplePoint>();
public int MaxHeight = 1;
public float Dampening = 0.9f;
public float WaveSize = 0.02f;
// Use this for initialization
void Start()
{
var mf = GetComponent<MeshFilter>();
mesh = mf.mesh;
_vertices = mesh.vertices;
center = transform.position;
_edges = GetMeshEdges(mesh);
}
// Update is called once per frame
void Update()
{
var newVertices = new Vector3[_vertices.Length];
CheckInput();
var VertexChange = new Dictionary<Vector3, Vector3>();
var newRipples = new List<RipplePoint>();
var toRemove = (from ripple in _ripples where ripple.Value.Force.sqrMagnitude < 0.0000001 select ripple.Key).ToList();
toRemove.ForEach(x => _ripples.Remove(x));
foreach (var ripple in _ripples)
{
ripple.Value.Frame++;
var vector = ripple.Key;
var ripplePoint = ripple.Value;
var nearby = NearbyVertices(vector);
VertexChange.Add(vector, ripplePoint.Force);
ripplePoint.Force = ripplePoint.Mul * ripplePoint.Force * Dampening;
newRipples.AddRange(nearby.Where(vector3 => (!_ripples.ContainsKey(vector3) || _ripples[vector3].Force.magnitude < ripplePoint.Force.magnitude) && newRipples.All(x => x.Center != vector3)).Select(vector3 => new RipplePoint()
{
Center = vector3,
Force = ripplePoint.Mul * ripplePoint.Force.magnitude * vector3.normalized * Dampening
}));
mesh.vertices = newVertices;
}
newRipples.ForEach(x =>
{
if (!_ripples.ContainsKey(x.Center)) _ripples.Add(x.Center, x);
else
{
_ripples[x.Center].Force = x.Force;
_ripples[x.Center].Frame = x.Frame;
}
});
for (int i = 0; i < _vertices.Length; i++)
{
if (VertexChange.ContainsKey(_vertices[i]))
{
newVertices[i] = _vertices[i] + VertexChange[_vertices[i]];
}
else
{
newVertices[i] = _vertices[i];
}
}
mesh.vertices = newVertices;
}
void CheckInput()
{
if (Input.GetMouseButton(0))
{
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
float minDistanceSqr = Mathf.Infinity;
Vector3 nearestVertex = Vector3.zero;
var point = hit.point;
foreach (Vector3 vertex in _vertices)
{
Vector3 diff = point - vertex;
float distSqr = diff.sqrMagnitude;
if (distSqr < minDistanceSqr)
{
minDistanceSqr = distSqr;
nearestVertex = vertex;
}
}
if (!_ripples.ContainsKey(nearestVertex))
{
_ripples.Add(nearestVertex, new RipplePoint()
{
Center = nearestVertex,
Force = hit.normal * WaveSize,
});
}
else
{
_ripples[nearestVertex].Force = hit.normal * WaveSize;
}
}
}
}
private Vector3[] NearbyVertices(Vector3 vector)
{
return _edges.Where(x => x.v1 == vector || x.v2 == vector).Select(x => x.v1 == vector ? x.v2 : x.v1).ToArray();
}
private class RipplePoint
{
public Vector3 Center;
public Vector3 Force;
public float ChangeSpeed;
public int Frame;
public float Mul
{
get
{
Debug.Log(Frame / 10f);
return Mathf.Cos((Frame/10f)*Mathf.PI);
}
}
}
public struct Edge
{
public Vector3 v1;
public Vector3 v2;
public Edge(Vector3 v1, Vector3 v2)
{
if (v1.x < v2.x || (v1.x == v2.x && (v1.y < v2.y || (v1.y == v2.y && v1.z <= v2.z))))
{
this.v1 = v1;
this.v2 = v2;
}
else
{
this.v1 = v2;
this.v2 = v1;
}
}
}
private Edge[] GetMeshEdges(Mesh mesh)
{
HashSet<Edge> edges = new HashSet<Edge>();
for (int i = 0; i < mesh.triangles.Length; i += 3)
{
var v1 = mesh.vertices[mesh.triangles[i]];
var v2 = mesh.vertices[mesh.triangles[i + 1]];
var v3 = mesh.vertices[mesh.triangles[i + 2]];
edges.Add(new Edge(v1, v2));
edges.Add(new Edge(v1, v3));
edges.Add(new Edge(v2, v3));
}
return edges.ToArray();
}