260 lines
6.6 KiB
C#
260 lines
6.6 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class BouyancyNew : MonoBehaviour
|
|
{
|
|
public float density = 500f;
|
|
|
|
public int slicesPerAxis = 2;
|
|
|
|
public bool isConcave;
|
|
|
|
public int voxelsLimit = 16;
|
|
|
|
private const float DAMPFER = 0.1f;
|
|
|
|
private const float WATER_DENSITY = 1000f;
|
|
|
|
private float voxelHalfHeight;
|
|
|
|
private Vector3 localArchimedesForce;
|
|
|
|
private List<Vector3> voxels;
|
|
|
|
private bool isMeshCollider;
|
|
|
|
public GameObject water;
|
|
|
|
public MegaDynamicRipple dynamicwater;
|
|
|
|
private Transform dwtrans;
|
|
|
|
private Rigidbody rbody;
|
|
|
|
private Collider mycollider;
|
|
|
|
public float ripplevel = 0.4f;
|
|
|
|
public float rippleforce = 0.1f;
|
|
|
|
private void Start()
|
|
{
|
|
if ((bool)water)
|
|
{
|
|
dynamicwater = water.GetComponent<MegaDynamicRipple>();
|
|
}
|
|
rbody = GetComponent<Rigidbody>();
|
|
mycollider = GetComponent<Collider>();
|
|
if ((bool)dynamicwater)
|
|
{
|
|
dwtrans = dynamicwater.transform;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning(string.Format("[Buoyancy.cs] No Dynamic Ripple modifier found", base.name));
|
|
}
|
|
Quaternion rotation = base.transform.rotation;
|
|
Vector3 position = base.transform.position;
|
|
base.transform.rotation = Quaternion.identity;
|
|
base.transform.position = Vector3.zero;
|
|
if (mycollider == null)
|
|
{
|
|
mycollider = base.gameObject.AddComponent<MeshCollider>();
|
|
Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no collider. MeshCollider has been added.", base.name));
|
|
}
|
|
isMeshCollider = GetComponent<MeshCollider>() != null;
|
|
Bounds bounds = mycollider.bounds;
|
|
if (bounds.size.x < bounds.size.y)
|
|
{
|
|
voxelHalfHeight = bounds.size.x;
|
|
}
|
|
else
|
|
{
|
|
voxelHalfHeight = bounds.size.y;
|
|
}
|
|
if (bounds.size.z < voxelHalfHeight)
|
|
{
|
|
voxelHalfHeight = bounds.size.z;
|
|
}
|
|
voxelHalfHeight /= 2f * (float)slicesPerAxis;
|
|
if (rbody == null)
|
|
{
|
|
rbody = base.gameObject.AddComponent<Rigidbody>();
|
|
Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no Rigidbody. Rigidbody has been added.", base.name));
|
|
}
|
|
rbody.centerOfMass = new Vector3(0f, (0f - bounds.extents.y) * 0f, 0f) + base.transform.InverseTransformPoint(bounds.center);
|
|
voxels = SliceIntoVoxels(isMeshCollider && isConcave);
|
|
base.transform.rotation = rotation;
|
|
base.transform.position = position;
|
|
float num = rbody.mass / density;
|
|
WeldPoints(voxels, voxelsLimit);
|
|
float y = 1000f * Mathf.Abs(Physics.gravity.y) * num;
|
|
localArchimedesForce = new Vector3(0f, y, 0f) / voxels.Count;
|
|
Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", base.name, num, rbody.mass, density));
|
|
}
|
|
|
|
private List<Vector3> SliceIntoVoxels(bool concave)
|
|
{
|
|
List<Vector3> list = new List<Vector3>(slicesPerAxis * slicesPerAxis * slicesPerAxis);
|
|
if (concave)
|
|
{
|
|
MeshCollider component = GetComponent<MeshCollider>();
|
|
bool convex = component.convex;
|
|
component.convex = false;
|
|
Bounds bounds = mycollider.bounds;
|
|
for (int i = 0; i < slicesPerAxis; i++)
|
|
{
|
|
for (int j = 0; j < slicesPerAxis; j++)
|
|
{
|
|
for (int k = 0; k < slicesPerAxis; k++)
|
|
{
|
|
float x = bounds.min.x + bounds.size.x / (float)slicesPerAxis * (0.5f + (float)i);
|
|
float y = bounds.min.y + bounds.size.y / (float)slicesPerAxis * (0.5f + (float)j);
|
|
float z = bounds.min.z + bounds.size.z / (float)slicesPerAxis * (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(bounds.center);
|
|
}
|
|
component.convex = convex;
|
|
}
|
|
else
|
|
{
|
|
Bounds bounds2 = GetComponent<Collider>().bounds;
|
|
for (int l = 0; l < slicesPerAxis; l++)
|
|
{
|
|
for (int m = 0; m < slicesPerAxis; m++)
|
|
{
|
|
for (int n = 0; n < slicesPerAxis; n++)
|
|
{
|
|
float x2 = bounds2.min.x + bounds2.size.x / (float)slicesPerAxis * (0.5f + (float)l);
|
|
float y2 = bounds2.min.y + bounds2.size.y / (float)slicesPerAxis * (0.5f + (float)m);
|
|
float z2 = bounds2.min.z + bounds2.size.z / (float)slicesPerAxis * (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
|
|
};
|
|
Vector3[] array2 = array;
|
|
foreach (Vector3 vector in array2)
|
|
{
|
|
RaycastHit hitInfo;
|
|
if (!c.Raycast(new Ray(p - vector * 1000f, vector), out hitInfo, 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)
|
|
{
|
|
int firstIndex;
|
|
int secondIndex;
|
|
FindClosestPoints(list, out firstIndex, out secondIndex);
|
|
Vector3 item = (list[firstIndex] + list[secondIndex]) * 0.5f;
|
|
list.RemoveAt(secondIndex);
|
|
list.RemoveAt(firstIndex);
|
|
list.Add(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
private float GetWaterLevel(float x, float z)
|
|
{
|
|
if ((bool)dynamicwater)
|
|
{
|
|
Vector3 zero = Vector3.zero;
|
|
zero.x = x;
|
|
zero.z = z;
|
|
return dynamicwater.GetWaterHeight(water.transform.worldToLocalMatrix.MultiplyPoint(zero));
|
|
}
|
|
return 0f;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
float y = dwtrans.position.y;
|
|
foreach (Vector3 voxel in voxels)
|
|
{
|
|
Vector3 vector = base.transform.TransformPoint(voxel);
|
|
float num = GetWaterLevel(vector.x, vector.z) + y;
|
|
if (vector.y - voxelHalfHeight < num)
|
|
{
|
|
float num2 = (num - vector.y) / (2f * voxelHalfHeight) + 0.5f;
|
|
if (num2 > 1f)
|
|
{
|
|
num2 = 1f;
|
|
}
|
|
else if (num2 < 0f)
|
|
{
|
|
num2 = 0f;
|
|
}
|
|
Vector3 pointVelocity = rbody.GetPointVelocity(vector);
|
|
Vector3 vector2 = -pointVelocity * 0.1f * rbody.mass;
|
|
Vector3 force = vector2 + Mathf.Sqrt(num2) * localArchimedesForce;
|
|
rbody.AddForceAtPosition(force, vector);
|
|
if ((bool)dynamicwater && pointVelocity.y < 0f - ripplevel)
|
|
{
|
|
dynamicwater.ForceAt(vector, pointVelocity.y * rippleforce);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmos()
|
|
{
|
|
}
|
|
}
|