GravityUtils.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /*
  2. Copyright (c) 2020 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2020.
  5. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  6. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  7. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  8. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  9. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  10. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  11. THE SOFTWARE.
  12. */
  13. using System.Linq;
  14. using UnityEngine;
  15. namespace PluginMaster
  16. {
  17. [System.Serializable]
  18. public class SimulateGravityData
  19. {
  20. [SerializeField] private int _maxIterations = 1000;
  21. [SerializeField] private Vector3 _gravity = Physics.gravity;
  22. [SerializeField] private float _drag = 0f;
  23. [SerializeField] private float _angularDrag = 0.05f;
  24. [SerializeField] private float _maxSpeed = 100;
  25. private float _maxSpeedSquared = 10000;
  26. [SerializeField] private float _maxAngularSpeed = 10;
  27. private float _maxAngularSpeedSquared = 100;
  28. [SerializeField] private float _mass = 1f;
  29. [SerializeField] private bool _changeLayer = false;
  30. [SerializeField] private int _tempLayer = 0;
  31. [SerializeField] private bool _ignoreSceneColliders = false;
  32. public int maxIterations
  33. {
  34. get => _maxIterations;
  35. set
  36. {
  37. value = Mathf.Clamp(value, 1, 100000);
  38. if (_maxIterations == value) return;
  39. _maxIterations = value;
  40. }
  41. }
  42. public Vector3 gravity
  43. {
  44. get => _gravity;
  45. set
  46. {
  47. if (_gravity == value) return;
  48. _gravity = value;
  49. }
  50. }
  51. public float drag
  52. {
  53. get => _drag;
  54. set
  55. {
  56. value = Mathf.Max(value, 0f);
  57. if (_drag == value) return;
  58. _drag = value;
  59. }
  60. }
  61. public float angularDrag
  62. {
  63. get => _angularDrag;
  64. set
  65. {
  66. value = Mathf.Max(value, 0f);
  67. if (_angularDrag == value) return;
  68. _angularDrag = value;
  69. }
  70. }
  71. public float maxSpeed
  72. {
  73. get => _maxSpeed;
  74. set
  75. {
  76. value = Mathf.Max(value, 0f);
  77. if (_maxSpeed == value) return;
  78. _maxSpeed = value;
  79. _maxSpeedSquared = _maxSpeed * _maxSpeed;
  80. }
  81. }
  82. public float maxAngularSpeed
  83. {
  84. get => _maxAngularSpeed;
  85. set
  86. {
  87. value = Mathf.Max(value, 0f);
  88. if (_maxAngularSpeed == value) return;
  89. _maxAngularSpeed = value;
  90. _maxAngularSpeedSquared = _maxAngularSpeed * _maxAngularSpeed;
  91. }
  92. }
  93. public float maxSpeedSquared => _maxSpeedSquared;
  94. public float maxAngularSpeedSquared => _maxAngularSpeedSquared;
  95. public float mass
  96. {
  97. get => _mass;
  98. set
  99. {
  100. value = Mathf.Max(value, 1e-7f);
  101. if (_mass == value) return;
  102. _mass = value;
  103. }
  104. }
  105. public bool changeLayer
  106. {
  107. get => _changeLayer;
  108. set
  109. {
  110. if (_changeLayer == value) return;
  111. _changeLayer = value;
  112. }
  113. }
  114. public int tempLayer
  115. {
  116. get => _tempLayer;
  117. set
  118. {
  119. if (_tempLayer == value) return;
  120. _tempLayer = value;
  121. }
  122. }
  123. public bool ignoreSceneColliders
  124. {
  125. get => _ignoreSceneColliders;
  126. set
  127. {
  128. if (_ignoreSceneColliders == value) return;
  129. _ignoreSceneColliders = value;
  130. }
  131. }
  132. public void Copy(SimulateGravityData other)
  133. {
  134. _maxIterations = other._maxIterations;
  135. _gravity = other._gravity;
  136. _drag = other._drag;
  137. _angularDrag = other._angularDrag;
  138. _maxSpeed = other._maxSpeed;
  139. _maxSpeedSquared = other._maxSpeedSquared;
  140. _maxAngularSpeed = other._maxAngularSpeed;
  141. _maxAngularSpeedSquared = other._maxAngularSpeedSquared;
  142. _mass = other._mass;
  143. _changeLayer = other._changeLayer;
  144. _tempLayer = other._tempLayer;
  145. _ignoreSceneColliders = other._ignoreSceneColliders;
  146. }
  147. }
  148. public static class GravityUtils
  149. {
  150. private static bool _isPlaying = false;
  151. private static bool _stop = false;
  152. private static void AddCollider(GameObject obj, Mesh mesh, SimulateGravityData data)
  153. {
  154. var collider = MeshUtils.AddCollider(mesh, obj);
  155. if (collider is MeshCollider) (collider as MeshCollider).convex = true;
  156. if (data.changeLayer) obj.layer = data.tempLayer;
  157. }
  158. private static bool IsVisible(GameObject obj)
  159. {
  160. if (obj == null) return false;
  161. var target = obj;
  162. var parentRenderer = target.GetComponentInParent<Renderer>();
  163. var parentTerrain = target.GetComponentInParent<Terrain>();
  164. if (parentRenderer != null) target = parentRenderer.gameObject;
  165. else if (parentTerrain != null) target = parentTerrain.gameObject;
  166. else
  167. {
  168. var parent = target.transform.parent;
  169. if (parent != null)
  170. {
  171. var siblingRenderer = parent.GetComponentInChildren<Renderer>();
  172. var siblingTerrain = parent.GetComponentInChildren<Terrain>();
  173. if (siblingRenderer != null) target = parent.gameObject;
  174. else if (siblingTerrain != null) target = parent.gameObject;
  175. }
  176. }
  177. var renderers = target.GetComponentsInChildren<Renderer>();
  178. if (renderers.Length > 0)
  179. {
  180. foreach (var renderer in renderers)
  181. if (renderer.enabled) return true;
  182. }
  183. var terrains = target.GetComponentsInChildren<Terrain>();
  184. if (terrains.Length > 0)
  185. {
  186. foreach (var terrain in terrains)
  187. if (terrain.enabled) return true;
  188. }
  189. return false;
  190. }
  191. public static Pose[] SimulateGravity(GameObject[] selection, SimulateGravityData simData, bool recordAction)
  192. {
  193. if (_isPlaying && recordAction) return null;
  194. System.Collections.Generic.List<Collider> tempColliders = null;
  195. Collider[] sceneColliders = null;
  196. Collider[] invisibleColliders = null;
  197. if (simData.ignoreSceneColliders)
  198. {
  199. #if UNITY_2022_2_OR_NEWER
  200. sceneColliders = Object.FindObjectsByType<Collider>(FindObjectsSortMode.None)
  201. .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !sc.isTrigger).ToArray();
  202. #else
  203. sceneColliders = Object.FindObjectsOfType<Collider>()
  204. .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !sc.isTrigger).ToArray();
  205. #endif
  206. foreach (var sceneCollider in sceneColliders) sceneCollider.enabled = false;
  207. #if UNITY_2022_2_OR_NEWER
  208. var sceneMeshFilters = Object.FindObjectsByType<MeshFilter>(FindObjectsSortMode.None)
  209. .Where(mf => mf.gameObject.activeInHierarchy && mf.sharedMesh != null).ToArray();
  210. #else
  211. var sceneMeshFilters = Object.FindObjectsOfType<MeshFilter>()
  212. .Where(mf => mf.gameObject.activeInHierarchy && mf.sharedMesh != null).ToArray();
  213. #endif
  214. tempColliders = new System.Collections.Generic.List<Collider>();
  215. foreach (var meshFilter in sceneMeshFilters)
  216. {
  217. var mesh = meshFilter.sharedMesh;
  218. Collider tempCollider = MeshUtils.AddCollider(meshFilter.sharedMesh, meshFilter.gameObject);
  219. if (tempCollider != null) tempColliders.Add(tempCollider);
  220. }
  221. }
  222. else
  223. {
  224. #if UNITY_2022_2_OR_NEWER
  225. invisibleColliders = Object.FindObjectsByType<Collider>(FindObjectsSortMode.None)
  226. .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !IsVisible(sc.gameObject)).ToArray();
  227. #else
  228. invisibleColliders = Object.FindObjectsOfType<Collider>()
  229. .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !IsVisible(sc.gameObject)).ToArray();
  230. #endif
  231. foreach (var invisibleCollider in invisibleColliders) invisibleCollider.enabled = false;
  232. }
  233. var originalGravity = Physics.gravity;
  234. Physics.gravity = simData.gravity;
  235. #if UNITY_2022_2_OR_NEWER
  236. var allBodies = Object.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None);
  237. #else
  238. var allBodies = Object.FindObjectsOfType<Rigidbody>();
  239. #endif
  240. var originalBodies = new System.Collections.Generic.List<(Rigidbody body, bool useGravity, bool isKinematic,
  241. float drag, float angularDrag, float mass, RigidbodyConstraints constraints,
  242. RigidbodyInterpolation interpolation, CollisionDetectionMode collisionDetectionMode)>();
  243. foreach (var rigidBody in allBodies)
  244. {
  245. originalBodies.Add((rigidBody, rigidBody.useGravity, rigidBody.isKinematic,
  246. #if UNITY_6000_0_OR_NEWER
  247. rigidBody.linearDamping, rigidBody.angularDamping,
  248. #else
  249. rigidBody.drag, rigidBody.angularDrag,
  250. #endif
  251. rigidBody.mass, rigidBody.constraints, rigidBody.interpolation, rigidBody.collisionDetectionMode));
  252. rigidBody.useGravity = false;
  253. rigidBody.isKinematic = true;
  254. #if UNITY_6000_0_OR_NEWER
  255. rigidBody.linearDamping = simData.drag;
  256. rigidBody.angularDamping = simData.angularDrag;
  257. #else
  258. rigidBody.drag = simData.drag;
  259. rigidBody.angularDrag = simData.angularDrag;
  260. #endif
  261. rigidBody.mass = simData.mass;
  262. rigidBody.constraints = RigidbodyConstraints.None;
  263. rigidBody.interpolation = RigidbodyInterpolation.None;
  264. rigidBody.collisionDetectionMode = CollisionDetectionMode.Discrete;
  265. }
  266. var simBodies = new System.Collections.Generic.List<Rigidbody>();
  267. var clones = new GameObject[selection.Length];
  268. var animData = new System.Collections.Generic.List<AnimData>();
  269. void AddColliders(GameObject source, GameObject dest)
  270. {
  271. var meshFilters = source.GetComponents<MeshFilter>();
  272. foreach (var meshFilter in meshFilters)
  273. {
  274. var mesh = meshFilter.sharedMesh;
  275. if (mesh == null) continue;
  276. AddCollider(dest, mesh, simData);
  277. }
  278. var skinnedMeshRenderers = source.GetComponents<SkinnedMeshRenderer>();
  279. foreach (var renderer in skinnedMeshRenderers)
  280. {
  281. var mesh = renderer.sharedMesh;
  282. if (mesh == null) continue;
  283. AddCollider(dest, mesh, simData);
  284. }
  285. foreach (Transform child in source.transform)
  286. {
  287. var destChild = new GameObject();
  288. destChild.transform.SetParent(dest.transform);
  289. destChild.transform.localPosition = child.localPosition;
  290. destChild.transform.localRotation = child.localRotation;
  291. destChild.transform.localScale = child.localScale;
  292. AddColliders(child.gameObject, destChild);
  293. }
  294. }
  295. for (int i = 0; i < selection.Length; ++i)
  296. {
  297. animData.Add(new AnimData(selection[i]));
  298. var obj = new GameObject();
  299. obj.layer = selection[i].layer;
  300. obj.transform.position = selection[i].transform.position;
  301. obj.transform.rotation = selection[i].transform.rotation;
  302. obj.transform.localScale = selection[i].transform.lossyScale;
  303. AddColliders(selection[i], obj);
  304. var magnitude = BoundsUtils.GetMagnitude(selection[i].transform);
  305. selection[i].transform.position += Vector3.up * (100 * magnitude);
  306. var rigidBody = obj.AddComponent<Rigidbody>();
  307. if (simData.changeLayer) obj.layer = simData.tempLayer;
  308. simBodies.Add(rigidBody);
  309. rigidBody.useGravity = true;
  310. rigidBody.isKinematic = false;
  311. clones[i] = obj;
  312. }
  313. #if UNITY_2022_2_OR_NEWER
  314. var prevSimMode = Physics.simulationMode;
  315. Physics.simulationMode = SimulationMode.Script;
  316. #else
  317. Physics.autoSimulation = false;
  318. #endif
  319. for (int i = 0; i < simData.maxIterations; ++i)
  320. {
  321. Physics.Simulate(Time.fixedDeltaTime);
  322. for (int objIdx = 0; objIdx < selection.Length; ++objIdx)
  323. {
  324. var body = simBodies[objIdx];
  325. #if UNITY_6000_0_OR_NEWER
  326. if (body.linearVelocity.sqrMagnitude > simData.maxSpeedSquared)
  327. body.linearVelocity = body.linearVelocity.normalized * simData.maxSpeed;
  328. #else
  329. if (body.velocity.sqrMagnitude > simData.maxSpeedSquared)
  330. body.velocity = body.velocity.normalized * simData.maxSpeed;
  331. #endif
  332. if (body.angularVelocity.sqrMagnitude > simData.maxAngularSpeedSquared)
  333. body.angularVelocity = body.angularVelocity.normalized * simData.maxAngularSpeed;
  334. if (i % 10 == 0) animData[objIdx].poses.Add(new Pose(body.position, body.rotation));
  335. }
  336. if (simBodies.All(rb => rb.IsSleeping())) break;
  337. }
  338. #if UNITY_2022_2_OR_NEWER
  339. Physics.simulationMode = prevSimMode;
  340. #else
  341. Physics.autoSimulation = true;
  342. #endif
  343. for (int i = 0; i < selection.Length; ++i)
  344. {
  345. selection[i].transform.position = clones[i].transform.position;
  346. selection[i].transform.rotation = clones[i].transform.rotation;
  347. animData[i].poses.Add(new Pose(selection[i].transform.position, selection[i].transform.rotation));
  348. Object.DestroyImmediate(clones[i]);
  349. }
  350. foreach (var item in originalBodies)
  351. {
  352. if (item.body == null) continue;
  353. item.body.useGravity = item.useGravity;
  354. item.body.isKinematic = item.isKinematic;
  355. #if UNITY_6000_0_OR_NEWER
  356. item.body.linearDamping = item.drag;
  357. item.body.angularDamping = item.angularDrag;
  358. #else
  359. item.body.drag = item.drag;
  360. item.body.angularDrag = item.angularDrag;
  361. #endif
  362. item.body.mass = item.mass;
  363. item.body.constraints = item.constraints;
  364. item.body.interpolation = item.interpolation;
  365. item.body.collisionDetectionMode = item.collisionDetectionMode;
  366. }
  367. Physics.gravity = originalGravity;
  368. if (simData.ignoreSceneColliders)
  369. {
  370. foreach (var sceneCollider in sceneColliders) sceneCollider.enabled = true;
  371. foreach (var tempCollider in tempColliders) Object.DestroyImmediate(tempCollider);
  372. }
  373. else
  374. {
  375. foreach (var invisibleCollider in invisibleColliders) invisibleCollider.enabled = true;
  376. }
  377. Animate(animData, simData, recordAction);
  378. var finalPoses = new System.Collections.Generic.List<Pose>();
  379. foreach (var d in animData) finalPoses.Add(d.poses.Last());
  380. return finalPoses.ToArray();
  381. }
  382. private class AnimData
  383. {
  384. public GameObject obj = null;
  385. public System.Collections.Generic.List<Pose> poses = new System.Collections.Generic.List<Pose>();
  386. public System.Collections.Generic.List<Collider> enabledColliders
  387. = new System.Collections.Generic.List<Collider>();
  388. public System.Collections.Generic.List<GameObject> tempColliders
  389. = new System.Collections.Generic.List<GameObject>();
  390. public AnimData(GameObject obj) => this.obj = obj;
  391. }
  392. private static void Animate(System.Collections.Generic.List<AnimData> animData,
  393. SimulateGravityData simData, bool recordAction)
  394. {
  395. _stop = false;
  396. _isPlaying = true;
  397. foreach (var item in animData)
  398. {
  399. item.enabledColliders = item.obj.GetComponentsInChildren<Collider>()
  400. .Where(collider => collider.enabled).ToList();
  401. item.tempColliders.Clear();
  402. var temp = new GameObject();
  403. temp.hideFlags = HideFlags.HideAndDontSave;
  404. var lastPose = item.poses.Last();
  405. temp.transform.position = lastPose.position;
  406. temp.transform.rotation = lastPose.rotation;
  407. temp.transform.localScale = item.obj.transform.lossyScale;
  408. var meshFilters = item.obj.GetComponentsInChildren<MeshFilter>();
  409. foreach (var meshFilter in meshFilters)
  410. {
  411. var mesh = meshFilter.sharedMesh;
  412. if (mesh == null) continue;
  413. AddCollider(temp, mesh, simData);
  414. }
  415. var skinnedMeshRenderers = item.obj.GetComponentsInChildren<SkinnedMeshRenderer>();
  416. foreach (var renderer in skinnedMeshRenderers)
  417. {
  418. var mesh = renderer.sharedMesh;
  419. if (mesh == null) continue;
  420. var meshFilter = renderer.gameObject.AddComponent<MeshFilter>();
  421. meshFilter.sharedMesh = mesh;
  422. AddCollider(temp, mesh, simData);
  423. }
  424. item.tempColliders.Add(temp);
  425. foreach (var collider in item.enabledColliders) collider.enabled = false;
  426. }
  427. Animate(animData, 0, recordAction);
  428. }
  429. private async static void Animate(System.Collections.Generic.List<AnimData> data, int frame, bool recordAction)
  430. {
  431. void EnableColliders(AnimData item)
  432. {
  433. foreach (var collider in item.enabledColliders)
  434. {
  435. if (collider == null) continue;
  436. collider.enabled = true;
  437. }
  438. }
  439. void DestroyTempColliders(AnimData item)
  440. {
  441. foreach (var temp in item.tempColliders) UnityEngine.Object.DestroyImmediate(temp);
  442. }
  443. void Record()
  444. {
  445. foreach (var item in data)
  446. {
  447. if (item.obj == null) continue;
  448. EnableColliders(item);
  449. DestroyTempColliders(item);
  450. item.obj.transform.position = item.poses.First().position;
  451. item.obj.transform.rotation = item.poses.First().rotation;
  452. UnityEditor.Undo.RecordObject(item.obj.transform, "Simulate Gravity");
  453. item.obj.transform.position = item.poses.Last().position;
  454. item.obj.transform.rotation = item.poses.Last().rotation;
  455. }
  456. }
  457. var isPlaying = false;
  458. if (_stop)
  459. {
  460. if (recordAction) Record();
  461. return;
  462. }
  463. foreach (var item in data)
  464. {
  465. if (item.obj == null) break;
  466. if (frame >= item.poses.Count)
  467. {
  468. item.obj.transform.position = item.poses.Last().position;
  469. item.obj.transform.rotation = item.poses.Last().rotation;
  470. continue;
  471. }
  472. isPlaying = true;
  473. item.obj.transform.position = item.poses[frame].position;
  474. item.obj.transform.rotation = item.poses[frame].rotation;
  475. }
  476. if (isPlaying)
  477. {
  478. await System.Threading.Tasks.Task.Delay((int)(Time.fixedDeltaTime * 1000));
  479. Animate(data, frame + 1, recordAction);
  480. }
  481. else
  482. {
  483. if (recordAction) Record();
  484. else
  485. {
  486. foreach (var item in data)
  487. {
  488. if (item.obj == null) continue;
  489. EnableColliders(item);
  490. DestroyTempColliders(item);
  491. }
  492. }
  493. _isPlaying = false;
  494. }
  495. }
  496. [UnityEditor.InitializeOnLoad]
  497. private static class UndoEventHandler
  498. {
  499. static UndoEventHandler() => UnityEditor.Undo.undoRedoPerformed += OnUndoRedoPerformed;
  500. private static void OnUndoRedoPerformed()
  501. {
  502. _isPlaying = false;
  503. _stop = true;
  504. }
  505. }
  506. }
  507. }