BoundsUtils.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. /*
  2. Copyright (c) 2021 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2021.
  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 UnityEngine;
  14. using System.Linq;
  15. namespace PluginMaster
  16. {
  17. public static class BoundsUtils
  18. {
  19. public static readonly Vector3 MIN_VECTOR3 = new Vector3(float.MinValue, float.MinValue, float.MinValue);
  20. public static readonly Vector3 MAX_VECTOR3 = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
  21. public enum ObjectProperty
  22. {
  23. BOUNDING_BOX,
  24. CENTER,
  25. PIVOT
  26. }
  27. public static Vector3 GetMaxVector(Vector3[] values)
  28. {
  29. var max = MIN_VECTOR3;
  30. foreach (var value in values) max = Vector3.Max(max, value);
  31. return max;
  32. }
  33. public static Vector3 GetMaxSize(GameObject[] objs)
  34. {
  35. var max = MIN_VECTOR3;
  36. foreach (var obj in objs)
  37. {
  38. var size = Vector3.zero;
  39. if (obj != null) size = GetBoundsRecursive(obj.transform).size;
  40. max = Vector3.Max(max, size);
  41. }
  42. return max;
  43. }
  44. private static System.Collections.Generic.Dictionary<(int, ObjectProperty), Bounds> _boundsDictionary
  45. = new System.Collections.Generic.Dictionary<(int, ObjectProperty), Bounds>();
  46. public static Bounds GetBounds(Transform transform, ObjectProperty property = ObjectProperty.BOUNDING_BOX,
  47. bool useDictionary = true)
  48. {
  49. var key = (transform.gameObject.GetInstanceID(), property);
  50. if (useDictionary && _boundsDictionary.ContainsKey(key)) return _boundsDictionary[key];
  51. var terrain = transform.GetComponent<Terrain>();
  52. var renderer = transform.GetComponent<Renderer>();
  53. var rectTransform = transform.GetComponent<RectTransform>();
  54. var lodGroup = renderer == null ? transform.GetComponent<LODGroup>() : null;
  55. Bounds DoGetBounds()
  56. {
  57. if (lodGroup != null && property == ObjectProperty.BOUNDING_BOX)
  58. {
  59. var lods = lodGroup.GetLODs();
  60. if (lods != null && lods.Length > 0)
  61. {
  62. lods = lods.Where(l => l.renderers != null).ToArray();
  63. if (lods.Length > 0)
  64. {
  65. var lodGameObjects = lods[0].renderers.Where(r => r != null).Select(r => r.gameObject)
  66. .Where(o => o.GetComponent<LODGroup>() == null).ToArray();
  67. return GetSelectionBounds(lodGameObjects, false);
  68. }
  69. }
  70. }
  71. if (rectTransform == null && terrain == null)
  72. {
  73. if (renderer == null || !renderer.enabled || property == ObjectProperty.PIVOT)
  74. return new Bounds(transform.position, Vector3.zero);
  75. if (property == ObjectProperty.CENTER) return new Bounds(renderer.bounds.center, Vector3.zero);
  76. return renderer.bounds;
  77. }
  78. if (property == ObjectProperty.PIVOT) return new Bounds(transform.position, Vector3.zero);
  79. if (terrain != null)
  80. {
  81. var bounds = terrain.terrainData.bounds;
  82. bounds.center += transform.position;
  83. return bounds;
  84. }
  85. return new Bounds(rectTransform.TransformPoint(rectTransform.rect.center),
  86. rectTransform.TransformVector(rectTransform.rect.size));
  87. }
  88. var result = DoGetBounds();
  89. if (useDictionary) _boundsDictionary.Add(key, result);
  90. return result;
  91. }
  92. private static System.Collections.Generic.Dictionary<(int, ObjectProperty, Vector2), Bounds>
  93. _boundsRecursiveDictionary = new System.Collections.Generic.Dictionary<(int, ObjectProperty, Vector2), Bounds>();
  94. public static Bounds GetBoundsRecursive(Transform transform, bool recursive = true,
  95. ObjectProperty property = ObjectProperty.BOUNDING_BOX, bool useDictionary = true)
  96. {
  97. if (!recursive) return GetBounds(transform, property, useDictionary);
  98. var pivot2D = Vector2.zero;
  99. var spriteRenderer = transform.GetComponent<SpriteRenderer>();
  100. if (spriteRenderer != null && spriteRenderer.enabled && spriteRenderer.sprite != null)
  101. pivot2D = spriteRenderer.sprite.pivot;
  102. var key = (transform.gameObject.GetInstanceID(), property, pivot2D);
  103. if (useDictionary && _boundsRecursiveDictionary.ContainsKey(key))
  104. return _boundsRecursiveDictionary[key];
  105. var children = transform.GetComponentsInChildren<Transform>(true);
  106. var min = MAX_VECTOR3;
  107. var max = MIN_VECTOR3;
  108. var emptyHierarchy = true;
  109. bool IsActiveInHierarchy(Transform obj)
  110. {
  111. var parent = obj;
  112. do
  113. {
  114. if (!parent.gameObject.activeSelf) return false;
  115. parent = parent.parent;
  116. }
  117. while (parent != null);
  118. return true;
  119. }
  120. foreach (var child in children)
  121. {
  122. var notActive = !IsActiveInHierarchy(child);
  123. if (notActive) continue;
  124. var renderer = child.GetComponent<Renderer>();
  125. var rectTransform = child.GetComponent<RectTransform>();
  126. var terrain = child.GetComponent<Terrain>();
  127. var lodGroup = child.GetComponent<LODGroup>();
  128. if ((renderer == null || !renderer.enabled) && rectTransform == null && terrain == null && lodGroup == null)
  129. continue;
  130. var bounds = GetBounds(child, property, useDictionary);
  131. if (bounds.size == Vector3.zero) continue;
  132. emptyHierarchy = false;
  133. min = Vector3.Min(bounds.min, min);
  134. max = Vector3.Max(bounds.max, max);
  135. }
  136. if (emptyHierarchy) return new Bounds(transform.position, Vector3.zero);
  137. var size = max - min;
  138. var center = min + size / 2f;
  139. var result = new Bounds(center, size);
  140. if (useDictionary) _boundsRecursiveDictionary.Add(key, result);
  141. return result;
  142. }
  143. public static Bounds GetSelectionBounds(GameObject[] selection, bool recursive = true,
  144. BoundsUtils.ObjectProperty property = BoundsUtils.ObjectProperty.BOUNDING_BOX)
  145. {
  146. var max = MIN_VECTOR3;
  147. var min = MAX_VECTOR3;
  148. if (selection.Length == 0) return new Bounds();
  149. foreach (var obj in selection)
  150. {
  151. if (obj == null) continue;
  152. var bounds = GetBoundsRecursive(obj.transform, recursive, property);
  153. max = Vector3.Max(bounds.max, max);
  154. min = Vector3.Min(bounds.min, min);
  155. }
  156. var size = max - min;
  157. var center = min + size / 2f;
  158. return new Bounds(center, size);
  159. }
  160. public static Bounds GetBounds(Transform transform, Quaternion rotation, bool useDictionary = true)
  161. {
  162. var rectTransform = transform.GetComponent<RectTransform>();
  163. if (rectTransform != null)
  164. return new Bounds(rectTransform.TransformPoint(rectTransform.rect.center),
  165. rectTransform.TransformVector(rectTransform.rect.size));
  166. var renderer = transform.GetComponent<Renderer>();
  167. var meshFilter = transform.GetComponent<MeshFilter>();
  168. if (renderer == null || meshFilter == null || meshFilter.sharedMesh == null || !renderer.enabled)
  169. return new Bounds(transform.position, Vector3.zero);
  170. var maxSqrDistance = MIN_VECTOR3;
  171. var minSqrDistance = MAX_VECTOR3;
  172. var center = GetBounds(transform, ObjectProperty.BOUNDING_BOX, true).center;
  173. var right = rotation * Vector3.right;
  174. var up = rotation * Vector3.up;
  175. var forward = rotation * Vector3.forward;
  176. foreach (var vertex in meshFilter.sharedMesh.vertices)
  177. {
  178. var centerToVertex = transform.TransformPoint(vertex) - center;
  179. var rightProjection = Vector3.Project(centerToVertex, right);
  180. var upProjection = Vector3.Project(centerToVertex, up);
  181. var forwardProjection = Vector3.Project(centerToVertex, forward);
  182. var rightSqrDistance = rightProjection.sqrMagnitude * (rightProjection.normalized != right ? -1 : 1);
  183. var upSqrDistance = upProjection.sqrMagnitude * (upProjection.normalized != up ? -1 : 1);
  184. var forwardSqrDistance = forwardProjection.sqrMagnitude
  185. * (forwardProjection.normalized != forward ? -1 : 1);
  186. maxSqrDistance.x = Mathf.Max(maxSqrDistance.x, rightSqrDistance);
  187. maxSqrDistance.y = Mathf.Max(maxSqrDistance.y, upSqrDistance);
  188. maxSqrDistance.z = Mathf.Max(maxSqrDistance.z, forwardSqrDistance);
  189. minSqrDistance.x = Mathf.Min(minSqrDistance.x, rightSqrDistance);
  190. minSqrDistance.y = Mathf.Min(minSqrDistance.y, upSqrDistance);
  191. minSqrDistance.z = Mathf.Min(minSqrDistance.z, forwardSqrDistance);
  192. }
  193. var size = new Vector3(
  194. Mathf.Sqrt(Mathf.Abs(maxSqrDistance.x)) * Mathf.Sign(maxSqrDistance.x)
  195. - Mathf.Sqrt(Mathf.Abs(minSqrDistance.x)) * Mathf.Sign(minSqrDistance.x),
  196. Mathf.Sqrt(Mathf.Abs(maxSqrDistance.y)) * Mathf.Sign(maxSqrDistance.y)
  197. - Mathf.Sqrt(Mathf.Abs(minSqrDistance.y)) * Mathf.Sign(minSqrDistance.y),
  198. Mathf.Sqrt(Mathf.Abs(maxSqrDistance.z)) * Mathf.Sign(maxSqrDistance.z)
  199. - Mathf.Sqrt(Mathf.Abs(minSqrDistance.z)) * Mathf.Sign(minSqrDistance.z));
  200. return new Bounds(center, size);
  201. }
  202. private static void GetDistanceFromCenter(Transform transform, Quaternion rotation,
  203. Vector3 center, out Vector3 min, out Vector3 max, bool ignoreDisabled = true)
  204. {
  205. min = max = Vector3.zero;
  206. if (ignoreDisabled && !transform.gameObject.activeSelf) return;
  207. var vertices = new System.Collections.Generic.List<Vector3>();
  208. var rectTransform = transform.GetComponent<RectTransform>();
  209. var terrain = transform.GetComponent<Terrain>();
  210. if (rectTransform != null)
  211. {
  212. vertices.Add(rectTransform.rect.min);
  213. vertices.Add(rectTransform.rect.max);
  214. vertices.Add(new Vector2(rectTransform.rect.min.x, rectTransform.rect.max.y));
  215. vertices.Add(new Vector2(rectTransform.rect.max.x, rectTransform.rect.min.y));
  216. }
  217. else if (terrain != null) vertices.AddRange(TerrainUtils.GetCorners(terrain, Space.Self));
  218. else
  219. {
  220. var renderer = transform.GetComponent<Renderer>();
  221. if (renderer == null || !renderer.enabled) return;
  222. if (renderer is SpriteRenderer)
  223. {
  224. var sprite = (renderer as SpriteRenderer).sprite;
  225. if (sprite == null) return;
  226. var rot = renderer.transform.rotation;
  227. renderer.transform.rotation = Quaternion.Euler(0, 0, 0);
  228. var spriteMin = renderer.transform.InverseTransformPoint(renderer.bounds.min);
  229. var spriteMax = renderer.transform.InverseTransformPoint(renderer.bounds.max);
  230. renderer.transform.rotation = rot;
  231. vertices.Add(new Vector3(spriteMin.x, spriteMin.y, 0));
  232. vertices.Add(new Vector3(spriteMin.x, spriteMax.y, 0));
  233. vertices.Add(new Vector3(spriteMax.x, spriteMin.y, 0));
  234. vertices.Add(new Vector3(spriteMax.x, spriteMax.y, 0));
  235. }
  236. else if (renderer is MeshRenderer)
  237. {
  238. var meshFilter = transform.GetComponent<MeshFilter>();
  239. if (meshFilter == null || meshFilter.sharedMesh == null) return;
  240. vertices.AddRange(meshFilter.sharedMesh.vertices);
  241. }
  242. else if (renderer is SkinnedMeshRenderer)
  243. {
  244. var mesh = (renderer as SkinnedMeshRenderer).sharedMesh;
  245. if (mesh == null) return;
  246. vertices.AddRange(mesh.vertices);
  247. }
  248. }
  249. if (vertices.Count == 0)
  250. {
  251. min = max = Vector3.zero;
  252. return;
  253. }
  254. var maxDistance = MIN_VECTOR3;
  255. var minDistance = MAX_VECTOR3;
  256. var right = rotation * Vector3.right;
  257. var up = rotation * Vector3.up;
  258. var forward = rotation * Vector3.forward;
  259. foreach (var vertex in vertices)
  260. {
  261. var centerToVertex = transform.TransformPoint(vertex) - center;
  262. var rightProjection = Vector3.Project(centerToVertex, right);
  263. var upProjection = Vector3.Project(centerToVertex, up);
  264. var forwardProjection = Vector3.Project(centerToVertex, forward);
  265. var rightDistance = rightProjection.magnitude * (rightProjection.normalized != right ? -1 : 1);
  266. var upDistance = upProjection.magnitude * (upProjection.normalized != up ? -1 : 1);
  267. var forwardDistance = forwardProjection.magnitude
  268. * (forwardProjection.normalized != forward ? -1 : 1);
  269. maxDistance.x = Mathf.Max(maxDistance.x, rightDistance);
  270. maxDistance.y = Mathf.Max(maxDistance.y, upDistance);
  271. maxDistance.z = Mathf.Max(maxDistance.z, forwardDistance);
  272. minDistance.x = Mathf.Min(minDistance.x, rightDistance);
  273. minDistance.y = Mathf.Min(minDistance.y, upDistance);
  274. minDistance.z = Mathf.Min(minDistance.z, forwardDistance);
  275. }
  276. min = new Vector3(
  277. Mathf.Abs(minDistance.x) * Mathf.Sign(minDistance.x),
  278. Mathf.Abs(minDistance.y) * Mathf.Sign(minDistance.y),
  279. Mathf.Abs(minDistance.z) * Mathf.Sign(minDistance.z));
  280. max = new Vector3(
  281. Mathf.Abs(maxDistance.x) * Mathf.Sign(maxDistance.x),
  282. Mathf.Abs(maxDistance.y) * Mathf.Sign(maxDistance.y),
  283. Mathf.Abs(maxDistance.z) * Mathf.Sign(maxDistance.z));
  284. }
  285. private static void GetDistanceFromCenterRecursive(Transform transform, Quaternion rotation,
  286. Vector3 center, out Vector3 minDistance, out Vector3 maxDistance, bool ignoreDissabled = true, bool recursive = true)
  287. {
  288. var children = recursive ? transform.GetComponentsInChildren<Transform>(true) : new Transform[] { transform };
  289. var emptyHierarchy = true;
  290. maxDistance = MIN_VECTOR3;
  291. minDistance = MAX_VECTOR3;
  292. foreach (var child in children)
  293. {
  294. var renderer = child.GetComponent<Renderer>();
  295. var rectTransform = child.GetComponent<RectTransform>();
  296. var terrain = child.GetComponent<Terrain>();
  297. if ((renderer == null || !renderer.enabled) && rectTransform == null && terrain == null) continue;
  298. emptyHierarchy = false;
  299. Vector3 min, max;
  300. GetDistanceFromCenter(child, rotation, center, out min, out max, ignoreDissabled);
  301. minDistance = Vector3.Min(min, minDistance);
  302. maxDistance = Vector3.Max(max, maxDistance);
  303. }
  304. if (emptyHierarchy) minDistance = maxDistance = Vector3.zero;
  305. }
  306. private struct BoundsRotKey
  307. {
  308. int id;
  309. Vector3 position;
  310. Quaternion rotation;
  311. Vector3 scale;
  312. Quaternion boundsRotation;
  313. Vector2 pivot2D;
  314. public BoundsRotKey(int id, Vector3 position, Quaternion rotation,
  315. Vector3 scale, Quaternion boundsRotation, Vector2 pivot2D)
  316. {
  317. this.id = id;
  318. this.position = position;
  319. this.rotation = rotation;
  320. this.scale = scale;
  321. this.boundsRotation = boundsRotation;
  322. this.pivot2D = pivot2D;
  323. }
  324. }
  325. private static System.Collections.Generic
  326. .Dictionary<BoundsRotKey, Bounds> _boundsRotDictionary
  327. = new System.Collections.Generic.Dictionary<BoundsRotKey, Bounds>();
  328. public static Bounds GetBoundsRecursive(Transform transform, Quaternion rotation, bool ignoreDissabled = true,
  329. ObjectProperty property = ObjectProperty.BOUNDING_BOX, bool recursive = true, bool useDictionary = true)
  330. {
  331. if (property == ObjectProperty.PIVOT) return new Bounds(transform.position, Vector3.zero);
  332. var pivot2D = Vector2.zero;
  333. var spriteRenderer = transform.GetComponent<SpriteRenderer>();
  334. if (spriteRenderer != null && spriteRenderer.enabled && spriteRenderer.sprite != null)
  335. pivot2D = spriteRenderer.sprite.pivot;
  336. var key = new BoundsRotKey(transform.gameObject.GetInstanceID(), transform.position,
  337. transform.rotation, transform.lossyScale, rotation, pivot2D);
  338. useDictionary = false;
  339. if (useDictionary && _boundsRotDictionary.ContainsKey(key)) return _boundsRotDictionary[key];
  340. var center = GetBoundsRecursive(transform, recursive, property, useDictionary).center;
  341. if (property == ObjectProperty.CENTER) return new Bounds(center, Vector3.zero);
  342. Vector3 maxDistance, minDistance;
  343. GetDistanceFromCenterRecursive(transform, rotation, center,
  344. out minDistance, out maxDistance, ignoreDissabled, recursive);
  345. var size = maxDistance - minDistance;
  346. center += rotation * (minDistance + size / 2);
  347. var bounds = new Bounds(center, size);
  348. if (useDictionary) _boundsRotDictionary.Add(key, bounds);
  349. return new Bounds(center, size);
  350. }
  351. public static Bounds GetBoundsRecursive(Transform transform, Quaternion rotation, Vector3 scale,
  352. bool ignoreDissabled = true)
  353. {
  354. var obj = Object.Instantiate(transform.gameObject);
  355. obj.transform.localScale = Vector3.Scale(obj.transform.localScale, scale);
  356. var bounds = GetBoundsRecursive(obj.transform, rotation, ignoreDissabled);
  357. Object.DestroyImmediate(obj);
  358. return bounds;
  359. }
  360. public static Bounds GetSelectionBounds(GameObject[] selection, Quaternion rotation, bool ignoreDissabled = true)
  361. {
  362. var max = MIN_VECTOR3;
  363. var min = MAX_VECTOR3;
  364. var center = GetSelectionBounds(selection).center;
  365. bool empty = true;
  366. foreach (var obj in selection)
  367. {
  368. if (obj == null) continue;
  369. var objMagnitude = GetMagnitude(obj.transform);
  370. if (objMagnitude == 0) continue;
  371. Vector3 minDistance, maxDistance;
  372. GetDistanceFromCenterRecursive(obj.transform, rotation, center,
  373. out minDistance, out maxDistance, ignoreDissabled);
  374. max = Vector3.Max(maxDistance, max);
  375. min = Vector3.Min(minDistance, min);
  376. empty = false;
  377. }
  378. if (empty) return new Bounds(center, Vector3.zero);
  379. var size = max - min;
  380. center += rotation * (min + size / 2);
  381. return new Bounds(center, size);
  382. }
  383. public static Vector3[] GetVertices(Transform transform)
  384. {
  385. var vertices = new System.Collections.Generic.List<Vector3>();
  386. var meshFilters = transform.GetComponentsInChildren<MeshFilter>();
  387. foreach (var filter in meshFilters)
  388. {
  389. if (filter.sharedMesh == null) continue;
  390. vertices.AddRange(filter.sharedMesh.vertices);
  391. }
  392. var skinnedMeshRenderers = transform.GetComponentsInChildren<SkinnedMeshRenderer>();
  393. foreach (var renderer in skinnedMeshRenderers)
  394. {
  395. if (renderer.sharedMesh == null) continue;
  396. vertices.AddRange(renderer.sharedMesh.vertices);
  397. }
  398. return vertices.ToArray();
  399. }
  400. public static Vector3[] GetFurthestVertices(Transform transform, Vector3 direction, out float magnitude)
  401. {
  402. var vertices = new System.Collections.Generic.HashSet<Vector3>();
  403. var allLocalVertices = new System.Collections.Generic.HashSet<(Vector3 position, float distance)>();
  404. var maxDistance = float.MinValue;
  405. var meshFilters = transform.GetComponentsInChildren<MeshFilter>();
  406. void UpdateLastVertex(Vector3 vertex, Transform child)
  407. {
  408. var worldVertex = child.TransformPoint(vertex);
  409. var localVertex = transform.InverseTransformPoint(worldVertex);
  410. var projection = Vector3.Project(localVertex, direction);
  411. var projectionDistance = Mathf.Sign(Vector3.Dot(direction, projection))
  412. * Vector3.Project(localVertex, direction).magnitude;
  413. allLocalVertices.Add((localVertex, projectionDistance));
  414. maxDistance = Mathf.Max(maxDistance, projectionDistance);
  415. }
  416. foreach (var filter in meshFilters)
  417. {
  418. if (filter.sharedMesh == null) continue;
  419. foreach (var vertex in filter.sharedMesh.vertices) UpdateLastVertex(vertex, filter.transform);
  420. }
  421. var skinnedMeshRenderers = transform.GetComponentsInChildren<SkinnedMeshRenderer>();
  422. foreach (var renderer in skinnedMeshRenderers)
  423. {
  424. if (renderer.sharedMesh == null) continue;
  425. foreach (var vertex in renderer.sharedMesh.vertices) UpdateLastVertex(vertex, renderer.transform);
  426. }
  427. var threshold = maxDistance * 0.01f;
  428. magnitude = float.MinValue;
  429. foreach (var vertex in allLocalVertices)
  430. {
  431. if (vertex.distance >= maxDistance - threshold)
  432. {
  433. vertices.Add(vertex.position);
  434. var projection = Vector3.Scale(Vector3.Project(vertex.position, direction), transform.localScale);
  435. var projectionDistance = Mathf.Sign(Vector3.Dot(direction, projection))
  436. * Vector3.Project(vertex.position, direction).magnitude;
  437. magnitude = Mathf.Max(magnitude, projectionDistance);
  438. }
  439. }
  440. if (vertices.Count == 0) magnitude = 0f;
  441. return vertices.ToArray();
  442. }
  443. public static float GetMagnitudeInDirection(Transform transform, Vector3 direction)
  444. {
  445. float magnitude;
  446. var vertices = GetFurthestVertices(transform, direction, out magnitude);
  447. return magnitude;
  448. }
  449. public static Vector3[] GetBottomVertices(Transform transform)
  450. {
  451. var vertices = new System.Collections.Generic.HashSet<Vector3>();
  452. var allLocalVertices = new System.Collections.Generic.HashSet<Vector3>();
  453. var minY = float.MaxValue;
  454. var meshFilters = transform.GetComponentsInChildren<MeshFilter>();
  455. void UpdateMinVertex(Vector3 vertex, Transform child)
  456. {
  457. var worldVertex = child.TransformPoint(vertex);
  458. var localVertex = transform.InverseTransformPoint(worldVertex);
  459. allLocalVertices.Add(localVertex);
  460. minY = Mathf.Min(localVertex.y, minY);
  461. }
  462. foreach (var filter in meshFilters)
  463. {
  464. if (filter.sharedMesh == null) continue;
  465. foreach (var vertex in filter.sharedMesh.vertices) UpdateMinVertex(vertex, filter.transform);
  466. }
  467. var skinnedMeshRenderers = transform.GetComponentsInChildren<SkinnedMeshRenderer>();
  468. foreach (var renderer in skinnedMeshRenderers)
  469. {
  470. if (renderer.sharedMesh == null) continue;
  471. foreach (var vertex in renderer.sharedMesh.vertices) UpdateMinVertex(vertex, renderer.transform);
  472. }
  473. var threshold = 0.01f;
  474. minY += threshold;
  475. foreach (var vertex in allLocalVertices) if (vertex.y < minY) vertices.Add(vertex);
  476. return vertices.ToArray();
  477. }
  478. public static float GetBottomMagnitude(Transform transform)
  479. {
  480. var vertices = GetBottomVertices(transform);
  481. var magnitude = float.MinValue;
  482. foreach (var vertex in vertices)
  483. magnitude = Mathf.Max(magnitude, vertex.y);
  484. return magnitude * transform.localScale.y;
  485. }
  486. public static float GetMagnitude(Transform transform)
  487. {
  488. var size = GetBoundsRecursive(transform).size;
  489. return Mathf.Max(size.x, size.y, size.z);
  490. }
  491. public static float GetAverageMagnitude(Transform transform)
  492. {
  493. var size = GetBoundsRecursive(transform).size;
  494. return (size.x + size.y + size.z) / 3;
  495. }
  496. public static void ClearBoundsDictionaries()
  497. {
  498. _boundsDictionary.Clear();
  499. _boundsRecursiveDictionary.Clear();
  500. _boundsRotDictionary.Clear();
  501. }
  502. }
  503. }