REMOVE
Maybe this can be a good start
This isn’t exactly trivial, but in any case you want to keep the entire mesh at your disposal and readily available for your interrogation.
This is because the mesh that is used by Unity is optimized for rendering purposes (aka flattened out), however you want to interrogate topological relationships which are absent.
HOWEVER, in your specific case – because you’re testing a sphere (planet) against a sphere (crater) – there is one nifty mathematical convenience that you can employ to your advantage: the vertices you need can be isolated with a simple distance check!
So what you need is a “culling” method, a method which gets the full assortment of your mesh’s vertices, and removes everything that is too far away from some arbitrary point in space (say point of impact). Now because we don’t want to actually modify the original list of vertices, but we want only to highlight them, we don’t want to actually “cull” anything.
But when you really think about it, you don’t want a list either nor you want to allocate a new structure in the memory, because you really want to walk through the original vertices and ask them, on the fly, one by one, “are you affected by this point of impact?”
So this is how you do it (code assumes your planet mesh is centered on world origin)
public void sphereDeformer(Vector3[] verts, Vector3 impactCenter, float impactRadius, float deformationStrength) {
var sqrRadius = impactRadius * impactRadius;
for(int i = 0; i < verts.Length; i++) {
var v = verts[i];
var diff = v - impactCenter;
var sqrDist = Vector3.Dot(diff, diff); // or just diff.sqrMagnitude
if(sqrDist <= sqrRadius) {
var deform = deformationStrength * v.normalized;
v += deform;
}
verts[i] = v;
}
}
Now you just need additional code to handle swapping the new vertices in your mesh properly. Maybe you make a duplicate, or you change your mesh on the fly (while keeping the original in memory for future reference).
There are also ways and ways to make the deformationStrength fall off from the impact center, which makes it less abrupt. This is how you might do this
if(sqrDist <= sqrRadius) {
var t = Mathf.Clamp01(1f - Mathf.Sqrt(sqrDist) / impactRadius);
var deform = t * deformationStrength * v.normalized;
v += deform;
}
If you want the crater to have a semi-circle cross-section, you might want to do this instead
if(sqrDist <= sqrRadius) {
var t = Mathf.Cos(Mathf.Clamp01(1f - Mathf.Sqrt(sqrDist) / impactRadius) * Mathf.Pi * .5f);
var deform = t * deformationStrength * v.normalized;
v += deform;
}
Now I’m typing this from my head, I’m sorry if it has errors. I can fix them at a later point.
If you want this to work with a planet set to an arbitrary center in space i.e. planetCenter
you just do
var v = verts[i] - planetCenter;
and later
verts[i] = v + planetCenter;
You also need to adjust your impactCenter to this local space, before the for loop starts
impactCenter -= planetCenter;
All in all
public void sphereDeformer(Vector3 sphereCenter, Vector3[] verts, Vector3 impactCenter, float impactRadius, float deformationStrength, float slopeAngleDegrees = 90) {
var sqrRadius = impactRadius * impactRadius;
var invRadius = 1f / impactRadius;
impactCenter -= sphereCenter;
var angleRad = Mathf.Clamp(slopeAngleDegrees, 0f, 90f) * Mathf.Deg2Rad;
for(int i = 0; i < verts.Length; i++) {
var v = verts[i] - sphereCenter;
var diff = v - impactCenter;
if(diff.sqrMagnitude <= sqrRadius) {
var t = shapeFunc(Mathf.Clamp01(1f - diff.magnitude * invRadius), angleRad);
v += t * deformationStrength * v.normalized;
}
verts[i] = v + sphereCenter;
}
}
float shapeFunc(float t, float angle)
=> Mathf.Cos(t * angle);
If you adjust shapeFunc to something else (example), you can even make crater ridges and whatnot.
edit:
v.normalized should really be a negative unless you want to make hills
v += t * deformationStrength * -v.normalized
or alternatively
v -= t * deformationStrength * v.normalized
(or even more alternatively)
var v = sphereCenter - verts[i]; // reads as "from vertex to center" which is inward
I messed that up, sorry.
Ultimately you can always decide that deformationStrength should be negative for inward deformation, that’s also fine, in that case I made no error
Here’s a slight better shapeFunc (I also forgot to normalize cos). It’s a bit costly, but it’s just something to play with.
float shapeFunc(float t, float angle = 1.048f, float w = 0.5f, float q = 5f)
=> Mathf.Pow(Mathf.Cos(Mathf.Pow(t, Mathf.Clamp(w, 0f, 10f)) * angle), Mathf.Clamp(q, 0f, 10f));