BoundingBox.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. using SC.XR.Unity.Module_InputSystem;
  6. using UnityEngine.Events;
  7. using System;
  8. using SC.XR.Unity;
  9. [AddComponentMenu("SDK/BoundingBox")]
  10. public class BoundingBox : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
  11. {
  12. [SerializeField]
  13. [Tooltip("Type of activation method for showing/hiding bounding box handles and controls")]
  14. private BoundingBoxActivationType activation = BoundingBoxActivationType.ActivateOnStart;
  15. public BoundingBoxActivationType ActivationType
  16. {
  17. get
  18. {
  19. return activation;
  20. }
  21. }
  22. [SerializeField]
  23. [Tooltip("Flatten bounds in the specified axis or flatten the smallest one if 'auto' is selected")]
  24. private FlattenModeType flattenAxis = FlattenModeType.DoNotFlatten;
  25. public FlattenModeType FlattenAxis
  26. {
  27. get
  28. {
  29. return flattenAxis;
  30. }
  31. set
  32. {
  33. flattenAxis = value;
  34. Redraw();
  35. }
  36. }
  37. [SerializeField]
  38. private HandleType activeHandle = ~HandleType.None;
  39. public HandleType ActiveHandle
  40. {
  41. get
  42. {
  43. return activeHandle;
  44. }
  45. set
  46. {
  47. activeHandle = value;
  48. Redraw();
  49. }
  50. }
  51. [SerializeField]
  52. private BoundingBoxAssets m_HandlerAssets;
  53. public BoundingBoxAssets BoundingBoxAssets {
  54. get {
  55. if (m_HandlerAssets == null) {
  56. m_HandlerAssets = Resources.Load<BoundingBoxAssets>("HandlerAssets/DefaultAssets");
  57. }
  58. return m_HandlerAssets;
  59. }
  60. }
  61. //[SerializeField]
  62. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Materials/BoundingBox.mat", typeof(Material))]
  63. private Material m_boxFocusDisplayMat;
  64. public Material boxFocusDisplayMat {
  65. get {
  66. if (m_boxFocusDisplayMat == null) {
  67. if (BoundingBoxAssets) {
  68. m_boxFocusDisplayMat = BoundingBoxAssets.boxFocusDisplayMat;
  69. }
  70. }
  71. return m_boxFocusDisplayMat;
  72. }
  73. }
  74. //[SerializeField]
  75. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Materials/BoundingBoxGrabbed.mat", typeof(Material))]
  76. private Material m_boxGrabDisplayMat;
  77. public Material boxGrabDisplayMat {
  78. get {
  79. if (m_boxGrabDisplayMat == null) {
  80. if (BoundingBoxAssets) {
  81. m_boxGrabDisplayMat = BoundingBoxAssets.boxGrabDisplayMat;
  82. }
  83. }
  84. return m_boxGrabDisplayMat;
  85. }
  86. }
  87. //[SerializeField]
  88. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Materials/BoundingBoxHandleWhite.mat", typeof(Material))]
  89. private Material m_HandleMaterial;
  90. public Material HandleMaterial {
  91. get {
  92. if (m_HandleMaterial == null) {
  93. if (BoundingBoxAssets) {
  94. m_HandleMaterial = BoundingBoxAssets.HandleMaterial;
  95. }
  96. }
  97. return m_HandleMaterial;
  98. }
  99. }
  100. //[SerializeField]
  101. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Materials/BoundingBoxHandleBlueGrabbed.mat", typeof(Material))]
  102. private Material m_HandleGrabMaterial;
  103. public Material HandleGrabMaterial {
  104. get {
  105. if (m_HandleGrabMaterial == null) {
  106. if (BoundingBoxAssets) {
  107. m_HandleGrabMaterial = BoundingBoxAssets.HandleGrabMaterial;
  108. }
  109. }
  110. return m_HandleGrabMaterial;
  111. }
  112. }
  113. //[SerializeField]
  114. //[Header("Scale Handles")]
  115. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Prefabs/BoundingBox_ScaleHandle.prefab", typeof(GameObject))]
  116. private GameObject m_CornerPrefab;
  117. public GameObject CornerPrefab {
  118. get {
  119. if (m_CornerPrefab == null) {
  120. if (BoundingBoxAssets) {
  121. m_CornerPrefab = BoundingBoxAssets.CornerPrefab;
  122. }
  123. }
  124. return m_CornerPrefab;
  125. }
  126. }
  127. //[SerializeField]
  128. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Prefabs/BoundingBox_ScaleHandle_Slate.prefab", typeof(GameObject))]
  129. private GameObject m_CornerSlatePrefab;
  130. public GameObject CornerSlatePrefab {
  131. get {
  132. if (m_CornerSlatePrefab == null) {
  133. if (BoundingBoxAssets) {
  134. m_CornerSlatePrefab = BoundingBoxAssets.CornerSlatePrefab;
  135. }
  136. }
  137. return m_CornerSlatePrefab;
  138. }
  139. }
  140. public bool activeScaleMinRestrict = true;
  141. [Tooltip("Minimum scaling allowed relative to the world size Unit of m")]
  142. public float scaleMinimum = 0.05f;
  143. public bool activeScaleMaxRestrict = false;
  144. [Tooltip("Maximum scaling allowed relative to the world size Unit of m")]
  145. public float scaleMaximum = 2.0f;
  146. public Vector3 boundingBoxInitScale { get; private set; }
  147. public Vector3 calculateMinScale {
  148. get {
  149. Vector3 temp = (CurrentBoundsExtents * 2 / scaleMinimum);
  150. return new Vector3(boundingBoxInitScale.x / temp.x, boundingBoxInitScale.y / temp.y, boundingBoxInitScale.z / temp.z);
  151. }
  152. }
  153. public Vector3 calculateMaxScale {
  154. get {
  155. Vector3 temp = (CurrentBoundsExtents * 2 / scaleMaximum);
  156. return new Vector3(boundingBoxInitScale.x / temp.x, boundingBoxInitScale.y / temp.y, boundingBoxInitScale.z / temp.z);
  157. }
  158. }
  159. [SerializeField]
  160. [Tooltip("Size of the cube collidable used in scale handles")]
  161. private float scaleHandleSize = 0.016f; // 1.6cm default handle size
  162. public float ScaleHandleSize
  163. {
  164. get
  165. {
  166. return scaleHandleSize;
  167. }
  168. set
  169. {
  170. scaleHandleSize = value;
  171. Redraw();
  172. }
  173. }
  174. [HideInInspector]
  175. public BoxCollider BoundBoxCollider;
  176. // Half the size of the current bounds
  177. private Vector3 currentBoundsExtents;
  178. public Vector3 CurrentBoundsExtents
  179. {
  180. get
  181. {
  182. return currentBoundsExtents;
  183. }
  184. }
  185. //[SerializeField]
  186. //[Header("Rotation Handles")]
  187. //[AssetPreAssign("Assets/SDK/Common/StandardAssets/Prefabs/BoundingBox_RotateHandle.prefab", typeof(GameObject))]
  188. private GameObject m_SidePrefab;
  189. public GameObject SidePrefab {
  190. get {
  191. if (m_SidePrefab == null) {
  192. if (BoundingBoxAssets) {
  193. m_SidePrefab = BoundingBoxAssets.SidePrefab;
  194. }
  195. }
  196. return m_SidePrefab;
  197. }
  198. }
  199. [SerializeField]
  200. [Tooltip("Radius of the handle geometry of rotation handles")]
  201. private float rotationHandleSize = 0.016f; // 1.6cm default handle size
  202. public float RotationHandleSize
  203. {
  204. get
  205. {
  206. return rotationHandleSize;
  207. }
  208. set
  209. {
  210. rotationHandleSize = value;
  211. Redraw();
  212. }
  213. }
  214. [SerializeField]
  215. [Tooltip("Check to show rotation handles for the X axis")]
  216. private bool showRotationHandleForX = true;
  217. public bool ShowRotationHandleForX
  218. {
  219. get
  220. {
  221. return showRotationHandleForX;
  222. }
  223. set
  224. {
  225. showRotationHandleForX = value;
  226. Redraw();
  227. }
  228. }
  229. [SerializeField]
  230. [Tooltip("Check to show rotation handles for the Y axis")]
  231. private bool showRotationHandleForY = true;
  232. public bool ShowRotationHandleForY
  233. {
  234. get
  235. {
  236. return showRotationHandleForY;
  237. }
  238. set
  239. {
  240. showRotationHandleForY = value;
  241. Redraw();
  242. }
  243. }
  244. [SerializeField]
  245. [Tooltip("Check to show rotation handles for the Z axis")]
  246. private bool showRotationHandleForZ = true;
  247. public bool ShowRotationHandleForZ
  248. {
  249. get
  250. {
  251. return showRotationHandleForZ;
  252. }
  253. set
  254. {
  255. showRotationHandleForZ = value;
  256. Redraw();
  257. }
  258. }
  259. //[SerializeField]
  260. //[Header("AxisScale Handles")]
  261. private GameObject m_facePrefab;
  262. public GameObject facePrefab {
  263. get {
  264. if (m_facePrefab == null) {
  265. if (BoundingBoxAssets) {
  266. m_facePrefab = BoundingBoxAssets.facePrefab;
  267. }
  268. }
  269. return m_facePrefab;
  270. }
  271. }
  272. [SerializeField]
  273. [Tooltip("Radius of the handle geometry of rotation handles")]
  274. private float axisScaleHandleSize = 0.016f; // 1.6cm default handle size
  275. public float AxisScaleHandleSize
  276. {
  277. get
  278. {
  279. return axisScaleHandleSize;
  280. }
  281. set
  282. {
  283. axisScaleHandleSize = value;
  284. Redraw();
  285. }
  286. }
  287. [SerializeField]
  288. private AxisType activeAxis = ~AxisType.None;
  289. public AxisType ActiveAxis
  290. {
  291. get
  292. {
  293. return activeAxis;
  294. }
  295. set
  296. {
  297. activeAxis = value;
  298. Redraw();
  299. }
  300. }
  301. public Transform BoundingBoxContainer
  302. {
  303. get;
  304. set;
  305. }
  306. public BoundingBoxRoot BoundingBoxRoot
  307. {
  308. get;
  309. private set;
  310. }
  311. public CornerBoundingBoxRoot CornerBoundingBoxRoot
  312. {
  313. get;
  314. private set;
  315. }
  316. protected SideBoundingBoxRoot SideBoundingBoxRoot
  317. {
  318. get;
  319. set;
  320. }
  321. private FaceBoundingBoxRoot FaceBoundingBoxRoot
  322. {
  323. get;
  324. set;
  325. }
  326. private List<IBoundingBoxRoot> BoundingBoxRootList
  327. {
  328. get;
  329. set;
  330. }
  331. [Header("Audio")]
  332. [SerializeField]
  333. public SCAudiosConfig.AudioType RotateStartAudio = SCAudiosConfig.AudioType.Manipulation_Start;
  334. [SerializeField]
  335. public SCAudiosConfig.AudioType RotateStopAudio = SCAudiosConfig.AudioType.Manipulation_End;
  336. [SerializeField]
  337. public SCAudiosConfig.AudioType ScaleStartAudio = SCAudiosConfig.AudioType.Manipulation_Start;
  338. [SerializeField]
  339. public SCAudiosConfig.AudioType ScaleStopAudio = SCAudiosConfig.AudioType.Manipulation_End;
  340. [Header("Events")]
  341. /// <summary>
  342. /// Event that gets fired when interaction with a rotation handle starts.
  343. /// </summary>
  344. public UnityEvent RotateStarted = new UnityEvent();
  345. /// <summary>
  346. /// Event that gets fired when interaction with a rotation handle stops.
  347. /// </summary>
  348. public UnityEvent RotateStopped = new UnityEvent();
  349. /// <summary>
  350. /// Event that gets fired when interaction with a scale handle starts.
  351. /// </summary>
  352. public UnityEvent ScaleStarted = new UnityEvent();
  353. /// <summary>
  354. /// Event that gets fired when interaction with a scale handle stops.
  355. /// </summary>
  356. public UnityEvent ScaleStopped = new UnityEvent();
  357. public UnityEvent Rotating = new UnityEvent();
  358. public UnityEvent Scaling = new UnityEvent();
  359. #region Class & Enum Define
  360. /// <summary>
  361. /// Enum which describes whether a BoundingBox handle which has been grabbed, is
  362. /// a Rotation Handle (sphere) or a Scale Handle( cube)
  363. /// </summary>
  364. [Flags]
  365. public enum HandleType
  366. {
  367. None = 1 << 0,
  368. Rotation = 1 << 1,
  369. Scale = 1 << 2,
  370. AxisScale = 1 << 3,
  371. }
  372. [Flags]
  373. public enum AxisType
  374. {
  375. None = 1 << 0,
  376. X = 1 << 1,
  377. Y = 1 << 2,
  378. Z = 1 << 3,
  379. NX = 1 << 4,
  380. NY = 1 << 5,
  381. NZ = 1 << 6,
  382. }
  383. /// <summary>
  384. /// Enum which describes how an object's BoundingBox is to be flattened.
  385. /// </summary>
  386. public enum FlattenModeType
  387. {
  388. DoNotFlatten = 0,
  389. /// <summary>
  390. /// Flatten the X axis
  391. /// </summary>
  392. FlattenX,
  393. /// <summary>
  394. /// Flatten the Y axis
  395. /// </summary>
  396. FlattenY,
  397. /// <summary>
  398. /// Flatten the Z axis
  399. /// </summary>
  400. FlattenZ,
  401. /// <summary>
  402. /// Flatten the smallest relative axis if it falls below threshold
  403. /// </summary>
  404. FlattenAuto,
  405. }
  406. /// <summary>
  407. /// This enum defines how the BoundingBox gets activated
  408. /// </summary>
  409. public enum BoundingBoxActivationType
  410. {
  411. ActivateOnStart = 0,
  412. ActivateByPointer,
  413. }
  414. public class Handle
  415. {
  416. /// <summary>
  417. /// Handle Type
  418. /// </summary>
  419. public HandleType type;
  420. /// <summary>
  421. /// Handle Root Gameobject
  422. /// </summary>
  423. public Transform root;
  424. /// <summary>
  425. /// Handle bounds
  426. /// </summary>
  427. public Bounds bounds;
  428. public Vector3 localPosition;
  429. public Transform visualsScale;
  430. public GameObject visual;
  431. public void SetActive(bool active)
  432. {
  433. root.gameObject.SetActive(active);
  434. }
  435. }
  436. #endregion
  437. #region Unity Lifecycle Function
  438. // Start is called before the first frame update
  439. void Start()
  440. {
  441. Init();
  442. }
  443. private void OnValidate()
  444. {
  445. Redraw();
  446. }
  447. #endregion
  448. private void Init()
  449. {
  450. CreatBoundingBoxRoot(flattenAxis);
  451. BoundingBoxRoot = new BoundingBoxRoot(this);
  452. CornerBoundingBoxRoot = new CornerBoundingBoxRoot(this);
  453. SideBoundingBoxRoot = new SideBoundingBoxRoot(this);
  454. FaceBoundingBoxRoot = new FaceBoundingBoxRoot(this);
  455. BoundingBoxRoot.Init();
  456. CornerBoundingBoxRoot.Init();
  457. SideBoundingBoxRoot.Init();
  458. FaceBoundingBoxRoot.Init();
  459. if (BoundingBoxRootList == null)
  460. {
  461. BoundingBoxRootList = new List<IBoundingBoxRoot>();
  462. BoundingBoxRootList.Add(BoundingBoxRoot);
  463. BoundingBoxRootList.Add(CornerBoundingBoxRoot);
  464. BoundingBoxRootList.Add(SideBoundingBoxRoot);
  465. BoundingBoxRootList.Add(FaceBoundingBoxRoot);
  466. }
  467. if (ActivationType == BoundingBoxActivationType.ActivateOnStart)
  468. {
  469. SetVisibility(true);
  470. }
  471. else
  472. {
  473. SetVisibility(false);
  474. }
  475. }
  476. private void CreatBoundingBoxRoot(FlattenModeType flattenMode)
  477. {
  478. RecaculateBounds();
  479. }
  480. public void Redraw()
  481. {
  482. if (BoundingBoxRootList == null)
  483. {
  484. return;
  485. }
  486. RecaculateBounds();
  487. for (int i = 0; i < BoundingBoxRootList.Count; i++)
  488. {
  489. IBoundingBoxRoot boundingBoxRoot = BoundingBoxRootList[i];
  490. boundingBoxRoot.ReDraw();
  491. }
  492. }
  493. private void RecaculateBounds()
  494. {
  495. // Make sure that the bounds of all child objects are up to date before we compute bounds
  496. Physics.SyncTransforms();
  497. BoundBoxCollider = GetComponentInChildren<BoxCollider>();
  498. if (BoundBoxCollider == null)
  499. {
  500. Debug.Log(this.gameObject.name+": Error! Please Add BoxCollider And Adjust Size For BoundingBoxGameobject");
  501. return;
  502. }
  503. // Store current rotation then zero out the rotation so that the bounds
  504. // are computed when the object is in its 'axis aligned orientation'.
  505. Quaternion currentRotation = transform.rotation;
  506. transform.rotation = Quaternion.identity;
  507. Physics.SyncTransforms(); // Update collider bounds
  508. currentBoundsExtents = BoundBoxCollider.bounds.extents;
  509. boundingBoxInitScale = BoundBoxCollider.transform.lossyScale;
  510. // After bounds are computed, restore rotation...
  511. transform.rotation = currentRotation;
  512. Physics.SyncTransforms();
  513. if (currentBoundsExtents != Vector3.zero)
  514. {
  515. if (FlattenAxis == FlattenModeType.FlattenAuto)
  516. {
  517. float min = Mathf.Min(currentBoundsExtents.x, Mathf.Min(currentBoundsExtents.y, currentBoundsExtents.z));
  518. flattenAxis = (min == currentBoundsExtents.x) ? FlattenModeType.FlattenX :
  519. ((min == currentBoundsExtents.y) ? FlattenModeType.FlattenY : FlattenModeType.FlattenZ);
  520. }
  521. currentBoundsExtents.x = (flattenAxis == FlattenModeType.FlattenX) ? 0.0f : currentBoundsExtents.x;
  522. currentBoundsExtents.y = (flattenAxis == FlattenModeType.FlattenY) ? 0.0f : currentBoundsExtents.y;
  523. currentBoundsExtents.z = (flattenAxis == FlattenModeType.FlattenZ) ? 0.0f : currentBoundsExtents.z;
  524. Transform existContainerTransform = this.transform.Find(GetType().ToString());
  525. if (existContainerTransform != null)
  526. {
  527. #if UNITY_EDITOR
  528. GameObject.Destroy(existContainerTransform.gameObject);
  529. #else
  530. GameObject.DestroyImmediate(existContainerTransform.gameObject);
  531. #endif
  532. }
  533. BoundingBoxContainer = new GameObject(GetType().ToString()).transform;
  534. BoundingBoxContainer.parent = transform;
  535. BoundingBoxContainer.position = BoundBoxCollider.bounds.center;
  536. BoundingBoxContainer.localRotation = Quaternion.identity;
  537. }
  538. }
  539. public void SetVisibility(bool isVisible)
  540. {
  541. for (int i = 0; i < BoundingBoxRootList.Count; i++)
  542. {
  543. IBoundingBoxRoot boundingBoxRoot = BoundingBoxRootList[i];
  544. boundingBoxRoot.SetVisible(isVisible);
  545. }
  546. }
  547. public void SetHighLight(Transform activeHandle, bool hideOtherHandle)
  548. {
  549. if (BoundingBoxRootList != null)
  550. {
  551. for (int i = 0; i < BoundingBoxRootList.Count; i++)
  552. {
  553. IBoundingBoxRoot boundingBoxRoot = BoundingBoxRootList[i];
  554. boundingBoxRoot.SetHighLight(activeHandle, hideOtherHandle);
  555. }
  556. }
  557. }
  558. #region BoundingBox PointerEvent
  559. public void OnPointerEnter(PointerEventData eventData)
  560. {
  561. SCPointEventData scPointEventData = eventData as SCPointEventData;
  562. if (scPointEventData == null)
  563. {
  564. return;
  565. }
  566. if (scPointEventData.DownPressGameObject == null)
  567. {
  568. SetVisibility(true);
  569. }
  570. }
  571. public void OnPointerExit(PointerEventData eventData)
  572. {
  573. SCPointEventData scPointEventData = eventData as SCPointEventData;
  574. if (scPointEventData == null)
  575. {
  576. return;
  577. }
  578. if (scPointEventData.DownPressGameObject == null)
  579. {
  580. if (activation == BoundingBoxActivationType.ActivateOnStart)
  581. {
  582. SetVisibility(true);
  583. }
  584. else
  585. {
  586. SetVisibility(false);
  587. }
  588. }
  589. }
  590. public void OnPointerDown(PointerEventData eventData)
  591. {
  592. SCPointEventData scPointEventData = eventData as SCPointEventData;
  593. if (scPointEventData == null)
  594. {
  595. return;
  596. }
  597. SetHighLight(eventData.pointerCurrentRaycast.gameObject.transform, true);
  598. }
  599. public void OnPointerUp(PointerEventData eventData)
  600. {
  601. SCPointEventData scPointEventData = eventData as SCPointEventData;
  602. if (scPointEventData == null)
  603. {
  604. return;
  605. }
  606. if (activation == BoundingBoxActivationType.ActivateOnStart)
  607. {
  608. SetVisibility(true);
  609. }
  610. else
  611. {
  612. SetVisibility(true);
  613. }
  614. }
  615. #endregion
  616. }