Hello,
I tried to create a script that modifies the mesh of a 3D object during runtime so that it follows the shape of the terrain underneath it.
Currently, I’ve achieved this result (before/after):
The model correctly follows the curve of the ground, but the thickness is not preserved at all. The model becomes very thin, and all details are lost.
I’ve created a diagram showing what’s happening vs. what should happen:
Here’s the current code:
public LayerMask groundLayer;
public float maxRaycastDistance = 100f;
public float deformationStrength = 1.0f;
private MeshFilter meshFilter;
private Mesh originalMeshe;
private Mesh workingMeshe;
private Vector3[] originalVertice;
private Vector3[] deformedVertice;
public float height = 3f;
private void InitializeMeshe()
{
meshFilter = GetComponent<MeshFilter>();
var meshRenderers = GetComponent<MeshRenderer>();
height = meshRenderers.bounds.size.y;
originalMeshe = meshFilter.sharedMesh;
workingMeshe = Instantiate(originalMeshe);
originalVertice = workingMeshe.vertices;
deformedVertice = new Vector3[originalVertice.Length];
meshFilter.mesh = workingMeshe;
}
public void DeformMeshe()
{
Vector3[] deformedVerts = new Vector3[originalVertice.Length];
for (int j = 0; j < originalVertice.Length; j++)
{
Vector3 worldPos = meshFilter.transform.TransformPoint(originalVertice[j]);
Vector3 closestPoint = worldPos;
Vector3? groundHeight = GetGroundHeight(worldPos);
if (groundHeight == null)
{
deformedVerts[j] = originalVertice[j];
continue;
}
float closestDistance = Vector3.Distance(worldPos, groundHeight.Value);
if (closestDistance < maxRaycastDistance) closestPoint = groundHeight.Value;
Vector3 targetPosition = Vector3.Lerp(worldPos, closestPoint, deformationStrength);
targetPosition.y += height;
deformedVerts[j] = meshFilter.transform.InverseTransformPoint(targetPosition);
}
workingMeshe.vertices = deformedVerts;
workingMeshe.RecalculateNormals();
workingMeshe.RecalculateBounds();
}
public Vector3? GetGroundHeight(Vector3 worldPos)
{
if (Physics.Raycast(worldPos, Vector3.down, out RaycastHit hitDown, maxRaycastDistance, groundLayer))
return hitDown.point;
if (Physics.Raycast(worldPos, Vector3.up, out RaycastHit hitUp, maxRaycastDistance, groundLayer))
return hitUp.point;
return null;
}
- The initial technique involves casting raycasts toward the ground to determine its height and adjusting the Y position of the vertices accordingly.
- I tried using the Normals to maintain a consistent distance between the points.
Vector3 normalWorld = meshFilter.transform.TransformDirection(vertexNormals[j]);
float originalDistance = Vector3.Dot(originalVertice[j], vertexNormals[j]);
Vector3 adjustedPosition = closestPoint + normalWorld * originalDistance;
Vector3 targetPosition = Vector3.Lerp(worldPos, adjustedPosition, deformationStrength);
- Still using the Normals, I attempted to move the points along a plane. The result is identical to attempt 2.
Vector3 displacement = closestPoint - worldPos;
Vector3 displacementOnPlane = displacement - Vector3.Dot(displacement, worldNormal) * worldNormal;
Vector3 targetPosition = worldPos + displacementOnPlane * deformationStrength;
targetPosition += worldNormal * height;
How can I fix my code to achieve my goal?