Hey guys, i have been struggling to figure out my issue with mesh deformation on collision for hours now and I don’t get it. I have two rigid body spheres dropping on cars, i move each vertex on the cars in the direction of the collision normal within a certain radius. Looks great on the red car I found on the asset store but the other car that an acquaintance I’m doing a project with just look messed up. I believe this has something to do with that the red car has scale 1 and the black car has lots of different scales on th different parts. But I can’t really figure out how to handle this correctly. I would really appreciate if someone with better understanding of how to handle this could help me figure this out.
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Assets.Scripts
{
public class ImpulseMeshDeformer : MonoBehaviour
{
private float _deformScalar = 0.1f;
// Only deform vertices that are within this distance from the collision
private float maxVertexCollisionPointDistance = 0.5f;
// List of all mesh filters who's mesh we want to deform
[SerializeField] private List<MeshFilter> _meshFilters;
[SerializeField] private float _minimumImpulseToCauseDeformation = 50f;
// List of vertices in the mesh as it was before applying damage
private List<Vector3[]> originalMeshData;
// List of vertices in the damaged mesh
List<Vector3[]> damagedMeshData;
// Preallocate memory to hold contact points to reduce the need for memory allocation and garbage
// at runtime
private readonly List<ContactPoint> _collisionContacts = new List<ContactPoint>(50);
// Only allow a vertice to be moved this far from its original point
private float _maxDeformOffset = 0.5f;
void Start()
{
if (_meshFilters.Count == 0)
_meshFilters = GetComponentsInChildren<MeshFilter>(true).ToList();
// Initialize a list of original
originalMeshData = new List<Vector3[]>(_meshFilters.Count);
damagedMeshData = new List<Vector3[]>(_meshFilters.Count);
// We need a original list of vertices for each mesh filter as well as a damaged list
// Loop trough the mesh filters and copy their vertice data to our damaged and original mesh
// struct / class
foreach (var meshFilter in _meshFilters)
{
originalMeshData.Add(meshFilter.mesh.vertices);
damagedMeshData.Add(meshFilter.mesh.vertices);
}
}
public void OnCollisionEnter(Collision collision) => DeformMeshes(collision);
void DeformMeshes(Collision collision)
{
int nrOfContactPoints = collision.GetContacts(_collisionContacts);
if (collision.impulse.magnitude < _minimumImpulseToCauseDeformation)
return;
Debug.Log("Impulse mesh magnitude: " + collision.impulse.magnitude);
// Loop trough the list of all mesh filters in the vehicle.
for (var meshNr = 0; meshNr < _meshFilters.Count; meshNr++)
{
var meshFilter = _meshFilters[meshNr];
// Skip inactive / empty mesh filters
if (meshFilter.mesh != null && !meshFilter.gameObject.activeSelf)
continue;
// Loop trough all contact points of the collision.
for (var index = 0; index < nrOfContactPoints; index++)
{
var collisionContact = _collisionContacts[index];
// Calculate the collision direction in local coordinate system of the car.
Vector3 localDirection = meshFilter.transform
.InverseTransformDirection(collisionContact.normal);
// Change the collision point to local coordinates of the meshs object.
Vector3 localContactPoint =
meshFilter.transform.InverseTransformPoint(collisionContact.point);
// Calculate the amount meshes should be deformed by taking the local collision direction
// and scaling
// it with the impulse and then multiplying it with scaling modifier.
var deformVector = localDirection * _deformScalar * collision.impulse.magnitude;
// Attempt to adapt movement to scale but does not seem to help
deformVector.x /= meshFilter.transform.localScale.x;
deformVector.y /= meshFilter.transform.localScale.y;
deformVector.z /= meshFilter.transform.localScale.z;
//Debug.Log("Divided with scale " + meshFilter.transform.localScale);
// Go trough each vertices in the meshFilters mesh.
for (int vertexNr = 0; vertexNr < damagedMeshData[meshNr].Length; vertexNr++)
{
// If distance between the collision point and the vertex is too far, ignore
// this vertex and move on to the next one.
if ((localContactPoint - damagedMeshData[meshNr][vertexNr]).magnitude
> maxVertexCollisionPointDistance)
continue;
// Modify the vertices of the mesh with the deformVector calculated for this collision point.
damagedMeshData[meshNr][vertexNr] += deformVector;
// Calculate the distance this vertex has moved compared to when the mesh was
// undamaged.
// If distance to original position is greater than the distance limit
Vector3 originalToDamageVertexVector =
damagedMeshData[meshNr][vertexNr] - originalMeshData[meshNr][vertexNr];
var directionToDamagedVertex = originalToDamageVertexVector.normalized;
var distanceToOriginalPoint = originalToDamageVertexVector.magnitude;
if (distanceToOriginalPoint > _maxDeformOffset)
{
// Clamp deform offset to _meshDeformOffset distance
damagedMeshData[meshNr][vertexNr] = originalMeshData[meshNr][vertexNr] +
directionToDamagedVertex * _maxDeformOffset;
}
}
}
// Update the mesh with the new vertices. Recalculate normals as well so light is
// reflected correctly.
_meshFilters[meshNr].mesh.SetVertices(damagedMeshData[meshNr]);
_meshFilters[meshNr].mesh.RecalculateNormals();
}
}
}
}
Here is a link to a minimal project demonstrating the issue:
https://github.com/endasil/car-colision-comparsion
the script in question car-colision-comparsion/Assets/Scripts/ImpulseMeshDeformer.cs at main · endasil/car-colision-comparsion · GitHub