using System;
using UnityEngine;
namespace Sbrt.Av.Gameplay
{
[DisallowMultipleComponent]
[RequireComponent(typeof(MeshFilter))]
public class MeshHeightStretcher : MonoBehaviour
{
[SerializeField] private bool recalculateNormals = true;
[SerializeField] private bool recalculateTangents = true;
[SerializeField] private bool updateMeshCollider = true;
private MeshFilter _meshFilter;
private MeshCollider _meshCollider;
private Mesh _runtimeMesh;
private Vector3[] _originalVertices;
private float _originalMinY;
private float _originalHeight;
private bool _isInitialized;
private void Awake() =>
Initialize();
public void Stretch(float heightMultiplier)
{
Initialize();
if (heightMultiplier <= 0f)
throw new ArgumentOutOfRangeException(nameof(heightMultiplier),
"Height multiplier must be greater than zero.");
if (_originalHeight <= Mathf.Epsilon)
return;
var stretchedVertices = new Vector3[_originalVertices.Length];
for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
{
var originalVertex = _originalVertices[vertexIndex];
var offsetFromBottom = originalVertex.y - _originalMinY;
var stretchedY = _originalMinY + offsetFromBottom * heightMultiplier;
stretchedVertices[vertexIndex] = new Vector3(
originalVertex.x,
stretchedY,
originalVertex.z);
}
ApplyVertices(stretchedVertices);
}
public void ResetStretch()
{
Initialize();
var restoredVertices = new Vector3[_originalVertices.Length];
Array.Copy(_originalVertices, restoredVertices, _originalVertices.Length);
ApplyVertices(restoredVertices);
}
private void Initialize()
{
if (_isInitialized)
return;
_meshFilter = GetComponent<MeshFilter>();
_meshCollider = GetComponent<MeshCollider>();
if (_meshFilter.sharedMesh == null)
throw new InvalidOperationException($"Object '{name}' does not contain a mesh in MeshFilter.");
_runtimeMesh = Instantiate(_meshFilter.sharedMesh);
_runtimeMesh.name = $"{_meshFilter.sharedMesh.name}_RuntimeStretched";
_meshFilter.sharedMesh = _runtimeMesh;
_originalVertices = _runtimeMesh.vertices;
if (_originalVertices == null || _originalVertices.Length == 0)
throw new InvalidOperationException($"Mesh on object '{name}' does not contain vertices.");
_originalMinY = float.MaxValue;
var originalMaxY = float.MinValue;
for (int vertexIndex = 0; vertexIndex < _originalVertices.Length; vertexIndex++)
{
var vertexY = _originalVertices[vertexIndex].y;
if (vertexY < _originalMinY)
_originalMinY = vertexY;
if (vertexY > originalMaxY)
originalMaxY = vertexY;
}
_originalHeight = originalMaxY - _originalMinY;
_isInitialized = true;
UpdateMeshCollider();
}
private void ApplyVertices(Vector3[] vertices)
{
_runtimeMesh.vertices = vertices;
_runtimeMesh.RecalculateBounds();
if (recalculateNormals)
_runtimeMesh.RecalculateNormals();
if (recalculateTangents)
_runtimeMesh.RecalculateTangents();
UpdateMeshCollider();
}
private void UpdateMeshCollider()
{
if (!updateMeshCollider || _meshCollider == null)
return;
_meshCollider.sharedMesh = null;
_meshCollider.sharedMesh = _runtimeMesh;
}
private void OnDestroy()
{
if (_runtimeMesh == null)
return;
if (Application.isPlaying)
Destroy(_runtimeMesh);
else
DestroyImmediate(_runtimeMesh);
}
}
}