123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /*
- Copyright (c) 2020 Omar Duarte
- Unauthorized copying of this file, via any medium is strictly prohibited.
- Writen by Omar Duarte, 2020.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- using System.Linq;
- using UnityEngine;
- namespace PluginMaster
- {
- [System.Serializable]
- public class SimulateGravityData
- {
- [SerializeField] private int _maxIterations = 1000;
- [SerializeField] private Vector3 _gravity = Physics.gravity;
- [SerializeField] private float _drag = 0f;
- [SerializeField] private float _angularDrag = 0.05f;
- [SerializeField] private float _maxSpeed = 100;
- private float _maxSpeedSquared = 10000;
- [SerializeField] private float _maxAngularSpeed = 10;
- private float _maxAngularSpeedSquared = 100;
- [SerializeField] private float _mass = 1f;
- [SerializeField] private bool _changeLayer = false;
- [SerializeField] private int _tempLayer = 0;
- [SerializeField] private bool _ignoreSceneColliders = false;
- public int maxIterations
- {
- get => _maxIterations;
- set
- {
- value = Mathf.Clamp(value, 1, 100000);
- if (_maxIterations == value) return;
- _maxIterations = value;
- }
- }
- public Vector3 gravity
- {
- get => _gravity;
- set
- {
- if (_gravity == value) return;
- _gravity = value;
- }
- }
- public float drag
- {
- get => _drag;
- set
- {
- value = Mathf.Max(value, 0f);
- if (_drag == value) return;
- _drag = value;
- }
- }
- public float angularDrag
- {
- get => _angularDrag;
- set
- {
- value = Mathf.Max(value, 0f);
- if (_angularDrag == value) return;
- _angularDrag = value;
- }
- }
- public float maxSpeed
- {
- get => _maxSpeed;
- set
- {
- value = Mathf.Max(value, 0f);
- if (_maxSpeed == value) return;
- _maxSpeed = value;
- _maxSpeedSquared = _maxSpeed * _maxSpeed;
- }
- }
- public float maxAngularSpeed
- {
- get => _maxAngularSpeed;
- set
- {
- value = Mathf.Max(value, 0f);
- if (_maxAngularSpeed == value) return;
- _maxAngularSpeed = value;
- _maxAngularSpeedSquared = _maxAngularSpeed * _maxAngularSpeed;
- }
- }
- public float maxSpeedSquared => _maxSpeedSquared;
- public float maxAngularSpeedSquared => _maxAngularSpeedSquared;
- public float mass
- {
- get => _mass;
- set
- {
- value = Mathf.Max(value, 1e-7f);
- if (_mass == value) return;
- _mass = value;
- }
- }
- public bool changeLayer
- {
- get => _changeLayer;
- set
- {
- if (_changeLayer == value) return;
- _changeLayer = value;
- }
- }
- public int tempLayer
- {
- get => _tempLayer;
- set
- {
- if (_tempLayer == value) return;
- _tempLayer = value;
- }
- }
- public bool ignoreSceneColliders
- {
- get => _ignoreSceneColliders;
- set
- {
- if (_ignoreSceneColliders == value) return;
- _ignoreSceneColliders = value;
- }
- }
- public void Copy(SimulateGravityData other)
- {
- _maxIterations = other._maxIterations;
- _gravity = other._gravity;
- _drag = other._drag;
- _angularDrag = other._angularDrag;
- _maxSpeed = other._maxSpeed;
- _maxSpeedSquared = other._maxSpeedSquared;
- _maxAngularSpeed = other._maxAngularSpeed;
- _maxAngularSpeedSquared = other._maxAngularSpeedSquared;
- _mass = other._mass;
- _changeLayer = other._changeLayer;
- _tempLayer = other._tempLayer;
- _ignoreSceneColliders = other._ignoreSceneColliders;
- }
- }
- public static class GravityUtils
- {
- private static bool _isPlaying = false;
- private static bool _stop = false;
- private static void AddCollider(GameObject obj, Mesh mesh, SimulateGravityData data)
- {
- var collider = MeshUtils.AddCollider(mesh, obj);
- if (collider is MeshCollider) (collider as MeshCollider).convex = true;
- if (data.changeLayer) obj.layer = data.tempLayer;
- }
- private static bool IsVisible(GameObject obj)
- {
- if (obj == null) return false;
- var target = obj;
- var parentRenderer = target.GetComponentInParent<Renderer>();
- var parentTerrain = target.GetComponentInParent<Terrain>();
- if (parentRenderer != null) target = parentRenderer.gameObject;
- else if (parentTerrain != null) target = parentTerrain.gameObject;
- else
- {
- var parent = target.transform.parent;
- if (parent != null)
- {
- var siblingRenderer = parent.GetComponentInChildren<Renderer>();
- var siblingTerrain = parent.GetComponentInChildren<Terrain>();
- if (siblingRenderer != null) target = parent.gameObject;
- else if (siblingTerrain != null) target = parent.gameObject;
- }
- }
- var renderers = target.GetComponentsInChildren<Renderer>();
- if (renderers.Length > 0)
- {
- foreach (var renderer in renderers)
- if (renderer.enabled) return true;
- }
- var terrains = target.GetComponentsInChildren<Terrain>();
- if (terrains.Length > 0)
- {
- foreach (var terrain in terrains)
- if (terrain.enabled) return true;
- }
- return false;
- }
- public static Pose[] SimulateGravity(GameObject[] selection, SimulateGravityData simData, bool recordAction)
- {
- if (_isPlaying && recordAction) return null;
- System.Collections.Generic.List<Collider> tempColliders = null;
- Collider[] sceneColliders = null;
- Collider[] invisibleColliders = null;
- if (simData.ignoreSceneColliders)
- {
- #if UNITY_2022_2_OR_NEWER
- sceneColliders = Object.FindObjectsByType<Collider>(FindObjectsSortMode.None)
- .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !sc.isTrigger).ToArray();
- #else
- sceneColliders = Object.FindObjectsOfType<Collider>()
- .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !sc.isTrigger).ToArray();
- #endif
- foreach (var sceneCollider in sceneColliders) sceneCollider.enabled = false;
- #if UNITY_2022_2_OR_NEWER
- var sceneMeshFilters = Object.FindObjectsByType<MeshFilter>(FindObjectsSortMode.None)
- .Where(mf => mf.gameObject.activeInHierarchy && mf.sharedMesh != null).ToArray();
- #else
- var sceneMeshFilters = Object.FindObjectsOfType<MeshFilter>()
- .Where(mf => mf.gameObject.activeInHierarchy && mf.sharedMesh != null).ToArray();
- #endif
- tempColliders = new System.Collections.Generic.List<Collider>();
- foreach (var meshFilter in sceneMeshFilters)
- {
- var mesh = meshFilter.sharedMesh;
- Collider tempCollider = MeshUtils.AddCollider(meshFilter.sharedMesh, meshFilter.gameObject);
- if (tempCollider != null) tempColliders.Add(tempCollider);
- }
- }
- else
- {
- #if UNITY_2022_2_OR_NEWER
- invisibleColliders = Object.FindObjectsByType<Collider>(FindObjectsSortMode.None)
- .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !IsVisible(sc.gameObject)).ToArray();
- #else
- invisibleColliders = Object.FindObjectsOfType<Collider>()
- .Where(sc => sc.enabled && sc.gameObject.activeInHierarchy && !IsVisible(sc.gameObject)).ToArray();
- #endif
- foreach (var invisibleCollider in invisibleColliders) invisibleCollider.enabled = false;
- }
- var originalGravity = Physics.gravity;
- Physics.gravity = simData.gravity;
- #if UNITY_2022_2_OR_NEWER
- var allBodies = Object.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None);
- #else
- var allBodies = Object.FindObjectsOfType<Rigidbody>();
- #endif
- var originalBodies = new System.Collections.Generic.List<(Rigidbody body, bool useGravity, bool isKinematic,
- float drag, float angularDrag, float mass, RigidbodyConstraints constraints,
- RigidbodyInterpolation interpolation, CollisionDetectionMode collisionDetectionMode)>();
- foreach (var rigidBody in allBodies)
- {
- originalBodies.Add((rigidBody, rigidBody.useGravity, rigidBody.isKinematic,
- #if UNITY_6000_0_OR_NEWER
- rigidBody.linearDamping, rigidBody.angularDamping,
- #else
- rigidBody.drag, rigidBody.angularDrag,
- #endif
- rigidBody.mass, rigidBody.constraints, rigidBody.interpolation, rigidBody.collisionDetectionMode));
- rigidBody.useGravity = false;
- rigidBody.isKinematic = true;
- #if UNITY_6000_0_OR_NEWER
- rigidBody.linearDamping = simData.drag;
- rigidBody.angularDamping = simData.angularDrag;
- #else
- rigidBody.drag = simData.drag;
- rigidBody.angularDrag = simData.angularDrag;
- #endif
- rigidBody.mass = simData.mass;
- rigidBody.constraints = RigidbodyConstraints.None;
- rigidBody.interpolation = RigidbodyInterpolation.None;
- rigidBody.collisionDetectionMode = CollisionDetectionMode.Discrete;
- }
- var simBodies = new System.Collections.Generic.List<Rigidbody>();
- var clones = new GameObject[selection.Length];
- var animData = new System.Collections.Generic.List<AnimData>();
- void AddColliders(GameObject source, GameObject dest)
- {
- var meshFilters = source.GetComponents<MeshFilter>();
- foreach (var meshFilter in meshFilters)
- {
- var mesh = meshFilter.sharedMesh;
- if (mesh == null) continue;
- AddCollider(dest, mesh, simData);
- }
- var skinnedMeshRenderers = source.GetComponents<SkinnedMeshRenderer>();
- foreach (var renderer in skinnedMeshRenderers)
- {
- var mesh = renderer.sharedMesh;
- if (mesh == null) continue;
- AddCollider(dest, mesh, simData);
- }
- foreach (Transform child in source.transform)
- {
- var destChild = new GameObject();
- destChild.transform.SetParent(dest.transform);
- destChild.transform.localPosition = child.localPosition;
- destChild.transform.localRotation = child.localRotation;
- destChild.transform.localScale = child.localScale;
- AddColliders(child.gameObject, destChild);
- }
- }
- for (int i = 0; i < selection.Length; ++i)
- {
- animData.Add(new AnimData(selection[i]));
- var obj = new GameObject();
- obj.layer = selection[i].layer;
- obj.transform.position = selection[i].transform.position;
- obj.transform.rotation = selection[i].transform.rotation;
- obj.transform.localScale = selection[i].transform.lossyScale;
- AddColliders(selection[i], obj);
- var magnitude = BoundsUtils.GetMagnitude(selection[i].transform);
- selection[i].transform.position += Vector3.up * (100 * magnitude);
- var rigidBody = obj.AddComponent<Rigidbody>();
- if (simData.changeLayer) obj.layer = simData.tempLayer;
- simBodies.Add(rigidBody);
- rigidBody.useGravity = true;
- rigidBody.isKinematic = false;
- clones[i] = obj;
- }
- #if UNITY_2022_2_OR_NEWER
- var prevSimMode = Physics.simulationMode;
- Physics.simulationMode = SimulationMode.Script;
- #else
- Physics.autoSimulation = false;
- #endif
- for (int i = 0; i < simData.maxIterations; ++i)
- {
- Physics.Simulate(Time.fixedDeltaTime);
- for (int objIdx = 0; objIdx < selection.Length; ++objIdx)
- {
- var body = simBodies[objIdx];
- #if UNITY_6000_0_OR_NEWER
- if (body.linearVelocity.sqrMagnitude > simData.maxSpeedSquared)
- body.linearVelocity = body.linearVelocity.normalized * simData.maxSpeed;
- #else
- if (body.velocity.sqrMagnitude > simData.maxSpeedSquared)
- body.velocity = body.velocity.normalized * simData.maxSpeed;
- #endif
- if (body.angularVelocity.sqrMagnitude > simData.maxAngularSpeedSquared)
- body.angularVelocity = body.angularVelocity.normalized * simData.maxAngularSpeed;
- if (i % 10 == 0) animData[objIdx].poses.Add(new Pose(body.position, body.rotation));
- }
- if (simBodies.All(rb => rb.IsSleeping())) break;
- }
- #if UNITY_2022_2_OR_NEWER
- Physics.simulationMode = prevSimMode;
- #else
- Physics.autoSimulation = true;
- #endif
- for (int i = 0; i < selection.Length; ++i)
- {
- selection[i].transform.position = clones[i].transform.position;
- selection[i].transform.rotation = clones[i].transform.rotation;
- animData[i].poses.Add(new Pose(selection[i].transform.position, selection[i].transform.rotation));
- Object.DestroyImmediate(clones[i]);
- }
- foreach (var item in originalBodies)
- {
- if (item.body == null) continue;
- item.body.useGravity = item.useGravity;
- item.body.isKinematic = item.isKinematic;
- #if UNITY_6000_0_OR_NEWER
- item.body.linearDamping = item.drag;
- item.body.angularDamping = item.angularDrag;
- #else
- item.body.drag = item.drag;
- item.body.angularDrag = item.angularDrag;
- #endif
- item.body.mass = item.mass;
- item.body.constraints = item.constraints;
- item.body.interpolation = item.interpolation;
- item.body.collisionDetectionMode = item.collisionDetectionMode;
- }
- Physics.gravity = originalGravity;
- if (simData.ignoreSceneColliders)
- {
- foreach (var sceneCollider in sceneColliders) sceneCollider.enabled = true;
- foreach (var tempCollider in tempColliders) Object.DestroyImmediate(tempCollider);
- }
- else
- {
- foreach (var invisibleCollider in invisibleColliders) invisibleCollider.enabled = true;
- }
- Animate(animData, simData, recordAction);
- var finalPoses = new System.Collections.Generic.List<Pose>();
- foreach (var d in animData) finalPoses.Add(d.poses.Last());
- return finalPoses.ToArray();
- }
- private class AnimData
- {
- public GameObject obj = null;
- public System.Collections.Generic.List<Pose> poses = new System.Collections.Generic.List<Pose>();
- public System.Collections.Generic.List<Collider> enabledColliders
- = new System.Collections.Generic.List<Collider>();
- public System.Collections.Generic.List<GameObject> tempColliders
- = new System.Collections.Generic.List<GameObject>();
- public AnimData(GameObject obj) => this.obj = obj;
- }
- private static void Animate(System.Collections.Generic.List<AnimData> animData,
- SimulateGravityData simData, bool recordAction)
- {
- _stop = false;
- _isPlaying = true;
- foreach (var item in animData)
- {
- item.enabledColliders = item.obj.GetComponentsInChildren<Collider>()
- .Where(collider => collider.enabled).ToList();
- item.tempColliders.Clear();
- var temp = new GameObject();
- temp.hideFlags = HideFlags.HideAndDontSave;
- var lastPose = item.poses.Last();
- temp.transform.position = lastPose.position;
- temp.transform.rotation = lastPose.rotation;
- temp.transform.localScale = item.obj.transform.lossyScale;
- var meshFilters = item.obj.GetComponentsInChildren<MeshFilter>();
- foreach (var meshFilter in meshFilters)
- {
- var mesh = meshFilter.sharedMesh;
- if (mesh == null) continue;
- AddCollider(temp, mesh, simData);
- }
- var skinnedMeshRenderers = item.obj.GetComponentsInChildren<SkinnedMeshRenderer>();
- foreach (var renderer in skinnedMeshRenderers)
- {
- var mesh = renderer.sharedMesh;
- if (mesh == null) continue;
- var meshFilter = renderer.gameObject.AddComponent<MeshFilter>();
- meshFilter.sharedMesh = mesh;
- AddCollider(temp, mesh, simData);
- }
- item.tempColliders.Add(temp);
- foreach (var collider in item.enabledColliders) collider.enabled = false;
- }
- Animate(animData, 0, recordAction);
- }
- private async static void Animate(System.Collections.Generic.List<AnimData> data, int frame, bool recordAction)
- {
- void EnableColliders(AnimData item)
- {
- foreach (var collider in item.enabledColliders)
- {
- if (collider == null) continue;
- collider.enabled = true;
- }
- }
- void DestroyTempColliders(AnimData item)
- {
- foreach (var temp in item.tempColliders) UnityEngine.Object.DestroyImmediate(temp);
- }
- void Record()
- {
- foreach (var item in data)
- {
- if (item.obj == null) continue;
- EnableColliders(item);
- DestroyTempColliders(item);
- item.obj.transform.position = item.poses.First().position;
- item.obj.transform.rotation = item.poses.First().rotation;
- UnityEditor.Undo.RecordObject(item.obj.transform, "Simulate Gravity");
- item.obj.transform.position = item.poses.Last().position;
- item.obj.transform.rotation = item.poses.Last().rotation;
- }
- }
- var isPlaying = false;
- if (_stop)
- {
- if (recordAction) Record();
- return;
- }
- foreach (var item in data)
- {
- if (item.obj == null) break;
- if (frame >= item.poses.Count)
- {
- item.obj.transform.position = item.poses.Last().position;
- item.obj.transform.rotation = item.poses.Last().rotation;
- continue;
- }
- isPlaying = true;
- item.obj.transform.position = item.poses[frame].position;
- item.obj.transform.rotation = item.poses[frame].rotation;
- }
- if (isPlaying)
- {
- await System.Threading.Tasks.Task.Delay((int)(Time.fixedDeltaTime * 1000));
- Animate(data, frame + 1, recordAction);
- }
- else
- {
- if (recordAction) Record();
- else
- {
- foreach (var item in data)
- {
- if (item.obj == null) continue;
- EnableColliders(item);
- DestroyTempColliders(item);
- }
- }
- _isPlaying = false;
- }
- }
- [UnityEditor.InitializeOnLoad]
- private static class UndoEventHandler
- {
- static UndoEventHandler() => UnityEditor.Undo.undoRedoPerformed += OnUndoRedoPerformed;
- private static void OnUndoRedoPerformed()
- {
- _isPlaying = false;
- _stop = true;
- }
- }
- }
- }
|