Advertisement
Guest User

Unity Buoyancy Script

a guest
Jul 4th, 2015
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.43 KB | None | 0 0
  1. // Buoyancy.cs
  2. // by Alex Zhdankin
  3. // Version 2.1
  4. //
  5. // http://forum.unity3d.com/threads/72974-Buoyancy-script
  6. //
  7. // Terms of use: do whatever you like
  8.  
  9. using System.Collections.Generic;
  10. using UnityEngine;
  11.  
  12. public class Buoyancy : MonoBehaviour
  13. {
  14. //  public Ocean ocean;
  15.  
  16.     public float density = 500;
  17.     public int slicesPerAxis = 2;
  18.     public bool isConcave = false;
  19.     public int voxelsLimit = 16;
  20.  
  21.     private const float DAMPFER = 0.1f;
  22.     private const float WATER_DENSITY = 1000;
  23.  
  24.     private float voxelHalfHeight;
  25.     private Vector3 localArchimedesForce;
  26.     private List<Vector3> voxels;
  27.     private bool isMeshCollider;
  28.     private List<Vector3[]> forces; // For drawing force gizmos
  29.  
  30.     /// <summary>
  31.     /// Provides initialization.
  32.     /// </summary>
  33.     private void Start()
  34.     {
  35.         forces = new List<Vector3[]>(); // For drawing force gizmos
  36.  
  37.         // Store original rotation and position
  38.         var originalRotation = transform.rotation;
  39.         var originalPosition = transform.position;
  40.         transform.rotation = Quaternion.identity;
  41.         transform.position = Vector3.zero;
  42.  
  43.         // The object must have a collider
  44.         if (collider == null)
  45.         {
  46.             gameObject.AddComponent<MeshCollider>();
  47.             Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no collider. MeshCollider has been added.", name));
  48.         }
  49.         isMeshCollider = GetComponent<MeshCollider>() != null;
  50.  
  51.         var bounds = collider.bounds;
  52.         if (bounds.size.x < bounds.size.y)
  53.         {
  54.             voxelHalfHeight = bounds.size.x;
  55.         }
  56.         else
  57.         {
  58.             voxelHalfHeight = bounds.size.y;
  59.         }
  60.         if (bounds.size.z < voxelHalfHeight)
  61.         {
  62.             voxelHalfHeight = bounds.size.z;
  63.         }
  64.         voxelHalfHeight /= 2 * slicesPerAxis;
  65.  
  66.         // The object must have a RidigBody
  67.         if (rigidbody == null)
  68.         {
  69.             gameObject.AddComponent<Rigidbody>();
  70.             Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no Rigidbody. Rigidbody has been added.", name));
  71.         }
  72.         rigidbody.centerOfMass = new Vector3(0, -bounds.extents.y * 0f, 0) + transform.InverseTransformPoint(bounds.center);
  73.  
  74.         voxels = SliceIntoVoxels(isMeshCollider && isConcave);
  75.  
  76.         // Restore original rotation and position
  77.         transform.rotation = originalRotation;
  78.         transform.position = originalPosition;
  79.  
  80.         float volume = rigidbody.mass / density;
  81.  
  82.         WeldPoints(voxels, voxelsLimit);
  83.  
  84.         float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
  85.         localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / voxels.Count;
  86.  
  87.         Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", name, volume, rigidbody.mass, density));
  88.     }
  89.  
  90.     /// <summary>
  91.     /// Slices the object into number of voxels represented by their center points.
  92.     /// <param name="concave">Whether the object have a concave shape.</param>
  93.     /// <returns>List of voxels represented by their center points.</returns>
  94.     /// </summary>
  95.     private List<Vector3> SliceIntoVoxels(bool concave)
  96.     {
  97.         var points = new List<Vector3>(slicesPerAxis * slicesPerAxis * slicesPerAxis);
  98.  
  99.         if (concave)
  100.         {
  101.             var meshCol = GetComponent<MeshCollider>();
  102.  
  103.             var convexValue = meshCol.convex;
  104.             meshCol.convex = false;
  105.  
  106.             // Concave slicing
  107.             var bounds = collider.bounds;
  108.             for (int ix = 0; ix < slicesPerAxis; ix++)
  109.             {
  110.                 for (int iy = 0; iy < slicesPerAxis; iy++)
  111.                 {
  112.                     for (int iz = 0; iz < slicesPerAxis; iz++)
  113.                     {
  114.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
  115.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
  116.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
  117.  
  118.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
  119.  
  120.                         if (PointIsInsideMeshCollider(meshCol, p))
  121.                         {
  122.                             points.Add(p);
  123.                         }
  124.                     }
  125.                 }
  126.             }
  127.             if (points.Count == 0)
  128.             {
  129.                 points.Add(bounds.center);
  130.             }
  131.  
  132.             meshCol.convex = convexValue;
  133.         }
  134.         else
  135.         {
  136.             // Convex slicing
  137.             var bounds = GetComponent<Collider>().bounds;
  138.             for (int ix = 0; ix < slicesPerAxis; ix++)
  139.             {
  140.                 for (int iy = 0; iy < slicesPerAxis; iy++)
  141.                 {
  142.                     for (int iz = 0; iz < slicesPerAxis; iz++)
  143.                     {
  144.                         float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
  145.                         float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
  146.                         float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
  147.  
  148.                         var p = transform.InverseTransformPoint(new Vector3(x, y, z));
  149.  
  150.                         points.Add(p);
  151.                     }
  152.                 }
  153.             }
  154.         }
  155.  
  156.         return points;
  157.     }
  158.  
  159.     /// <summary>
  160.     /// Returns whether the point is inside the mesh collider.
  161.     /// </summary>
  162.     /// <param name="c">Mesh collider.</param>
  163.     /// <param name="p">Point.</param>
  164.     /// <returns>True - the point is inside the mesh collider. False - the point is outside of the mesh collider. </returns>
  165.     private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
  166.     {
  167.         Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
  168.  
  169.         foreach (var ray in directions)
  170.         {
  171.             RaycastHit hit;
  172.             if (c.Raycast(new Ray(p - ray * 1000, ray), out hit, 1000f) == false)
  173.             {
  174.                 return false;
  175.             }
  176.         }
  177.  
  178.         return true;
  179.     }
  180.  
  181.     /// <summary>
  182.     /// Returns two closest points in the list.
  183.     /// </summary>
  184.     /// <param name="list">List of points.</param>
  185.     /// <param name="firstIndex">Index of the first point in the list. It's always less than the second index.</param>
  186.     /// <param name="secondIndex">Index of the second point in the list. It's always greater than the first index.</param>
  187.     private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
  188.     {
  189.         float minDistance = float.MaxValue, maxDistance = float.MinValue;
  190.         firstIndex = 0;
  191.         secondIndex = 1;
  192.  
  193.         for (int i = 0; i < list.Count - 1; i++)
  194.         {
  195.             for (int j = i + 1; j < list.Count; j++)
  196.             {
  197.                 float distance = Vector3.Distance(list[i], list[j]);
  198.                 if (distance < minDistance)
  199.                 {
  200.                     minDistance = distance;
  201.                     firstIndex = i;
  202.                     secondIndex = j;
  203.                 }
  204.                 if (distance > maxDistance)
  205.                 {
  206.                     maxDistance = distance;
  207.                 }
  208.             }
  209.         }
  210.     }
  211.  
  212.     /// <summary>
  213.     /// Welds closest points.
  214.     /// </summary>
  215.     /// <param name="list">List of points.</param>
  216.     /// <param name="targetCount">Target number of points in the list.</param>
  217.     private static void WeldPoints(IList<Vector3> list, int targetCount)
  218.     {
  219.         if (list.Count <= 2 || targetCount < 2)
  220.         {
  221.             return;
  222.         }
  223.  
  224.         while (list.Count > targetCount)
  225.         {
  226.             int first, second;
  227.             FindClosestPoints(list, out first, out second);
  228.  
  229.             var mixed = (list[first] + list[second]) * 0.5f;
  230.             list.RemoveAt(second); // the second index is always greater that the first => removing the second item first
  231.             list.RemoveAt(first);
  232.             list.Add(mixed);
  233.         }
  234.     }
  235.  
  236.     /// <summary>
  237.     /// Returns the water level at given location.
  238.     /// </summary>
  239.     /// <param name="x">x-coordinate</param>
  240.     /// <param name="z">z-coordinate</param>
  241.     /// <returns>Water level</returns>
  242.     private float GetWaterLevel(float x, float z)
  243.     {
  244. //      return ocean == null ? 0.0f : ocean.GetWaterHeightAtLocation(x, z);
  245.         return 0.0f;
  246.     }
  247.  
  248.     /// <summary>
  249.     /// Calculates physics.
  250.     /// </summary>
  251.     private void FixedUpdate()
  252.     {
  253.         forces.Clear(); // For drawing force gizmos
  254.  
  255.         foreach (var point in voxels)
  256.         {
  257.             var wp = transform.TransformPoint(point);
  258.             float waterLevel = GetWaterLevel(wp.x, wp.z);
  259.  
  260.             if (wp.y - voxelHalfHeight < waterLevel)
  261.             {
  262.                 float k = (waterLevel - wp.y) / (2 * voxelHalfHeight) + 0.5f;
  263.                 if (k > 1)
  264.                 {
  265.                     k = 1f;
  266.                 }
  267.                 else if (k < 0)
  268.                 {
  269.                     k = 0f;
  270.                 }
  271.  
  272.                 var velocity = rigidbody.GetPointVelocity(wp);
  273.                 var localDampingForce = -velocity * DAMPFER * rigidbody.mass;
  274.                 var force = localDampingForce + Mathf.Sqrt(k) * localArchimedesForce;
  275.                 rigidbody.AddForceAtPosition(force, wp);
  276.  
  277.                 forces.Add(new[] { wp, force }); // For drawing force gizmos
  278.             }
  279.         }
  280.     }
  281.  
  282.     /// <summary>
  283.     /// Draws gizmos.
  284.     /// </summary>
  285.     private void OnDrawGizmos()
  286.     {
  287.         if (voxels == null || forces == null)
  288.         {
  289.             return;
  290.         }
  291.  
  292.         const float gizmoSize = 0.05f;
  293.         Gizmos.color = Color.yellow;
  294.  
  295.         foreach (var p in voxels)
  296.         {
  297.             Gizmos.DrawCube(transform.TransformPoint(p), new Vector3(gizmoSize, gizmoSize, gizmoSize));
  298.         }
  299.  
  300.         Gizmos.color = Color.cyan;
  301.  
  302.         foreach (var force in forces)
  303.         {
  304.             Gizmos.DrawCube(force[0], new Vector3(gizmoSize, gizmoSize, gizmoSize));
  305.             Gizmos.DrawLine(force[0], force[0] + force[1] / rigidbody.mass);
  306.         }
  307.     }
  308. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement