BoundingBox.cs 15 KB

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