BBox3DHandler.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. #if ZED_HDRP || ZED_URP
  6. using UnityEngine.Rendering;
  7. #endif
  8. /// <summary>
  9. /// For the ZED 3D Object Detection sample. Handles the cube objects that are moved and resized
  10. /// to represent objects detected in 3D.
  11. /// This was designed specifically for the 3D Bounding Box prefab, and expects it to use the default
  12. /// Unity cube model, the TopBottomBBoxMat, and a label object that floats adjacently.
  13. /// </summary>
  14. public class BBox3DHandler : MonoBehaviour
  15. {
  16. /// <summary>
  17. /// Root transform of the child object that's attached nearby and holds information on the object's ID and distance.
  18. /// </summary>
  19. [Header("Label")]
  20. public Transform labelRoot;
  21. /// <summary>
  22. /// Text component that displays the object's ID and distance from the camera.
  23. /// </summary>
  24. [Tooltip("Text component that displays the object's ID and distance from the camera. ")]
  25. public Text text2D;
  26. /// <summary>
  27. /// Background image on the label.
  28. /// </summary>
  29. [Tooltip("Background image on the label.")]
  30. public Image backgroundImage;
  31. /// <summary>
  32. /// Outline component around the background image on the label. Should reference the same object as backgroundImage.
  33. /// </summary>
  34. [Tooltip("Outline component around the background image on the label. Should reference the same object as backgroundImage.")]
  35. public Outline backgroundOutline;
  36. /// <summary>
  37. /// The Y difference between the center of the label and the top of the bounding box.
  38. /// This is used to keep the box at a consistent position, even as the box itself changes in scale.
  39. /// </summary>
  40. [Tooltip("The Y difference between the center of the label and the top of the bounding box. " +
  41. "This is used to keep the box at a consistent position, even as the box itself changes in scale.")]
  42. public float heightFromBoxCeiling = -0.05f;
  43. /// <summary>
  44. /// If true, the text2D's color will be changed when you call SetColor().
  45. /// </summary>
  46. [Space(2)]
  47. [Tooltip("If true, the text2D's color will be changed when you call SetColor().")]
  48. public bool applyColorToText2D = true;
  49. /// <summary>
  50. /// If true, the backgroundImage's color will be changed when you call SetColor().
  51. /// </summary>
  52. [Tooltip("If true, the backgroundImage's color will be changed when you call SetColor().")]
  53. public bool applyColorToBackgroundImage = false;
  54. /// <summary>
  55. /// If true, the backgroundOutline's color will be changed when you call SetColor().
  56. /// </summary>
  57. [Tooltip("If true, the backgroundOutline's color will be changed when you call SetColor().")]
  58. public bool applyColorToBackgroundOutline = true;
  59. /// <summary>
  60. /// If true, the object's ID will be displayed in text2D, assuming it's been updated.
  61. /// </summary>
  62. [Space(5)]
  63. [Tooltip("If true, the object's ID will be displayed in text2D, assuming it's been updated.")]
  64. public bool showID = true;
  65. /// <summary>
  66. /// If true, the object's distance from the detecting camera will be diplayed in text2D, assuming it's been updated.
  67. /// </summary>
  68. [Tooltip("If true, the object's distance from the detecting camera will be diplayed in text2D, assuming it's been updated.")]
  69. public bool showDistance = true;
  70. /// <summary>
  71. /// If true, the label will increase size when further than maxDistBeforeScaling from each rendering camera so it's never too small.
  72. /// </summary>
  73. [Space(5)]
  74. [Tooltip("If true, the label will increase size when further than maxDistBeforeScaling from each rendering camera so it's never too small.")]
  75. public bool useLabelMaxDistScaling = true;
  76. /// <summary>
  77. /// If useLabelMaxDistScaling is true, this defines how far the label must be from a rendering camera to scale up.
  78. /// </summary>
  79. [Tooltip("If useLabelMaxDistScaling is true, this defines how far the label must be from a rendering camera to scale up.")]
  80. public float maxDistBeforeScaling = 12f;
  81. /// <summary>
  82. /// Cache for the label's calculated scale. If useLabelMaxDistScaling is enabled, this holds the scale until Camera.onPreRender where the scale is applied.
  83. /// </summary>
  84. private Vector3 thisFrameScale;
  85. /// <summary>
  86. /// Lossy (world) scale of the label on start. Used to offset changes in the parent bounding box transform each frame.
  87. /// </summary>
  88. private Vector3 labelStartLossyScale;
  89. /// <summary>
  90. /// ID of the object that this instance is currently representing.
  91. /// </summary>
  92. private string currentID = "";
  93. /// <summary>
  94. /// Distance from this object to the ZED camera that detected it.
  95. /// </summary>
  96. private float currentDistance = -1f;
  97. /// <summary>
  98. /// MeshRenderer attached to this bounding box.
  99. /// </summary>
  100. private MeshRenderer rend;
  101. #if ZED_HDRP
  102. /// <summary>
  103. /// If using HDRP, you can't move or modify objects on a per-camera basis: all "beginCameraRendering" behavior appears to run before the first camera starts rendering,
  104. /// so the object state during the last camera to render is how it appears in all cameras.
  105. /// As such, we just pick one single camera (by default the left camera of the first ZEDManager we find) and make labels face that one.
  106. /// </summary>
  107. private Camera labelFacingCamera;
  108. #endif
  109. #region Shader ID caches
  110. //We look up the IDs of shader properties once, to save a lookup (and performance) each time we access them.
  111. private static int boxBGColorIndex;
  112. private static int boxTexColorIndex;
  113. private static int edgeColorIndex;
  114. private static int xScaleIndex;
  115. private static int yScaleIndex;
  116. private static int zScaleIndex;
  117. private static int floorHeightIndex;
  118. private static bool shaderIndexIDsSet = false;
  119. #endregion
  120. // Use this for initialization
  121. void Awake ()
  122. {
  123. if (!text2D) text2D = labelRoot.GetComponentInChildren<Text>();
  124. thisFrameScale = labelRoot.localScale;
  125. labelStartLossyScale = labelRoot.lossyScale;
  126. if(!shaderIndexIDsSet)
  127. {
  128. FindShaderIndexes();
  129. }
  130. #if !ZED_HDRP && !ZED_URP
  131. Camera.onPreCull += OnCameraPreRender;
  132. #elif ZED_URP
  133. RenderPipelineManager.beginCameraRendering += URPBeginCamera;
  134. #elif ZED_HDRP
  135. ZEDManager manager = FindObjectOfType<ZEDManager>();
  136. labelFacingCamera = manager.GetLeftCamera();
  137. RenderPipelineManager.beginFrameRendering += HDRPBeginFrame;
  138. #endif
  139. }
  140. private void Update()
  141. {
  142. //Position the label so that it stays at a consistent place relative to the box's scale.
  143. UpdateLabelScaleAndPosition();
  144. UpdateBoxUVScales();
  145. }
  146. /// <summary>
  147. /// Sets the ID value that will be displayed on the box's label.
  148. /// Usually set when the box first starts representing a detected object.
  149. /// </summary>
  150. public void SetID(string id)
  151. {
  152. currentID = id;
  153. UpdateText(currentID, currentDistance);
  154. }
  155. /// <summary>
  156. /// Sets the distance value that will be displayed on the box's label.
  157. /// Designed to indicate the distance from the camera that saw the object.
  158. /// Value is expected in meters. Should be updated with each new detection.
  159. /// </summary>
  160. public void SetDistance(float dist)
  161. {
  162. currentDistance = dist;
  163. UpdateText(currentID, currentDistance);
  164. }
  165. /// <summary>
  166. /// Sets the color of the box and label elements.
  167. /// Use this to alternate between several colors if you have multiple detected objects, and
  168. /// keep them distinguished from one another. Or use to easily customize the visuals however you'd like.
  169. /// </summary>
  170. public void SetColor(Color col)
  171. {
  172. if (text2D && applyColorToText2D)
  173. {
  174. text2D.color = new Color(col.r, col.g, col.b, text2D.color.a);
  175. }
  176. if (backgroundImage && applyColorToBackgroundImage)
  177. {
  178. backgroundImage.color = new Color(col.r, col.g, col.b, backgroundImage.color.a);
  179. }
  180. if (backgroundOutline && applyColorToBackgroundOutline)
  181. {
  182. backgroundOutline.effectColor = new Color(col.r, col.g, col.b, backgroundOutline.effectColor.a);
  183. }
  184. ApplyColorToBoxMats(col);
  185. }
  186. /// <summary>
  187. /// Tells the TopBottomBBoxMat material attached to the box what the transform's current scale is,
  188. /// so that the UVs can be scaled appropriately and avoid stretching.
  189. /// </summary>
  190. public void UpdateBoxUVScales() //TODO: Cache shader IDs.
  191. {
  192. MeshRenderer rend = GetComponentInChildren<MeshRenderer>();
  193. if (rend)
  194. {
  195. foreach (Material mat in rend.materials)
  196. {
  197. if (mat.HasProperty(xScaleIndex))
  198. {
  199. mat.SetFloat(xScaleIndex, transform.lossyScale.x);
  200. }
  201. if (mat.HasProperty(yScaleIndex))
  202. {
  203. mat.SetFloat(yScaleIndex, transform.lossyScale.y);
  204. }
  205. if (mat.HasProperty(zScaleIndex))
  206. {
  207. mat.SetFloat(zScaleIndex, transform.lossyScale.z);
  208. }
  209. if (mat.HasProperty(floorHeightIndex))
  210. {
  211. float height = transform.position.y - transform.lossyScale.y / 2f;
  212. mat.SetFloat(floorHeightIndex, height);
  213. }
  214. }
  215. }
  216. }
  217. /// <summary>
  218. /// Adjusts the label's scale and position to compensate for any changes in the bounding box's scale.
  219. /// </summary>
  220. public void UpdateLabelScaleAndPosition()
  221. {
  222. float lossyxdif = labelStartLossyScale.x / labelRoot.lossyScale.x;
  223. float lossyydif = labelStartLossyScale.y / labelRoot.lossyScale.y;
  224. float lossyzdif = labelStartLossyScale.z / labelRoot.lossyScale.z;
  225. thisFrameScale = new Vector3(labelRoot.localScale.x * lossyxdif,
  226. labelRoot.localScale.y * lossyydif,
  227. labelRoot.localScale.z * lossyzdif);
  228. labelRoot.localPosition = new Vector3(labelRoot.localPosition.x, 0.5f + heightFromBoxCeiling / transform.localScale.y, labelRoot.localPosition.z);
  229. if(!useLabelMaxDistScaling) //If we're using this, we don't apply the scale until the OnPreRender event to add additional scaling effects.
  230. {
  231. labelRoot.localScale = thisFrameScale;
  232. }
  233. }
  234. /// <summary>
  235. /// Updates the text in the label based on the given ID and distance values.
  236. /// </summary>
  237. private void UpdateText(string id, float dist)
  238. {
  239. string newtext = "";
  240. if (showID) newtext += "ID: " + id.ToString();
  241. if (showID && showDistance) newtext += "\r\n";
  242. if (showDistance) newtext += dist.ToString("F2") + "m";
  243. if (text2D) text2D.text = newtext;
  244. }
  245. /// <summary>
  246. /// Updates the colors of the 3D box materials to the given color.
  247. /// </summary>
  248. private void ApplyColorToBoxMats(Color col)
  249. {
  250. if (!rend) rend = GetComponent<MeshRenderer>();
  251. Material[] mats = rend.materials;
  252. if(!shaderIndexIDsSet)
  253. {
  254. FindShaderIndexes();
  255. }
  256. for (int m = 0; m < mats.Length; m++)
  257. {
  258. Material mat = new Material(rend.materials[m]);
  259. if (mat.HasProperty(boxTexColorIndex))
  260. {
  261. float texalpha = mat.GetColor(boxTexColorIndex).a;
  262. mat.SetColor(boxTexColorIndex, new Color(col.r, col.g, col.b, texalpha));
  263. }
  264. if (mat.HasProperty(boxBGColorIndex))
  265. {
  266. float bgalpha = mat.GetColor(boxBGColorIndex).a;
  267. mat.SetColor(boxBGColorIndex, new Color(col.r, col.g, col.b, bgalpha));
  268. }
  269. if (mat.HasProperty(edgeColorIndex))
  270. {
  271. float bgalpha = mat.GetColor(edgeColorIndex).a;
  272. mat.SetColor(edgeColorIndex, new Color(col.r, col.g, col.b, bgalpha));
  273. }
  274. mats[m] = mat;
  275. }
  276. rend.materials = mats;
  277. }
  278. #if ZED_URP
  279. private void URPBeginCamera(ScriptableRenderContext context, Camera rendcam)
  280. {
  281. OnCameraPreRender(rendcam);
  282. }
  283. #elif ZED_HDRP
  284. private void HDRPBeginFrame(ScriptableRenderContext context, Camera[] rendcams)
  285. {
  286. OnCameraPreRender(labelFacingCamera);
  287. }
  288. #endif
  289. private void OnCameraPreRender(Camera cam)
  290. {
  291. if (!useLabelMaxDistScaling) return;
  292. if (cam.name.Contains("Scene")) return;
  293. if (float.IsInfinity(thisFrameScale.x) || float.IsInfinity(thisFrameScale.y) || float.IsInfinity(thisFrameScale.z))
  294. {
  295. return;
  296. }
  297. //float dist = Vector3.Distance(cam.transform.position, labelRoot.transform.position);
  298. float depth = cam.WorldToScreenPoint(labelRoot.transform.position).z;
  299. if (depth > maxDistBeforeScaling)
  300. {
  301. labelRoot.localScale = thisFrameScale * (depth / maxDistBeforeScaling);
  302. }
  303. else
  304. {
  305. labelRoot.localScale = thisFrameScale;
  306. }
  307. }
  308. /// <summary>
  309. /// Finds and sets the static shader indexes for the properties that we'll set.
  310. /// Used so we can call those indexes when we set the properties, which avoids a lookup
  311. /// and increases performance.
  312. /// </summary>
  313. private static void FindShaderIndexes()
  314. {
  315. boxBGColorIndex = Shader.PropertyToID("_BGColor");
  316. boxTexColorIndex = Shader.PropertyToID("_Color");
  317. edgeColorIndex = Shader.PropertyToID("_EdgeColor");
  318. xScaleIndex = Shader.PropertyToID("_XScale");
  319. yScaleIndex = Shader.PropertyToID("_YScale");
  320. zScaleIndex = Shader.PropertyToID("_ZScale");
  321. floorHeightIndex = Shader.PropertyToID("_FloorHeight");
  322. shaderIndexIDsSet = true;
  323. }
  324. private void OnDestroy()
  325. {
  326. #if !ZED_HDRP && !ZED_URP
  327. Camera.onPreCull -= OnCameraPreRender;
  328. #elif ZED_URP
  329. RenderPipelineManager.beginCameraRendering -= URPBeginCamera;
  330. #elif ZED_HDRP
  331. RenderPipelineManager.beginFrameRendering -= HDRPBeginFrame;
  332. #endif
  333. }
  334. }