Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

293 lines
13 KiB

  1. // Outline Object Copy|Highlighters|40030
  2. namespace VRTK.Highlighters
  3. {
  4. using UnityEngine;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. /// <summary>
  9. /// Creates a mesh copy and applies an outline shader which is toggled on and off when highlighting the object.
  10. /// </summary>
  11. /// <remarks>
  12. /// > A valid mesh must be found or provided for the clone mesh to be created.
  13. ///
  14. /// **Script Usage:**
  15. /// * Place the `VRTK_OutlineObjectCopyHighlighter` script on either:
  16. /// * The GameObject of the Interactable Object to highlight.
  17. /// * Any other scene GameObject and then link that GameObject to the Interactable Objects `Object Highlighter` parameter to denote use of the highlighter.
  18. /// * Ensure the `Active` parameter is checked.
  19. /// </remarks>
  20. /// <example>
  21. /// `VRTK/Examples/005_Controller_BasicObjectGrabbing` demonstrates the outline highlighting on the green sphere when the controller touches it.
  22. ///
  23. /// `VRTK/Examples/035_Controller_OpacityAndHighlighting` demonstrates the outline highlighting if the left controller collides with the green box.
  24. /// </example>
  25. [AddComponentMenu("VRTK/Scripts/Interactions/Highlighters/VRTK_OutlineObjectCopyHighlighter")]
  26. public class VRTK_OutlineObjectCopyHighlighter : VRTK_BaseHighlighter
  27. {
  28. [Tooltip("The thickness of the outline effect")]
  29. public float thickness = 1f;
  30. [Tooltip("The GameObjects to use as the model to outline. If one isn't provided then the first GameObject with a valid Renderer in the current GameObject hierarchy will be used.")]
  31. public GameObject[] customOutlineModels;
  32. [Tooltip("A path to a GameObject to find at runtime, if the GameObject doesn't exist at edit time.")]
  33. public string[] customOutlineModelPaths;
  34. [Tooltip("If the mesh has multiple sub-meshes to highlight then this should be checked, otherwise only the first mesh will be highlighted.")]
  35. public bool enableSubmeshHighlight = false;
  36. protected Material stencilOutline;
  37. protected Renderer[] highlightModels;
  38. protected string[] copyComponents = new string[] { "UnityEngine.MeshFilter", "UnityEngine.MeshRenderer" };
  39. /// <summary>
  40. /// The Initialise method sets up the highlighter for use.
  41. /// </summary>
  42. /// <param name="color">Not used.</param>
  43. /// <param name="affectObject">An optional GameObject to specify which object to apply the highlighting to.</param>
  44. /// <param name="options">A dictionary array containing the highlighter options:\r * `&lt;'thickness', float&gt;` - Same as `thickness` inspector parameter.\r * `&lt;'customOutlineModels', GameObject[]&gt;` - Same as `customOutlineModels` inspector parameter.\r * `&lt;'customOutlineModelPaths', string[]&gt;` - Same as `customOutlineModelPaths` inspector parameter.</param>
  45. public override void Initialise(Color? color = null, GameObject affectObject = null, Dictionary<string, object> options = null)
  46. {
  47. objectToAffect = (affectObject != null ? affectObject : gameObject);
  48. usesClonedObject = true;
  49. if (stencilOutline == null)
  50. {
  51. stencilOutline = Instantiate((Material)Resources.Load("OutlineBasic"));
  52. }
  53. SetOptions(options);
  54. ResetHighlighter();
  55. }
  56. /// <summary>
  57. /// The ResetHighlighter method creates the additional model to use as the outline highlighted object.
  58. /// </summary>
  59. public override void ResetHighlighter()
  60. {
  61. DeleteExistingHighlightModels();
  62. //First try and use the paths if they have been set
  63. ResetHighlighterWithCustomModelPaths();
  64. //If the custom models have been set then use these to override any set paths.
  65. ResetHighlighterWithCustomModels();
  66. //if no highlights set then try falling back
  67. ResetHighlightersWithCurrentGameObject();
  68. }
  69. /// <summary>
  70. /// The Highlight method initiates the outline object to be enabled and display the outline colour.
  71. /// </summary>
  72. /// <param name="color">The colour to outline with.</param>
  73. /// <param name="duration">Not used.</param>
  74. public override void Highlight(Color? color, float duration = 0f)
  75. {
  76. if (highlightModels != null && highlightModels.Length > 0 && stencilOutline != null)
  77. {
  78. stencilOutline.SetFloat("_Thickness", thickness);
  79. stencilOutline.SetColor("_OutlineColor", (Color)color);
  80. for (int i = 0; i < highlightModels.Length; i++)
  81. {
  82. if (highlightModels[i] != null)
  83. {
  84. highlightModels[i].gameObject.SetActive(true);
  85. highlightModels[i].material = stencilOutline;
  86. }
  87. }
  88. }
  89. }
  90. /// <summary>
  91. /// The Unhighlight method hides the outline object and removes the outline colour.
  92. /// </summary>
  93. /// <param name="color">Not used.</param>
  94. /// <param name="duration">Not used.</param>
  95. public override void Unhighlight(Color? color = null, float duration = 0f)
  96. {
  97. if (objectToAffect == null)
  98. {
  99. return;
  100. }
  101. if (highlightModels != null)
  102. {
  103. for (int i = 0; i < highlightModels.Length; i++)
  104. {
  105. if (highlightModels[i] != null)
  106. {
  107. highlightModels[i].gameObject.SetActive(false);
  108. }
  109. }
  110. }
  111. }
  112. protected virtual void OnEnable()
  113. {
  114. if (customOutlineModels == null)
  115. {
  116. customOutlineModels = new GameObject[0];
  117. }
  118. if (customOutlineModelPaths == null)
  119. {
  120. customOutlineModelPaths = new string[0];
  121. }
  122. }
  123. protected virtual void OnDestroy()
  124. {
  125. if (highlightModels != null)
  126. {
  127. for (int i = 0; i < highlightModels.Length; i++)
  128. {
  129. if (highlightModels[i] != null)
  130. {
  131. Destroy(highlightModels[i]);
  132. }
  133. }
  134. }
  135. Destroy(stencilOutline);
  136. }
  137. protected virtual void ResetHighlighterWithCustomModels()
  138. {
  139. if (customOutlineModels != null && customOutlineModels.Length > 0)
  140. {
  141. highlightModels = new Renderer[customOutlineModels.Length];
  142. for (int i = 0; i < customOutlineModels.Length; i++)
  143. {
  144. highlightModels[i] = CreateHighlightModel(customOutlineModels[i], "");
  145. }
  146. }
  147. }
  148. protected virtual void ResetHighlighterWithCustomModelPaths()
  149. {
  150. if (customOutlineModelPaths != null && customOutlineModelPaths.Length > 0)
  151. {
  152. highlightModels = new Renderer[customOutlineModelPaths.Length];
  153. for (int i = 0; i < customOutlineModelPaths.Length; i++)
  154. {
  155. highlightModels[i] = CreateHighlightModel(null, customOutlineModelPaths[i]);
  156. }
  157. }
  158. }
  159. protected virtual void ResetHighlightersWithCurrentGameObject()
  160. {
  161. if (highlightModels == null || highlightModels.Length == 0)
  162. {
  163. highlightModels = new Renderer[1];
  164. highlightModels[0] = CreateHighlightModel(null, "");
  165. }
  166. }
  167. protected virtual void SetOptions(Dictionary<string, object> options = null)
  168. {
  169. float tmpThickness = GetOption<float>(options, "thickness");
  170. if (tmpThickness > 0f)
  171. {
  172. thickness = tmpThickness;
  173. }
  174. GameObject[] tmpCustomModels = GetOption<GameObject[]>(options, "customOutlineModels");
  175. if (tmpCustomModels != null)
  176. {
  177. customOutlineModels = tmpCustomModels;
  178. }
  179. string[] tmpCustomModelPaths = GetOption<string[]>(options, "customOutlineModelPaths");
  180. if (tmpCustomModelPaths != null)
  181. {
  182. customOutlineModelPaths = tmpCustomModelPaths;
  183. }
  184. }
  185. protected virtual void DeleteExistingHighlightModels()
  186. {
  187. VRTK_PlayerObject[] existingHighlighterObjects = objectToAffect.GetComponentsInChildren<VRTK_PlayerObject>(true);
  188. for (int i = 0; i < existingHighlighterObjects.Length; i++)
  189. {
  190. if (existingHighlighterObjects[i].objectType == VRTK_PlayerObject.ObjectTypes.Highlighter)
  191. {
  192. Destroy(existingHighlighterObjects[i].gameObject);
  193. }
  194. }
  195. highlightModels = new Renderer[0];
  196. }
  197. protected virtual Renderer CreateHighlightModel(GameObject givenOutlineModel, string givenOutlineModelPath)
  198. {
  199. if (givenOutlineModel != null)
  200. {
  201. givenOutlineModel = (givenOutlineModel.GetComponent<Renderer>() ? givenOutlineModel : givenOutlineModel.GetComponentInChildren<Renderer>().gameObject);
  202. }
  203. else if (givenOutlineModelPath != "")
  204. {
  205. Transform getChildModel = objectToAffect.transform.Find(givenOutlineModelPath);
  206. givenOutlineModel = (getChildModel ? getChildModel.gameObject : null);
  207. }
  208. GameObject copyModel = givenOutlineModel;
  209. if (copyModel == null)
  210. {
  211. Renderer copyModelRenderer = objectToAffect.GetComponentInChildren<Renderer>();
  212. copyModel = (copyModelRenderer != null ? copyModelRenderer.gameObject : null);
  213. }
  214. if (copyModel == null)
  215. {
  216. VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "VRTK_OutlineObjectCopyHighlighter", "Renderer", "the same or child", " to add the highlighter to"));
  217. return null;
  218. }
  219. GameObject highlightModel = new GameObject(objectToAffect.name + "_HighlightModel");
  220. highlightModel.transform.SetParent(copyModel.transform.parent, false);
  221. highlightModel.transform.localPosition = copyModel.transform.localPosition;
  222. highlightModel.transform.localRotation = copyModel.transform.localRotation;
  223. highlightModel.transform.localScale = copyModel.transform.localScale;
  224. highlightModel.transform.SetParent(objectToAffect.transform);
  225. Component[] copyModelComponents = copyModel.GetComponents<Component>();
  226. for (int i = 0; i < copyModelComponents.Length; i++)
  227. {
  228. Component copyModelComponent = copyModelComponents[i];
  229. if (Array.IndexOf(copyComponents, copyModelComponent.GetType().ToString()) >= 0)
  230. {
  231. VRTK_SharedMethods.CloneComponent(copyModelComponent, highlightModel);
  232. }
  233. }
  234. MeshFilter copyMesh = copyModel.GetComponent<MeshFilter>();
  235. MeshFilter highlightMesh = highlightModel.GetComponent<MeshFilter>();
  236. Renderer returnHighlightModel = highlightModel.GetComponent<Renderer>();
  237. if (highlightMesh != null)
  238. {
  239. if (enableSubmeshHighlight)
  240. {
  241. HashSet<CombineInstance> combine = new HashSet<CombineInstance>();
  242. for (int i = 0; i < copyMesh.mesh.subMeshCount; i++)
  243. {
  244. CombineInstance ci = new CombineInstance();
  245. ci.mesh = copyMesh.mesh;
  246. ci.subMeshIndex = i;
  247. ci.transform = copyMesh.transform.localToWorldMatrix;
  248. combine.Add(ci);
  249. }
  250. highlightMesh.mesh = new Mesh();
  251. highlightMesh.mesh.CombineMeshes(combine.ToArray(), true, false);
  252. }
  253. else
  254. {
  255. highlightMesh.mesh = copyMesh.mesh;
  256. }
  257. returnHighlightModel.material = stencilOutline;
  258. returnHighlightModel.shadowCastingMode = copyModel.transform.GetComponent<Renderer>().shadowCastingMode;
  259. }
  260. highlightModel.SetActive(false);
  261. VRTK_PlayerObject.SetPlayerObject(highlightModel, VRTK_PlayerObject.ObjectTypes.Highlighter);
  262. return returnHighlightModel;
  263. }
  264. }
  265. }