BouyancyNew.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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. using System.Collections.Generic;
  9. using UnityEngine;
  10. public class BouyancyNew : MonoBehaviour
  11. {
  12. // public Ocean ocean;
  13. public float density = 500;
  14. public int slicesPerAxis = 2;
  15. public bool isConcave = false;
  16. public int voxelsLimit = 16;
  17. private const float DAMPFER = 0.1f;
  18. private const float WATER_DENSITY = 1000;
  19. private float voxelHalfHeight;
  20. private Vector3 localArchimedesForce;
  21. private List<Vector3> voxels;
  22. private bool isMeshCollider;
  23. //private List<Vector3[]> forces; // For drawing force gizmos
  24. public GameObject water;
  25. public MegaDynamicRipple dynamicwater;
  26. Transform dwtrans;
  27. Rigidbody rbody;
  28. Collider mycollider;
  29. /// <summary>
  30. /// Provides initialization.
  31. /// </summary>
  32. private void Start()
  33. {
  34. if ( water )
  35. {
  36. dynamicwater = (MegaDynamicRipple)water.GetComponent<MegaDynamicRipple>();
  37. }
  38. rbody = GetComponent<Rigidbody>();
  39. mycollider = GetComponent<Collider>();
  40. if ( dynamicwater )
  41. dwtrans = dynamicwater.transform;
  42. else
  43. {
  44. Debug.LogWarning(string.Format("[Buoyancy.cs] No Dynamic Ripple modifier found", name));
  45. }
  46. //forces = new List<Vector3[]>(); // For drawing force gizmos
  47. // Store original rotation and position
  48. var originalRotation = transform.rotation;
  49. var originalPosition = transform.position;
  50. transform.rotation = Quaternion.identity;
  51. transform.position = Vector3.zero;
  52. // The object must have a collider
  53. if ( mycollider == null )
  54. {
  55. mycollider = gameObject.AddComponent<MeshCollider>();
  56. Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no collider. MeshCollider has been added.", name));
  57. }
  58. isMeshCollider = GetComponent<MeshCollider>() != null;
  59. var bounds = mycollider.bounds;
  60. if ( bounds.size.x < bounds.size.y )
  61. {
  62. voxelHalfHeight = bounds.size.x;
  63. }
  64. else
  65. {
  66. voxelHalfHeight = bounds.size.y;
  67. }
  68. if ( bounds.size.z < voxelHalfHeight )
  69. {
  70. voxelHalfHeight = bounds.size.z;
  71. }
  72. voxelHalfHeight /= 2.0f * slicesPerAxis;
  73. // The object must have a RidigBody
  74. if ( rbody == null )
  75. {
  76. rbody = gameObject.AddComponent<Rigidbody>();
  77. Debug.LogWarning(string.Format("[Buoyancy.cs] Object \"{0}\" had no Rigidbody. Rigidbody has been added.", name));
  78. }
  79. rbody.centerOfMass = new Vector3(0, -bounds.extents.y * 0f, 0) + transform.InverseTransformPoint(bounds.center);
  80. voxels = SliceIntoVoxels(isMeshCollider && isConcave);
  81. // Restore original rotation and position
  82. transform.rotation = originalRotation;
  83. transform.position = originalPosition;
  84. float volume = rbody.mass / density;
  85. WeldPoints(voxels, voxelsLimit);
  86. float archimedesForceMagnitude = WATER_DENSITY * Mathf.Abs(Physics.gravity.y) * volume;
  87. localArchimedesForce = new Vector3(0, archimedesForceMagnitude, 0) / voxels.Count;
  88. Debug.Log(string.Format("[Buoyancy.cs] Name=\"{0}\" volume={1:0.0}, mass={2:0.0}, density={3:0.0}", name, volume, rbody.mass, density));
  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. if ( concave )
  99. {
  100. var meshCol = GetComponent<MeshCollider>();
  101. var convexValue = meshCol.convex;
  102. meshCol.convex = false;
  103. // Concave slicing
  104. var bounds = mycollider.bounds;
  105. for ( int ix = 0; ix < slicesPerAxis; ix++ )
  106. {
  107. for ( int iy = 0; iy < slicesPerAxis; iy++ )
  108. {
  109. for ( int iz = 0; iz < slicesPerAxis; iz++ )
  110. {
  111. float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
  112. float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
  113. float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
  114. var p = transform.InverseTransformPoint(new Vector3(x, y, z));
  115. if ( PointIsInsideMeshCollider(meshCol, p) )
  116. {
  117. points.Add(p);
  118. }
  119. }
  120. }
  121. }
  122. if ( points.Count == 0 )
  123. {
  124. points.Add(bounds.center);
  125. }
  126. meshCol.convex = convexValue;
  127. }
  128. else
  129. {
  130. // Convex slicing
  131. var bounds = GetComponent<Collider>().bounds;
  132. for ( int ix = 0; ix < slicesPerAxis; ix++ )
  133. {
  134. for ( int iy = 0; iy < slicesPerAxis; iy++ )
  135. {
  136. for ( int iz = 0; iz < slicesPerAxis; iz++ )
  137. {
  138. float x = bounds.min.x + bounds.size.x / slicesPerAxis * (0.5f + ix);
  139. float y = bounds.min.y + bounds.size.y / slicesPerAxis * (0.5f + iy);
  140. float z = bounds.min.z + bounds.size.z / slicesPerAxis * (0.5f + iz);
  141. var p = transform.InverseTransformPoint(new Vector3(x, y, z));
  142. points.Add(p);
  143. }
  144. }
  145. }
  146. }
  147. return points;
  148. }
  149. /// <summary>
  150. /// Returns whether the point is inside the mesh collider.
  151. /// </summary>
  152. /// <param name="c">Mesh collider.</param>
  153. /// <param name="p">Point.</param>
  154. /// <returns>True - the point is inside the mesh collider. False - the point is outside of the mesh collider. </returns>
  155. private static bool PointIsInsideMeshCollider(Collider c, Vector3 p)
  156. {
  157. Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
  158. foreach ( var ray in directions )
  159. {
  160. RaycastHit hit;
  161. if ( c.Raycast(new Ray(p - ray * 1000, ray), out hit, 1000f) == false )
  162. {
  163. return false;
  164. }
  165. }
  166. return true;
  167. }
  168. /// <summary>
  169. /// Returns two closest points in the list.
  170. /// </summary>
  171. /// <param name="list">List of points.</param>
  172. /// <param name="firstIndex">Index of the first point in the list. It's always less than the second index.</param>
  173. /// <param name="secondIndex">Index of the second point in the list. It's always greater than the first index.</param>
  174. private static void FindClosestPoints(IList<Vector3> list, out int firstIndex, out int secondIndex)
  175. {
  176. float minDistance = float.MaxValue, maxDistance = float.MinValue;
  177. firstIndex = 0;
  178. secondIndex = 1;
  179. for ( int i = 0; i < list.Count - 1; i++ )
  180. {
  181. for ( int j = i + 1; j < list.Count; j++ )
  182. {
  183. float distance = Vector3.Distance(list[i], list[j]);
  184. if ( distance < minDistance )
  185. {
  186. minDistance = distance;
  187. firstIndex = i;
  188. secondIndex = j;
  189. }
  190. if ( distance > maxDistance )
  191. {
  192. maxDistance = distance;
  193. }
  194. }
  195. }
  196. }
  197. /// <summary>
  198. /// Welds closest points.
  199. /// </summary>
  200. /// <param name="list">List of points.</param>
  201. /// <param name="targetCount">Target number of points in the list.</param>
  202. private static void WeldPoints(IList<Vector3> list, int targetCount)
  203. {
  204. if ( list.Count <= 2 || targetCount < 2 )
  205. {
  206. return;
  207. }
  208. while ( list.Count > targetCount )
  209. {
  210. int first, second;
  211. FindClosestPoints(list, out first, out second);
  212. var mixed = (list[first] + list[second]) * 0.5f;
  213. list.RemoveAt(second); // the second index is always greater that the first => removing the second item first
  214. list.RemoveAt(first);
  215. list.Add(mixed);
  216. }
  217. }
  218. /// <summary>
  219. /// Returns the water level at given location.
  220. /// </summary>
  221. /// <param name="x">x-coordinate</param>
  222. /// <param name="z">z-coordinate</param>
  223. /// <returns>Water level</returns>
  224. private float GetWaterLevel(float x, float z)
  225. {
  226. if ( dynamicwater )
  227. {
  228. Vector3 lpos = Vector3.zero;
  229. lpos.x = x;
  230. lpos.z = z;
  231. float h = dynamicwater.GetWaterHeight(water.transform.worldToLocalMatrix.MultiplyPoint(lpos));
  232. //h += dynamicwater.transform.position.y;
  233. return h;
  234. }
  235. // return ocean == null ? 0.0f : ocean.GetWaterHeightAtLocation(x, z);
  236. return 0.0f;
  237. }
  238. /// <summary>
  239. /// Calculates physics.
  240. /// </summary>
  241. private void FixedUpdate()
  242. {
  243. //forces.Clear(); // For drawing force gizmos
  244. float y = dwtrans.position.y;
  245. foreach ( var point in voxels )
  246. {
  247. Vector3 wp = transform.TransformPoint(point);
  248. float waterLevel = GetWaterLevel(wp.x, wp.z) + y;
  249. if ( wp.y - voxelHalfHeight < waterLevel )
  250. {
  251. float k = (waterLevel - wp.y) / (2.0f * voxelHalfHeight) + 0.5f;
  252. if ( k > 1.0f )
  253. {
  254. k = 1.0f;
  255. }
  256. else if ( k < 0.0f )
  257. {
  258. k = 0.0f;
  259. }
  260. Vector3 velocity = rbody.GetPointVelocity(wp);
  261. Vector3 localDampingForce = -velocity * DAMPFER * rbody.mass;
  262. Vector3 force = localDampingForce + Mathf.Sqrt(k) * localArchimedesForce;
  263. rbody.AddForceAtPosition(force, wp);
  264. if ( dynamicwater )
  265. {
  266. if ( velocity.y < -ripplevel )
  267. {
  268. dynamicwater.ForceAt(wp, velocity.y * rippleforce); //-force.y * 0.04f);
  269. }
  270. }
  271. }
  272. }
  273. }
  274. public float ripplevel = 0.4f;
  275. public float rippleforce = 0.1f;
  276. /// <summary>
  277. /// Draws gizmos.
  278. /// </summary>
  279. private void OnDrawGizmos()
  280. {
  281. }
  282. }