282 lines
7.0 KiB
C#
282 lines
7.0 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
[RequireComponent(typeof(Rigidbody))]
|
|
[RequireComponent(typeof(Collider))]
|
|
public class BouncyInteract : MonoBehaviour
|
|
{
|
|
public enum ModelSourceEnum
|
|
{
|
|
Collider = 0,
|
|
Mesh = 1
|
|
}
|
|
|
|
public ModelSourceEnum VolumeSource = ModelSourceEnum.Mesh;
|
|
|
|
public Transform OvverideCenterOfMass;
|
|
|
|
[Range(100f, 1000f)]
|
|
public float Density = 450f;
|
|
|
|
[Range(1f, 6f)]
|
|
public int SlicesPerAxisX = 2;
|
|
|
|
[Range(1f, 6f)]
|
|
public int SlicesPerAxisY = 2;
|
|
|
|
[Range(1f, 6f)]
|
|
public int SlicesPerAxisZ = 2;
|
|
|
|
public bool isConcave;
|
|
|
|
[Range(2f, 32f)]
|
|
public int VoxelsLimit = 16;
|
|
|
|
public float AngularDrag = 0.25f;
|
|
|
|
public float Drag = 0.25f;
|
|
|
|
[Range(0f, 1f)]
|
|
public float NormalForce = 0.2f;
|
|
|
|
public bool DebugForces;
|
|
|
|
private const float DAMPFER = 0.1f;
|
|
|
|
private const float WATER_DENSITY = 800f;
|
|
|
|
private Vector3 localArchimedesForce;
|
|
|
|
private List<Vector3> voxels;
|
|
|
|
private Vector3[] currentForces;
|
|
|
|
private bool isMeshCollider;
|
|
|
|
private List<Vector3[]> debugForces;
|
|
|
|
private Rigidbody rigidBody;
|
|
|
|
private Collider collider;
|
|
|
|
private float bounceMaxSize;
|
|
|
|
private void OnEnable()
|
|
{
|
|
debugForces = new List<Vector3[]>();
|
|
rigidBody = GetComponent<Rigidbody>();
|
|
collider = GetComponent<Collider>();
|
|
Quaternion rotation = base.transform.rotation;
|
|
Vector3 position = base.transform.position;
|
|
base.transform.rotation = Quaternion.identity;
|
|
base.transform.position = Vector3.zero;
|
|
Bounds bounds = collider.bounds;
|
|
bounceMaxSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z);
|
|
isMeshCollider = GetComponent<MeshCollider>() != null;
|
|
if ((bool)OvverideCenterOfMass)
|
|
{
|
|
rigidBody.centerOfMass = base.transform.InverseTransformPoint(OvverideCenterOfMass.transform.position);
|
|
}
|
|
rigidBody.angularDrag = AngularDrag;
|
|
rigidBody.drag = Drag;
|
|
voxels = SliceIntoVoxels(isMeshCollider && isConcave);
|
|
currentForces = new Vector3[voxels.Count];
|
|
base.transform.rotation = rotation;
|
|
base.transform.position = position;
|
|
float num = rigidBody.mass / Density;
|
|
WeldPoints(voxels, VoxelsLimit);
|
|
float y = 800f * Mathf.Abs(Physics.gravity.y) * num;
|
|
localArchimedesForce = new Vector3(0f, y, 0f) / voxels.Count;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
}
|
|
|
|
private Bounds GetCurrentBounds()
|
|
{
|
|
Bounds result = default(Bounds);
|
|
if (VolumeSource == ModelSourceEnum.Mesh)
|
|
{
|
|
return GetComponent<Renderer>().bounds;
|
|
}
|
|
if (VolumeSource == ModelSourceEnum.Collider)
|
|
{
|
|
MeshCollider component = GetComponent<MeshCollider>();
|
|
if (component != null)
|
|
{
|
|
return component.sharedMesh.bounds;
|
|
}
|
|
return GetComponent<Collider>().bounds;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private List<Vector3> SliceIntoVoxels(bool concave)
|
|
{
|
|
List<Vector3> list = new List<Vector3>(SlicesPerAxisX * SlicesPerAxisY * SlicesPerAxisZ);
|
|
Bounds currentBounds = GetCurrentBounds();
|
|
if (concave)
|
|
{
|
|
MeshCollider component = GetComponent<MeshCollider>();
|
|
bool convex = component.convex;
|
|
component.convex = false;
|
|
for (int i = 0; i < SlicesPerAxisX; i++)
|
|
{
|
|
for (int j = 0; j < SlicesPerAxisY; j++)
|
|
{
|
|
for (int k = 0; k < SlicesPerAxisZ; k++)
|
|
{
|
|
float x = currentBounds.min.x + currentBounds.size.x / (float)SlicesPerAxisX * (0.5f + (float)i);
|
|
float y = currentBounds.min.y + currentBounds.size.y / (float)SlicesPerAxisY * (0.5f + (float)j);
|
|
float z = currentBounds.min.z + currentBounds.size.z / (float)SlicesPerAxisZ * (0.5f + (float)k);
|
|
Vector3 vector = base.transform.InverseTransformPoint(new Vector3(x, y, z));
|
|
if (PointIsInsideMeshCollider(component, vector))
|
|
{
|
|
list.Add(vector);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (list.Count == 0)
|
|
{
|
|
list.Add(currentBounds.center);
|
|
}
|
|
component.convex = convex;
|
|
}
|
|
else
|
|
{
|
|
for (int l = 0; l < SlicesPerAxisX; l++)
|
|
{
|
|
for (int m = 0; m < SlicesPerAxisY; m++)
|
|
{
|
|
for (int n = 0; n < SlicesPerAxisZ; n++)
|
|
{
|
|
float x2 = currentBounds.min.x + currentBounds.size.x / (float)SlicesPerAxisX * (0.5f + (float)l);
|
|
float y2 = currentBounds.min.y + currentBounds.size.y / (float)SlicesPerAxisY * (0.5f + (float)m);
|
|
float z2 = currentBounds.min.z + currentBounds.size.z / (float)SlicesPerAxisZ * (0.5f + (float)n);
|
|
Vector3 item = base.transform.InverseTransformPoint(new Vector3(x2, y2, z2));
|
|
list.Add(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
|
|
{
|
|
Vector3[] array = new Vector3[6]
|
|
{
|
|
Vector3.up,
|
|
Vector3.down,
|
|
Vector3.left,
|
|
Vector3.right,
|
|
Vector3.forward,
|
|
Vector3.back
|
|
};
|
|
foreach (Vector3 vector in array)
|
|
{
|
|
if (!c.Raycast(new Ray(p - vector * 1000f, vector), out var _, 1000f))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
|
|
{
|
|
float num = float.MaxValue;
|
|
float num2 = float.MinValue;
|
|
firstIndex = 0;
|
|
secondIndex = 1;
|
|
for (int i = 0; i < list.Count - 1; i++)
|
|
{
|
|
for (int j = i + 1; j < list.Count; j++)
|
|
{
|
|
float num3 = Vector3.Distance(list[i], list[j]);
|
|
if (num3 < num)
|
|
{
|
|
num = num3;
|
|
firstIndex = i;
|
|
secondIndex = j;
|
|
}
|
|
if (num3 > num2)
|
|
{
|
|
num2 = num3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WeldPoints(IList<Vector3> list, int targetCount)
|
|
{
|
|
if (list.Count > 2 && targetCount >= 2)
|
|
{
|
|
while (list.Count > targetCount)
|
|
{
|
|
FindClosestPoints(list, out var firstIndex, out var secondIndex);
|
|
Vector3 item = (list[firstIndex] + list[secondIndex]) * 0.5f;
|
|
list.RemoveAt(secondIndex);
|
|
list.RemoveAt(firstIndex);
|
|
list.Add(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (DebugForces)
|
|
{
|
|
debugForces.Clear();
|
|
}
|
|
for (int i = 0; i < voxels.Count; i++)
|
|
{
|
|
Vector3 vector = base.transform.TransformPoint(voxels[i]);
|
|
Vector3 zero = Vector3.zero;
|
|
Vector3 vector2 = -rigidBody.GetPointVelocity(vector) * 0.1f * rigidBody.mass;
|
|
float num = 0f - vector.y;
|
|
if (num > 1f)
|
|
{
|
|
num = 1f;
|
|
}
|
|
else if (num < 0f)
|
|
{
|
|
num = 0f;
|
|
vector2 *= 0.2f;
|
|
}
|
|
zero = vector2 + Mathf.Sqrt(num) * localArchimedesForce;
|
|
Vector3 normalized = Vector3.Cross(rhs: 0f * new Vector3(0.01f, 0f, 0f), lhs: 0f * new Vector3(0f, 0f, 0.01f)).normalized;
|
|
zero.x += normalized.x * NormalForce * rigidBody.mass;
|
|
zero.z += normalized.z * NormalForce * rigidBody.mass;
|
|
currentForces[i] = zero * 2f;
|
|
rigidBody.AddForceAtPosition(zero, vector);
|
|
if (DebugForces)
|
|
{
|
|
debugForces.Add(new Vector3[2] { vector, zero });
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
if (!DebugForces || voxels == null || debugForces == null)
|
|
{
|
|
return;
|
|
}
|
|
float num = 0.02f * bounceMaxSize;
|
|
Gizmos.color = Color.yellow;
|
|
foreach (Vector3 voxel in voxels)
|
|
{
|
|
Gizmos.DrawCube(base.transform.TransformPoint(voxel), new Vector3(num, num, num));
|
|
}
|
|
Gizmos.color = Color.cyan;
|
|
foreach (Vector3[] debugForce in debugForces)
|
|
{
|
|
Gizmos.DrawCube(debugForce[0], new Vector3(num, num, num));
|
|
Gizmos.DrawRay(debugForce[0], debugForce[1] / rigidBody.mass * bounceMaxSize * 0.25f);
|
|
}
|
|
}
|
|
}
|