HighlightEffect.cs 137 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945
  1. /// <summary>
  2. /// Highlight Plus - (c) Kronnect Technologies SL
  3. /// </summary>
  4. using System;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. using UnityEngine.Rendering;
  8. namespace HighlightPlus {
  9. public delegate bool OnObjectHighlightEvent(GameObject obj);
  10. public delegate bool OnRendererHighlightEvent(Renderer renderer);
  11. /// <summary>
  12. /// Triggers when target effect animation occurs
  13. /// </summary>
  14. /// <param name="t">A value from 0 to 1 that represent the animation time from start to end, based on target duration and start time</param>
  15. public delegate void OnTargetAnimatesEvent(ref Vector3 center, ref Quaternion rotation, ref Vector3 scale, float t);
  16. public delegate void FullScreenBlitMethod(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex);
  17. public enum NormalsOption {
  18. Smooth = 0,
  19. PreserveOriginal = 1,
  20. Reorient = 2
  21. }
  22. public enum SeeThroughMode {
  23. WhenHighlighted = 0,
  24. AlwaysWhenOccluded = 1,
  25. Never = 2
  26. }
  27. public enum SeeThroughSortingMode {
  28. Default = 0,
  29. SortByMaterialsRenderQueue = 1,
  30. SortByMaterialsRenderQueueInverted = 2
  31. }
  32. public enum OverlayMode {
  33. WhenHighlighted = 0,
  34. Always = 10
  35. }
  36. public enum TextureUVSpace {
  37. Triplanar = 0,
  38. ObjectSpace = 1,
  39. ScreenSpace = 2
  40. }
  41. public enum QualityLevel {
  42. Fastest = 0,
  43. High = 1,
  44. Highest = 2,
  45. Medium = 3
  46. }
  47. public enum TargetOptions {
  48. Children,
  49. OnlyThisObject,
  50. RootToChildren,
  51. LayerInScene,
  52. LayerInChildren,
  53. Scripting
  54. }
  55. public enum Visibility {
  56. Normal,
  57. AlwaysOnTop,
  58. OnlyWhenOccluded
  59. }
  60. public enum ColorStyle {
  61. SingleColor,
  62. Gradient
  63. }
  64. public enum OutlineEdgeMode {
  65. Exterior,
  66. Any
  67. }
  68. public enum ContourStyle {
  69. AroundVisibleParts,
  70. AroundObjectShape
  71. }
  72. public enum GlowBlendMode {
  73. Additive,
  74. AlphaBlending
  75. }
  76. public enum InnerGlowBlendMode {
  77. Additive,
  78. AlphaBlending
  79. }
  80. public enum BlurMethod {
  81. Gaussian,
  82. Kawase
  83. }
  84. public static class QualityLevelExtensions {
  85. public static bool UsesMultipleOffsets(this QualityLevel qualityLevel) {
  86. return qualityLevel == QualityLevel.Medium || qualityLevel == QualityLevel.High;
  87. }
  88. }
  89. [Serializable]
  90. public struct GlowPassData {
  91. public float offset;
  92. public float alpha;
  93. public Color color;
  94. }
  95. [ExecuteInEditMode]
  96. [HelpURL("https://kronnect.com/guides/highlight-plus-introduction/")]
  97. public partial class HighlightEffect : MonoBehaviour {
  98. /// <summary>
  99. /// Gets or sets the current profile. To load a profile and apply its settings at runtime, please use ProfileLoad() method.
  100. /// </summary>
  101. [Tooltip("The current profile (optional). A profile let you store Highlight Plus settings and apply those settings easily to many objects. You can also load a profile and apply its settings at runtime, using the ProfileLoad() method of the Highlight Effect component.")]
  102. public HighlightProfile profile;
  103. /// <summary>
  104. /// Sets if changes to the original profile should propagate to this effect.
  105. /// </summary>
  106. [Tooltip("If enabled, settings from the profile will be applied to this component automatically when game starts or when any profile setting is updated.")]
  107. public bool profileSync;
  108. /// <summary>
  109. /// Which cameras can render the effects
  110. /// </summary>
  111. [Tooltip("Which cameras can render the effect.")]
  112. public LayerMask camerasLayerMask = -1;
  113. /// <summary>
  114. /// Specifies which objects are affected by this effect.
  115. /// </summary>
  116. [Tooltip("Different options to specify which objects are affected by this Highlight Effect component.")]
  117. public TargetOptions effectGroup = TargetOptions.Children;
  118. /// <summary>
  119. /// The layer that contains the affected objects by this effect when effectGroup is set to LayerMask.
  120. /// </summary>
  121. [Tooltip("The layer that contains the affected objects by this effect when effectGroup is set to LayerMask.")]
  122. public LayerMask effectGroupLayer = -1;
  123. /// <summary>
  124. /// Optional object name filter
  125. /// </summary>
  126. [Tooltip("Only include objects whose names contains this text.")]
  127. public string effectNameFilter;
  128. /// <summary>
  129. /// Combine objects into a single mesh
  130. /// </summary>
  131. [Tooltip("Combine meshes of all objects in this group affected by Highlight Effect reducing draw calls.")]
  132. public bool combineMeshes;
  133. /// <summary>
  134. /// The alpha threshold for transparent cutout objects. Pixels with alpha below this value will be discarded.
  135. /// </summary>
  136. [Tooltip("The alpha threshold for transparent cutout objects. Pixels with alpha below this value will be discarded.")]
  137. [Range(0, 1)]
  138. public float alphaCutOff;
  139. /// <summary>
  140. /// If back facing triangles are ignored. Backfaces triangles are not visible but you may set this property to false to force highlight effects to act on those triangles as well.
  141. /// </summary>
  142. [Tooltip("If back facing triangles are ignored.Backfaces triangles are not visible but you may set this property to false to force highlight effects to act on those triangles as well.")]
  143. public bool cullBackFaces = true;
  144. /// <summary>
  145. /// Show highlight effects even if the object is currently not visible. This option is useful if the affected objects are rendered using GPU instancing tools which render directly to the GPU without creating real game object geometry in CPU.
  146. /// </summary>
  147. [Tooltip("Show highlight effects even if the object is not visible. If this object or its children use GPU Instancing tools, the MeshRenderer can be disabled although the object is visible. In this case, this option is useful to enable highlighting.")]
  148. public bool ignoreObjectVisibility;
  149. /// <summary>
  150. /// Enable to support reflection probes
  151. /// </summary>
  152. [Tooltip("Support reflection probes. Enable only if you want the effects to be visible in reflections.")]
  153. public bool reflectionProbes;
  154. /// <summary>
  155. /// Enable to support reflection probes
  156. /// </summary>
  157. [Tooltip("Enables GPU instancing. Reduces draw calls in outline and outer glow effects on platforms that support GPU instancing. Should be enabled by default.")]
  158. public bool GPUInstancing = true;
  159. /// <summary>
  160. /// Enable to support reflection probes
  161. /// </summary>
  162. [Tooltip("Bakes skinned mesh to leverage GPU instancing when using outline/outer glow with mesh-based rendering. Reduces draw calls significantly on skinned meshes.")]
  163. public bool optimizeSkinnedMesh = true;
  164. /// <summary>
  165. /// Enabled depth buffer flip in HQ
  166. /// </summary>
  167. [Tooltip("Enables depth buffer clipping. Only applies to outline or outer glow in High Quality mode.")]
  168. public bool depthClip;
  169. [Tooltip("Fades out effects based on distance to camera")]
  170. public bool cameraDistanceFade;
  171. [Tooltip("The closest distance particles can get to the camera before they fade from the camera’s view.")]
  172. public float cameraDistanceFadeNear;
  173. [Tooltip("The farthest distance particles can get away from the camera before they fade from the camera’s view.")]
  174. public float cameraDistanceFadeFar = 1000;
  175. [Tooltip("Normals handling option:\nPreserve original: use original mesh normals.\nSmooth: average normals to produce a smoother outline/glow mesh based effect.\nReorient: recomputes normals based on vertex direction to centroid.")]
  176. public NormalsOption normalsOption;
  177. /// <summary>
  178. /// Ignores highlight effects on this object.
  179. /// </summary>
  180. [Tooltip("Ignore highlighting on this object.")]
  181. public bool ignore;
  182. [SerializeField]
  183. bool _highlighted;
  184. public bool highlighted { get { return _highlighted; } set { SetHighlighted(value); } }
  185. public float fadeInDuration;
  186. public float fadeOutDuration;
  187. public bool flipY;
  188. [Tooltip("Keeps the outline/glow size unaffected by object distance.")]
  189. public bool constantWidth = true;
  190. [Tooltip("Mask to include or exclude certain submeshes. By default, all submeshes are included.")]
  191. public int subMeshMask = -1;
  192. [Range(0, 1)]
  193. [Tooltip("Intensity of the overlay effect. A value of 0 disables the overlay completely.")]
  194. public float overlay;
  195. public OverlayMode overlayMode = OverlayMode.WhenHighlighted;
  196. [ColorUsage(true, true)] public Color overlayColor = Color.yellow;
  197. public float overlayAnimationSpeed = 1f;
  198. [Range(0, 1)]
  199. public float overlayMinIntensity = 0.5f;
  200. [Range(0, 1)]
  201. [Tooltip("Controls the blending or mix of the overlay color with the natural colors of the object.")]
  202. public float overlayBlending = 1.0f;
  203. [Tooltip("Optional overlay texture.")]
  204. public Texture2D overlayTexture;
  205. public TextureUVSpace overlayTextureUVSpace;
  206. public float overlayTextureScale = 1f;
  207. public Visibility overlayVisibility = Visibility.Normal;
  208. [Range(0, 1)]
  209. [Tooltip("Intensity of the outline. A value of 0 disables the outline completely.")]
  210. public float outline = 1f;
  211. [ColorUsage(true, true)] public Color outlineColor = Color.black;
  212. public ColorStyle outlineColorStyle = ColorStyle.SingleColor;
  213. [GradientUsage(hdr: true, ColorSpace.Linear)] public Gradient outlineGradient;
  214. public bool outlineGradientInLocalSpace;
  215. public float outlineWidth = 0.45f;
  216. [Range(1, 3)]
  217. public int outlineBlurPasses = 2;
  218. public QualityLevel outlineQuality = QualityLevel.Medium;
  219. public OutlineEdgeMode outlineEdgeMode = OutlineEdgeMode.Exterior;
  220. public float outlineEdgeThreshold = 0.995f;
  221. [Range(1, 8)]
  222. [Tooltip("Reduces the quality of the outline but improves performance a bit.")]
  223. public int outlineDownsampling = 1;
  224. public Visibility outlineVisibility = Visibility.Normal;
  225. public GlowBlendMode glowBlendMode = GlowBlendMode.Additive;
  226. public bool outlineBlitDebug;
  227. [Tooltip("If enabled, this object won't combine the outline with other objects.")]
  228. public bool outlineIndependent;
  229. public ContourStyle outlineContourStyle = ContourStyle.AroundVisibleParts;
  230. [Range(0, 5)]
  231. [Tooltip("The intensity of the outer glow effect. A value of 0 disables the glow completely.")]
  232. public float glow;
  233. public float glowWidth = 0.4f;
  234. public QualityLevel glowQuality = QualityLevel.Medium;
  235. public BlurMethod glowBlurMethod = BlurMethod.Gaussian;
  236. [Range(1, 8)]
  237. [Tooltip("Reduces the quality of the glow but improves performance a bit.")]
  238. public int glowDownsampling = 2;
  239. [ColorUsage(true, true)] public Color glowHQColor = new Color(0.64f, 1f, 0f, 1f);
  240. [Tooltip("When enabled, outer glow renders with dithering. When disabled, glow appears as a solid color.")]
  241. public bool glowDithering = true;
  242. [Tooltip("Seed for the dithering effect")]
  243. public float glowMagicNumber1 = 0.75f;
  244. [Tooltip("Another seed for the dithering effect that combines with first seed to create different patterns")]
  245. public float glowMagicNumber2 = 0.5f;
  246. public float glowAnimationSpeed = 1f;
  247. public Visibility glowVisibility = Visibility.Normal;
  248. public bool glowBlitDebug;
  249. [Tooltip("Blends glow passes one after another. If this option is disabled, glow passes won't overlap (in this case, make sure the glow pass 1 has a smaller offset than pass 2, etc.)")]
  250. public bool glowBlendPasses = true;
  251. #if UNITY_2020_2_OR_NEWER
  252. [NonReorderable]
  253. #endif
  254. public GlowPassData[] glowPasses;
  255. [Tooltip("If enabled, glow effect will not use a stencil mask. This can be used to render the glow effect alone.")]
  256. public bool glowIgnoreMask;
  257. [Range(0, 5f)]
  258. [Tooltip("The intensity of the inner glow effect. A value of 0 disables the glow completely.")]
  259. public float innerGlow;
  260. [Range(0, 2)]
  261. public float innerGlowWidth = 1f;
  262. [ColorUsage(true, true)] public Color innerGlowColor = Color.white;
  263. public InnerGlowBlendMode innerGlowBlendMode = InnerGlowBlendMode.Additive;
  264. public Visibility innerGlowVisibility = Visibility.Normal;
  265. [Tooltip("Enables the targetFX effect. This effect draws an animated sprite over the object.")]
  266. public bool targetFX;
  267. public Texture2D targetFXTexture;
  268. [ColorUsage(true, true)] public Color targetFXColor = Color.white;
  269. public Transform targetFXCenter;
  270. public float targetFXRotationSpeed = 50f;
  271. public float targetFXInitialScale = 4f;
  272. public float targetFXEndScale = 1.5f;
  273. [Tooltip("Makes target scale relative to object renderer bounds")]
  274. public bool targetFXScaleToRenderBounds = true;
  275. [Tooltip("Places target FX sprite at the bottom of the highlighted object.")]
  276. public bool targetFXAlignToGround;
  277. [Tooltip("Fade out effect with altitude")]
  278. public float targetFXFadePower = 32;
  279. public float targetFXGroundMaxDistance = 10f;
  280. public LayerMask targetFXGroundLayerMask = -1;
  281. public float targetFXTransitionDuration = 0.5f;
  282. [Tooltip("The duration of the effect. A value of 0 will keep the target sprite on screen while object is highlighted.")]
  283. public float targetFXStayDuration = 1.5f;
  284. public Visibility targetFXVisibility = Visibility.AlwaysOnTop;
  285. public event OnObjectHighlightEvent OnObjectHighlightStart;
  286. public event OnObjectHighlightEvent OnObjectHighlightEnd;
  287. public event OnRendererHighlightEvent OnRendererHighlightStart;
  288. public event OnTargetAnimatesEvent OnTargetAnimates;
  289. [Tooltip("See-through mode for this Highlight Effect component.")]
  290. public SeeThroughMode seeThrough = SeeThroughMode.Never;
  291. [Tooltip("This mask setting let you specify which objects will be considered as occluders and cause the see-through effect for this Highlight Effect component. For example, you assign your walls to a different layer and specify that layer here, so only walls and not other objects, like ground or ceiling, will trigger the see-through effect.")]
  292. public LayerMask seeThroughOccluderMask = -1;
  293. [Tooltip("A multiplier for the occluder volume size which can be used to reduce the actual size of occluders when Highlight Effect checks if they're occluding this object.")]
  294. [Range(0.01f, 0.6f)] public float seeThroughOccluderThreshold = 0.3f;
  295. [Tooltip("Uses stencil buffers to ensure pixel-accurate occlusion test. If this option is disabled, only physics raycasting is used to test for occlusion.")]
  296. public bool seeThroughOccluderMaskAccurate;
  297. [Tooltip("The interval of time between occlusion tests.")]
  298. public float seeThroughOccluderCheckInterval = 1f;
  299. [Tooltip("If enabled, occlusion test is performed for each children element. If disabled, the bounds of all children is combined and a single occlusion test is performed for the combined bounds.")]
  300. public bool seeThroughOccluderCheckIndividualObjects;
  301. [Tooltip("Shows the see-through effect only if the occluder if at this 'offset' distance from the object.")]
  302. public float seeThroughDepthOffset;
  303. [Tooltip("Hides the see-through effect if the occluder is further than this distance from the object (0 = infinite)")]
  304. public float seeThroughMaxDepth;
  305. [Range(0, 5f)] public float seeThroughIntensity = 0.8f;
  306. [Range(0, 1)] public float seeThroughTintAlpha = 0.5f;
  307. [ColorUsage(true, true)] public Color seeThroughTintColor = Color.red;
  308. [Range(0, 1)] public float seeThroughNoise = 1f;
  309. [Range(0, 1)] public float seeThroughBorder;
  310. public Color seeThroughBorderColor = Color.black;
  311. [Tooltip("Only display the border instead of the full see-through effect.")]
  312. public bool seeThroughBorderOnly;
  313. public float seeThroughBorderWidth = 0.45f;
  314. [Tooltip("This option clears the stencil buffer after rendering the see-through effect which results in correct rendering order and supports other stencil-based effects that render afterwards.")]
  315. public bool seeThroughOrdered;
  316. [Tooltip("Optional see-through mask effect texture.")]
  317. public Texture2D seeThroughTexture;
  318. public TextureUVSpace seeThroughTextureUVSpace;
  319. public float seeThroughTextureScale = 1f;
  320. [Tooltip("The order by which children objects are rendered by the see-through effect")]
  321. public SeeThroughSortingMode seeThroughChildrenSortingMode = SeeThroughSortingMode.Default;
  322. struct ModelMaterials {
  323. public bool render; // if this object can render this frame
  324. public Transform transform;
  325. public bool renderWasVisibleDuringSetup;
  326. public Mesh mesh, originalMesh, bakedSkinnedMesh;
  327. public Renderer renderer;
  328. public bool isSkinnedMesh;
  329. public Material[] fxMatMask, fxMatOutline, fxMatGlow, fxMatSolidColor, fxMatSeeThroughInner, fxMatSeeThroughBorder, fxMatOverlay, fxMatInnerGlow;
  330. public NormalsOption normalsOption;
  331. public Matrix4x4 renderingMatrix;
  332. public bool isCombined;
  333. public bool preserveOriginalMesh { get { return !isCombined && normalsOption == NormalsOption.PreserveOriginal; } }
  334. public void Init() {
  335. render = false;
  336. transform = null;
  337. mesh = originalMesh = null;
  338. if (bakedSkinnedMesh != null) DestroyImmediate(bakedSkinnedMesh);
  339. renderer = null;
  340. isSkinnedMesh = false;
  341. normalsOption = NormalsOption.Smooth;
  342. isCombined = false;
  343. }
  344. }
  345. enum FadingState {
  346. FadingOut = -1,
  347. NoFading = 0,
  348. FadingIn = 1
  349. }
  350. [SerializeField, HideInInspector]
  351. ModelMaterials[] rms;
  352. [SerializeField, HideInInspector]
  353. int rmsCount;
  354. /// <summary>
  355. /// Number of objects affected by this highlight effect script
  356. /// </summary>
  357. public int includedObjectsCount => rmsCount;
  358. #if UNITY_EDITOR
  359. /// <summary>
  360. /// True if there's some static children
  361. /// </summary>
  362. [NonSerialized]
  363. public bool staticChildren;
  364. #endif
  365. /// <summary>
  366. /// Returns true if the renderer for this gameobject is visible by any camera
  367. /// </summary>
  368. [NonSerialized]
  369. public bool isVisible;
  370. [NonSerialized]
  371. public Transform target;
  372. /// <summary>
  373. /// Time in which the highlight started
  374. /// </summary>
  375. [NonSerialized]
  376. public float highlightStartTime;
  377. /// <summary>
  378. /// Time in which the target fx started
  379. /// </summary>
  380. [NonSerialized]
  381. public float targetFxStartTime;
  382. /// <summary>
  383. /// True if this object is selected (if selectOnClick is used)
  384. /// </summary>
  385. [NonSerialized]
  386. public bool isSelected;
  387. /// <summary>
  388. /// If a sprite is used with this script, spriteMode = true. Certain mesh-only options will be disabled.
  389. /// </summary>
  390. [NonSerialized]
  391. public bool spriteMode;
  392. [NonSerialized]
  393. public HighlightProfile previousSettings;
  394. public void RestorePreviousHighlightEffectSettings() {
  395. previousSettings.Load(this);
  396. }
  397. const float TAU = 0.70711f;
  398. // Reference materials. These are instanced per object (rms).
  399. static Material fxMatMask, fxMatSolidColor, fxMatSeeThrough, fxMatSeeThroughBorder, fxMatOverlay, fxMatClearStencil;
  400. static Material fxMatSeeThroughMask;
  401. // Per-object materials
  402. Material fxMatGlowTemplate, fxMatInnerGlow, fxMatOutlineTemplate, fxMatTarget;
  403. Material fxMatComposeGlow, fxMatComposeOutline, fxMatBlurGlow, fxMatBlurOutline;
  404. static Vector4[] offsets;
  405. float fadeStartTime;
  406. FadingState fading = FadingState.NoFading;
  407. CommandBuffer cbHighlight;
  408. bool cbHighlightEmpty;
  409. int[] mipGlowBuffers, mipOutlineBuffers;
  410. static Mesh quadMesh, cubeMesh;
  411. int sourceRT;
  412. Matrix4x4 quadGlowMatrix, quadOutlineMatrix;
  413. Vector4[] corners;
  414. RenderTextureDescriptor sourceDesc;
  415. Color debugColor, blackColor;
  416. Visibility lastOutlineVisibility;
  417. bool requireUpdateMaterial;
  418. [NonSerialized]
  419. public static List<HighlightEffect> effects = new List<HighlightEffect>();
  420. public static bool customSorting;
  421. [NonSerialized]
  422. public float sortingOffset; // used to avoid two objects with same distance to camera during sorting
  423. bool useSmoothGlow, useSmoothOutline, useSmoothBlend;
  424. bool useGPUInstancing;
  425. bool usesReversedZBuffer;
  426. bool usesSeeThrough;
  427. class PerCameraOcclusionData {
  428. public float checkLastTime = -10000;
  429. public int occlusionRenderFrame;
  430. public bool lastOcclusionTestResult;
  431. public readonly List<Renderer> cachedOccluders = new List<Renderer>();
  432. }
  433. readonly Dictionary<Camera, PerCameraOcclusionData> perCameraOcclusionData = new Dictionary<Camera, PerCameraOcclusionData>();
  434. MaterialPropertyBlock glowPropertyBlock, outlinePropertyBlock;
  435. static readonly List<Vector4> matDataDirection = new List<Vector4>();
  436. static readonly List<Vector4> matDataGlow = new List<Vector4>();
  437. static readonly List<Vector4> matDataColor = new List<Vector4>();
  438. static Matrix4x4[] matrices;
  439. int outlineOffsetsMin, outlineOffsetsMax;
  440. int glowOffsetsMin, glowOffsetsMax;
  441. static CombineInstance[] combineInstances;
  442. bool maskRequired;
  443. FullScreenBlitMethod FullScreenBlit;
  444. Texture2D outlineGradientTex;
  445. Color[] outlineGradientColors;
  446. public static bool isVREnabled;
  447. bool shouldBakeSkinnedMesh;
  448. void OnEnable() {
  449. lastOutlineVisibility = outlineVisibility;
  450. debugColor = new Color(1f, 0f, 0f, 0.5f);
  451. blackColor = new Color(0, 0, 0, 0);
  452. if (offsets == null || offsets.Length != 8) {
  453. offsets = new Vector4[] {
  454. new Vector4(0,1),
  455. new Vector4(1,0),
  456. new Vector4(0,-1),
  457. new Vector4(-1,0),
  458. new Vector4 (-TAU, TAU),
  459. new Vector4 (TAU, TAU),
  460. new Vector4 (TAU, -TAU),
  461. new Vector4 (-TAU, -TAU)
  462. };
  463. }
  464. if (corners == null || corners.Length != 8) {
  465. corners = new Vector4[8];
  466. }
  467. InitCommandBuffer();
  468. if (quadMesh == null) {
  469. BuildQuad();
  470. }
  471. if (cubeMesh == null) {
  472. BuildCube();
  473. }
  474. if (target == null) {
  475. target = transform;
  476. }
  477. if (profileSync && profile != null) {
  478. profile.Load(this);
  479. }
  480. if (glowPasses == null || glowPasses.Length == 0) {
  481. glowPasses = new GlowPassData[4];
  482. glowPasses[0] = new GlowPassData() { offset = 4, alpha = 0.1f, color = new Color(0.64f, 1f, 0f, 1f) };
  483. glowPasses[1] = new GlowPassData() { offset = 3, alpha = 0.2f, color = new Color(0.64f, 1f, 0f, 1f) };
  484. glowPasses[2] = new GlowPassData() { offset = 2, alpha = 0.3f, color = new Color(0.64f, 1f, 0f, 1f) };
  485. glowPasses[3] = new GlowPassData() { offset = 1, alpha = 0.4f, color = new Color(0.64f, 1f, 0f, 1f) };
  486. }
  487. sourceRT = Shader.PropertyToID("_HPSourceRT");
  488. useGPUInstancing = GPUInstancing && SystemInfo.supportsInstancing;
  489. usesReversedZBuffer = SystemInfo.usesReversedZBuffer;
  490. if (useGPUInstancing) {
  491. if (glowPropertyBlock == null) {
  492. glowPropertyBlock = new MaterialPropertyBlock();
  493. }
  494. if (outlinePropertyBlock == null) {
  495. outlinePropertyBlock = new MaterialPropertyBlock();
  496. }
  497. }
  498. CheckGeometrySupportDependencies();
  499. UpdateVisibilityState();
  500. if (!effects.Contains(this)) {
  501. effects.Add(this);
  502. }
  503. }
  504. private void Start() {
  505. SetupMaterial();
  506. }
  507. public void OnDidApplyAnimationProperties() { // support for animating property based fields
  508. UpdateMaterialProperties();
  509. }
  510. void OnDisable() {
  511. UpdateMaterialProperties();
  512. if (effects != null) {
  513. int k = effects.IndexOf(this);
  514. if (k >= 0) {
  515. effects.RemoveAt(k);
  516. }
  517. }
  518. }
  519. void DestroyMaterial(Material mat) {
  520. if (mat != null) DestroyImmediate(mat);
  521. }
  522. void DestroyMaterialArray(Material[] mm) {
  523. if (mm == null) return;
  524. for (int k = 0; k < mm.Length; k++) {
  525. DestroyMaterial(mm[k]);
  526. }
  527. }
  528. void OnDestroy() {
  529. if (rms != null) {
  530. for (int k = 0; k < rms.Length; k++) {
  531. DestroyMaterialArray(rms[k].fxMatMask);
  532. DestroyMaterialArray(rms[k].fxMatOutline);
  533. DestroyMaterialArray(rms[k].fxMatGlow);
  534. DestroyMaterialArray(rms[k].fxMatSolidColor);
  535. DestroyMaterialArray(rms[k].fxMatSeeThroughInner);
  536. DestroyMaterialArray(rms[k].fxMatSeeThroughBorder);
  537. DestroyMaterialArray(rms[k].fxMatOverlay);
  538. DestroyMaterialArray(rms[k].fxMatInnerGlow);
  539. }
  540. }
  541. DestroyMaterial(fxMatGlowTemplate);
  542. DestroyMaterial(fxMatInnerGlow);
  543. DestroyMaterial(fxMatOutlineTemplate);
  544. DestroyMaterial(fxMatTarget);
  545. DestroyMaterial(fxMatComposeGlow);
  546. DestroyMaterial(fxMatComposeOutline);
  547. DestroyMaterial(fxMatBlurGlow);
  548. DestroyMaterial(fxMatBlurOutline);
  549. if (combinedMeshes.ContainsKey(combinedMeshesHashId)) {
  550. combinedMeshes.Remove(combinedMeshesHashId);
  551. }
  552. foreach (Mesh instancedMesh in instancedMeshes) {
  553. if (instancedMesh == null) continue;
  554. int usageCount;
  555. if (sharedMeshUsage.TryGetValue(instancedMesh, out usageCount)) {
  556. if (usageCount <= 1) {
  557. sharedMeshUsage.Remove(instancedMesh);
  558. DestroyImmediate(instancedMesh);
  559. } else {
  560. sharedMeshUsage[instancedMesh] = usageCount - 1;
  561. }
  562. }
  563. }
  564. }
  565. void Reset() {
  566. SetupMaterial();
  567. }
  568. private void OnBecameVisible() {
  569. isVisible = true;
  570. }
  571. private void OnBecameInvisible() {
  572. if (rms == null || rms.Length != 1 || rms[0].transform != transform) {
  573. // if effect group doesn't include exactly one object and this object is this same gameobject
  574. // ignore this optimization
  575. isVisible = true;
  576. } else {
  577. isVisible = false;
  578. }
  579. }
  580. /// <summary>
  581. /// Loads a profile into this effect
  582. /// </summary>
  583. public void ProfileLoad(HighlightProfile profile) {
  584. if (profile != null) {
  585. this.profile = profile;
  586. profile.Load(this);
  587. }
  588. }
  589. /// <summary>
  590. /// Reloads currently assigned profile
  591. /// </summary>
  592. public void ProfileReload() {
  593. if (profile != null) {
  594. profile.Load(this);
  595. }
  596. }
  597. /// <summary>
  598. /// Save current settings into given profile
  599. /// </summary>
  600. public void ProfileSaveChanges(HighlightProfile profile) {
  601. if (profile != null) {
  602. profile.Save(this);
  603. }
  604. }
  605. /// <summary>
  606. /// Save current settings into current profile
  607. /// </summary>
  608. public void ProfileSaveChanges() {
  609. if (profile != null) {
  610. profile.Save(this);
  611. }
  612. }
  613. public void Refresh(bool discardCachedMeshes = false) {
  614. if (discardCachedMeshes) {
  615. RefreshCachedMeshes();
  616. }
  617. if (enabled) {
  618. SetupMaterial();
  619. }
  620. }
  621. RenderTargetIdentifier colorAttachmentBuffer, depthAttachmentBuffer;
  622. public CommandBuffer GetCommandBuffer(Camera cam, RenderTargetIdentifier colorAttachmentBuffer, RenderTargetIdentifier depthAttachmentBuffer, FullScreenBlitMethod fullScreenBlit, bool clearStencil) {
  623. this.colorAttachmentBuffer = colorAttachmentBuffer;
  624. this.depthAttachmentBuffer = depthAttachmentBuffer;
  625. this.FullScreenBlit = fullScreenBlit;
  626. BuildCommandBuffer(cam, clearStencil);
  627. return cbHighlightEmpty ? null : cbHighlight;
  628. }
  629. void BuildCommandBuffer(Camera cam, bool clearStencil) {
  630. if (colorAttachmentBuffer == 0) {
  631. colorAttachmentBuffer = BuiltinRenderTextureType.CameraTarget;
  632. }
  633. if (depthAttachmentBuffer == 0) {
  634. depthAttachmentBuffer = BuiltinRenderTextureType.CameraTarget;
  635. }
  636. InitCommandBuffer();
  637. if (requireUpdateMaterial) {
  638. requireUpdateMaterial = false;
  639. UpdateMaterialProperties();
  640. }
  641. bool independentFullScreenNotExecuted = true;
  642. if (clearStencil) {
  643. ConfigureOutput();
  644. cbHighlight.DrawMesh(quadMesh, Matrix4x4.identity, fxMatClearStencil, 0, 0);
  645. independentFullScreenNotExecuted = false;
  646. }
  647. bool seeThroughReal = usesSeeThrough;
  648. if (seeThroughReal) {
  649. ConfigureOutput();
  650. seeThroughReal = RenderSeeThroughOccluders(cbHighlight, cam);
  651. if (seeThroughReal && seeThroughOccluderMask != -1) {
  652. if (seeThroughOccluderMaskAccurate) {
  653. CheckOcclusionAccurate(cbHighlight, cam);
  654. } else {
  655. seeThroughReal = CheckOcclusion(cam);
  656. }
  657. }
  658. }
  659. bool showOverlay = hitActive || overlayMode == OverlayMode.Always;
  660. if (!_highlighted && !seeThroughReal && !showOverlay) {
  661. return;
  662. }
  663. ConfigureOutput();
  664. if (rms == null) {
  665. SetupMaterial();
  666. if (rms == null) return;
  667. }
  668. // Check camera culling mask
  669. int cullingMask = cam.cullingMask;
  670. // Ensure renderers are valid and visible (in case LODgroup has changed active renderer)
  671. if (!ignoreObjectVisibility) {
  672. for (int k = 0; k < rmsCount; k++) {
  673. if (rms[k].renderer != null && rms[k].renderer.isVisible != rms[k].renderWasVisibleDuringSetup) {
  674. SetupMaterial();
  675. break;
  676. }
  677. }
  678. }
  679. // Apply effect
  680. float glowReal = _highlighted ? this.glow : 0;
  681. if (fxMatMask == null)
  682. return;
  683. // Check smooth blend ztesting capability
  684. Visibility smoothGlowVisibility = glowVisibility;
  685. Visibility smoothOutlineVisibility = outlineVisibility;
  686. // First create masks
  687. float aspect = cam.aspect;
  688. bool somePartVisible = false;
  689. for (int k = 0; k < rmsCount; k++) {
  690. rms[k].render = false;
  691. Transform t = rms[k].transform;
  692. if (t == null)
  693. continue;
  694. if (rms[k].isSkinnedMesh && shouldBakeSkinnedMesh) {
  695. SkinnedMeshRenderer smr = (SkinnedMeshRenderer)rms[k].renderer;
  696. if (rms[k].bakedSkinnedMesh == null) {
  697. rms[k].bakedSkinnedMesh = new Mesh();
  698. }
  699. smr.BakeMesh(rms[k].bakedSkinnedMesh, true);
  700. rms[k].mesh = rms[k].bakedSkinnedMesh;
  701. rms[k].normalsOption = NormalsOption.Smooth;
  702. }
  703. Mesh mesh = rms[k].mesh;
  704. if (mesh == null)
  705. continue;
  706. if (!ignoreObjectVisibility) {
  707. int layer = t.gameObject.layer;
  708. if (((1 << layer) & cullingMask) == 0)
  709. continue;
  710. if (!rms[k].renderer.isVisible)
  711. continue;
  712. }
  713. rms[k].render = true;
  714. somePartVisible = true;
  715. if (rms[k].isCombined) {
  716. rms[k].renderingMatrix = t.localToWorldMatrix;
  717. }
  718. if (outlineIndependent) {
  719. if (useSmoothBlend) {
  720. if (independentFullScreenNotExecuted) {
  721. independentFullScreenNotExecuted = false;
  722. cbHighlight.DrawMesh(quadMesh, Matrix4x4.identity, fxMatClearStencil, 0, 0);
  723. }
  724. } else if (outline > 0 || glow > 0) {
  725. float width = outlineWidth;
  726. if (glow > 0) {
  727. width = Mathf.Max(width, glowWidth);
  728. }
  729. for (int l = 0; l < mesh.subMeshCount; l++) {
  730. if (((1 << l) & subMeshMask) == 0) continue;
  731. if (outlineQuality.UsesMultipleOffsets()) {
  732. for (int o = outlineOffsetsMin; o <= outlineOffsetsMax; o++) {
  733. Vector4 direction = offsets[o] * (width / 100f);
  734. direction.y *= aspect;
  735. cbHighlight.SetGlobalVector(ShaderParams.OutlineDirection, direction);
  736. if (rms[k].isCombined) {
  737. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatOutline[l], l, 1);
  738. } else {
  739. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatOutline[l], l, 1);
  740. }
  741. }
  742. } else {
  743. cbHighlight.SetGlobalVector(ShaderParams.OutlineDirection, Vector4.zero);
  744. if (rms[k].isCombined) {
  745. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatOutline[l], l, 1);
  746. } else {
  747. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatOutline[l], l, 1);
  748. }
  749. }
  750. }
  751. }
  752. }
  753. }
  754. bool renderMaskOnTop = _highlighted && ((outline > 0 && smoothOutlineVisibility != Visibility.Normal) || (glow > 0 && smoothGlowVisibility != Visibility.Normal) || (innerGlow > 0 && innerGlowVisibility != Visibility.Normal));
  755. renderMaskOnTop = renderMaskOnTop | (useSmoothBlend && outlineContourStyle == ContourStyle.AroundObjectShape);
  756. if (maskRequired) {
  757. for (int k = 0; k < rmsCount; k++) {
  758. if (rms[k].render) {
  759. RenderMask(k, rms[k].mesh, renderMaskOnTop);
  760. }
  761. }
  762. }
  763. // Compute tweening
  764. float fadeGroup = 1f;
  765. float fade = 1f;
  766. if (fading != FadingState.NoFading) {
  767. if (fading == FadingState.FadingIn) {
  768. if (fadeInDuration > 0) {
  769. fadeGroup = (Time.time - fadeStartTime) / fadeInDuration;
  770. if (fadeGroup > 1f) {
  771. fadeGroup = 1f;
  772. fading = FadingState.NoFading;
  773. }
  774. }
  775. } else if (fadeOutDuration > 0) {
  776. fadeGroup = 1f - (Time.time - fadeStartTime) / fadeOutDuration;
  777. if (fadeGroup < 0f) {
  778. fadeGroup = 0f;
  779. fading = FadingState.NoFading;
  780. _highlighted = false;
  781. if (OnObjectHighlightEnd != null) {
  782. OnObjectHighlightEnd(gameObject);
  783. }
  784. SendMessage("HighlightEnd", null, SendMessageOptions.DontRequireReceiver);
  785. }
  786. }
  787. }
  788. if (glowQuality == QualityLevel.High) {
  789. glowReal *= 0.25f;
  790. } else if (glowQuality == QualityLevel.Medium) {
  791. glowReal *= 0.5f;
  792. }
  793. bool targetEffectRendered = false;
  794. bool usesSeeThroughBorder = (seeThroughBorder * seeThroughBorderWidth) > 0;
  795. // Add mesh effects
  796. for (int k = 0; k < rmsCount; k++) {
  797. if (!rms[k].render)
  798. continue;
  799. Mesh mesh = rms[k].mesh;
  800. fade = fadeGroup;
  801. // Distance fade
  802. if (cameraDistanceFade) {
  803. fade *= ComputeCameraDistanceFade(rms[k].transform.position, cam.transform);
  804. cbHighlight.SetGlobalFloat(ShaderParams.FadeFactor, fade);
  805. } else {
  806. cbHighlight.SetGlobalFloat(ShaderParams.FadeFactor, 1f);
  807. }
  808. if (_highlighted || showOverlay) {
  809. // Hit FX
  810. Color overlayColor = this.overlayColor;
  811. float overlayMinIntensity = this.overlayMinIntensity;
  812. float overlayBlending = this.overlayBlending;
  813. Color innerGlowColorA = this.innerGlowColor;
  814. float innerGlow = this.innerGlow;
  815. if (hitActive) {
  816. overlayColor.a = _highlighted ? overlay : 0;
  817. innerGlowColorA.a = _highlighted ? innerGlow : 0;
  818. float t = hitFadeOutDuration > 0 ? (Time.time - hitStartTime) / hitFadeOutDuration : 1f;
  819. if (t >= 1f) {
  820. hitActive = false;
  821. } else {
  822. if (hitFxMode == HitFxMode.InnerGlow) {
  823. bool lerpToCurrentInnerGlow = _highlighted && innerGlow > 0;
  824. innerGlowColorA = lerpToCurrentInnerGlow ? Color.Lerp(hitColor, innerGlowColor, t) : hitColor;
  825. innerGlowColorA.a = lerpToCurrentInnerGlow ? Mathf.Lerp(1f - t, innerGlow, t) : 1f - t;
  826. innerGlowColorA.a *= hitInitialIntensity;
  827. } else {
  828. bool lerpToCurrentOverlay = _highlighted && overlay > 0;
  829. overlayColor = lerpToCurrentOverlay ? Color.Lerp(hitColor, overlayColor, t) : hitColor;
  830. overlayColor.a = lerpToCurrentOverlay ? Mathf.Lerp(1f - t, overlay, t) : 1f - t;
  831. overlayColor.a *= hitInitialIntensity;
  832. overlayMinIntensity = 1f;
  833. overlayBlending = 0;
  834. }
  835. }
  836. } else {
  837. overlayColor.a = overlay * fade;
  838. innerGlowColorA.a = innerGlow * fade;
  839. }
  840. for (int l = 0; l < mesh.subMeshCount; l++) {
  841. if (((1 << l) & subMeshMask) == 0) continue;
  842. // Overlay
  843. if (overlayColor.a > 0) {
  844. Material fxMat = rms[k].fxMatOverlay[l];
  845. fxMat.SetColor(ShaderParams.OverlayColor, overlayColor);
  846. fxMat.SetVector(ShaderParams.OverlayData, new Vector4(overlayAnimationSpeed, overlayMinIntensity, overlayBlending, overlayTextureScale));
  847. if (hitActive && hitFxMode == HitFxMode.LocalHit) {
  848. fxMat.SetVector(ShaderParams.OverlayHitPosData, new Vector4(hitPosition.x, hitPosition.y, hitPosition.z, hitRadius));
  849. fxMat.SetFloat(ShaderParams.OverlayHitStartTime, hitStartTime);
  850. } else {
  851. fxMat.SetVector(ShaderParams.OverlayHitPosData, Vector4.zero);
  852. }
  853. if (rms[k].isCombined) {
  854. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatOverlay[l], l);
  855. } else {
  856. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatOverlay[l], l);
  857. }
  858. }
  859. // Inner Glow
  860. if (innerGlowColorA.a > 0) {
  861. rms[k].fxMatInnerGlow[l].SetColor(ShaderParams.InnerGlowColor, innerGlowColorA);
  862. if (rms[k].isCombined) {
  863. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatInnerGlow[l], l);
  864. } else {
  865. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatInnerGlow[l], l);
  866. }
  867. }
  868. }
  869. }
  870. if (!_highlighted)
  871. continue;
  872. bool allowGPUInstancing = useGPUInstancing && !rms[k].isSkinnedMesh;
  873. for (int l = 0; l < mesh.subMeshCount; l++) {
  874. if (((1 << l) & subMeshMask) == 0) continue;
  875. // Glow
  876. if (glow > 0 && glowQuality != QualityLevel.Highest) {
  877. matDataGlow.Clear();
  878. matDataColor.Clear();
  879. matDataDirection.Clear();
  880. for (int glowPass = 0; glowPass < glowPasses.Length; glowPass++) {
  881. if (glowQuality.UsesMultipleOffsets()) {
  882. for (int o = glowOffsetsMin; o <= glowOffsetsMax; o++) {
  883. Vector4 direction = offsets[o];
  884. direction.y *= aspect;
  885. Color dataColor = glowPasses[glowPass].color;
  886. Vector4 dataGlow = new Vector4(fade * glowReal * glowPasses[glowPass].alpha, glowPasses[glowPass].offset * glowWidth / 100f, glowMagicNumber1, glowMagicNumber2);
  887. if (allowGPUInstancing) {
  888. matDataDirection.Add(direction);
  889. matDataGlow.Add(dataGlow);
  890. matDataColor.Add(new Vector4(dataColor.r, dataColor.g, dataColor.b, dataColor.a));
  891. } else {
  892. cbHighlight.SetGlobalVector(ShaderParams.GlowDirection, direction);
  893. cbHighlight.SetGlobalColor(ShaderParams.GlowColor, dataColor);
  894. cbHighlight.SetGlobalVector(ShaderParams.Glow, dataGlow);
  895. if (rms[k].isCombined) {
  896. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatGlow[l], l);
  897. } else {
  898. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatGlow[l], l);
  899. }
  900. }
  901. }
  902. } else {
  903. Vector4 dataGlow = new Vector4(fade * glowReal * glowPasses[glowPass].alpha, glowPasses[glowPass].offset * glowWidth / 100f, glowMagicNumber1, glowMagicNumber2);
  904. Color dataColor = glowPasses[glowPass].color;
  905. if (allowGPUInstancing) {
  906. matDataDirection.Add(Vector4.zero);
  907. matDataGlow.Add(dataGlow);
  908. matDataColor.Add(new Vector4(dataColor.r, dataColor.g, dataColor.b, dataColor.a));
  909. } else {
  910. int matIndex = glowPass * 8;
  911. cbHighlight.SetGlobalColor(ShaderParams.GlowColor, dataColor);
  912. cbHighlight.SetGlobalVector(ShaderParams.Glow, dataGlow);
  913. cbHighlight.SetGlobalVector(ShaderParams.GlowDirection, Vector4.zero);
  914. if (rms[k].isCombined) {
  915. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatGlow[l], l);
  916. } else {
  917. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatGlow[l], l);
  918. }
  919. }
  920. }
  921. }
  922. if (allowGPUInstancing) {
  923. int instanceCount = matDataDirection.Count;
  924. if (instanceCount > 0) {
  925. glowPropertyBlock.Clear();
  926. glowPropertyBlock.SetVectorArray(ShaderParams.GlowDirection, matDataDirection);
  927. glowPropertyBlock.SetVectorArray(ShaderParams.GlowColor, matDataColor);
  928. glowPropertyBlock.SetVectorArray(ShaderParams.Glow, matDataGlow);
  929. if (matrices == null || matrices.Length < instanceCount) {
  930. matrices = new Matrix4x4[instanceCount];
  931. }
  932. if (rms[k].isCombined) {
  933. for (int m = 0; m < instanceCount; m++) {
  934. matrices[m] = rms[k].renderingMatrix;
  935. }
  936. } else {
  937. Matrix4x4 objectToWorld = rms[k].transform.localToWorldMatrix;
  938. for (int m = 0; m < instanceCount; m++) {
  939. matrices[m] = objectToWorld;
  940. }
  941. }
  942. cbHighlight.DrawMeshInstanced(mesh, l, rms[k].fxMatGlow[l], 0, matrices, instanceCount, glowPropertyBlock);
  943. }
  944. }
  945. }
  946. // Outline
  947. if (outline > 0 && outlineQuality != QualityLevel.Highest) {
  948. Color outlineColor = this.outlineColor;
  949. if (outlineColorStyle == ColorStyle.Gradient) {
  950. outlineColor.a *= outline * fade;
  951. Bounds bounds = outlineGradientInLocalSpace ? mesh.bounds : rms[k].renderer.bounds;
  952. cbHighlight.SetGlobalVector(ShaderParams.OutlineVertexData, new Vector4(bounds.min.y, bounds.size.y + 0.0001f, 0, 0));
  953. } else {
  954. outlineColor.a = outline * fade;
  955. cbHighlight.SetGlobalVector(ShaderParams.OutlineVertexData, new Vector4(-1e6f, 1f, 0, 0));
  956. }
  957. cbHighlight.SetGlobalColor(ShaderParams.OutlineColor, outlineColor);
  958. if (outlineQuality.UsesMultipleOffsets()) {
  959. matDataDirection.Clear();
  960. for (int o = outlineOffsetsMin; o <= outlineOffsetsMax; o++) {
  961. Vector4 direction = offsets[o] * (outlineWidth / 100f);
  962. direction.y *= aspect;
  963. if (allowGPUInstancing) {
  964. matDataDirection.Add(direction);
  965. } else {
  966. cbHighlight.SetGlobalVector(ShaderParams.OutlineDirection, direction);
  967. if (rms[k].isCombined) {
  968. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatOutline[l], l, 0);
  969. } else {
  970. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatOutline[l], l, 0);
  971. }
  972. }
  973. }
  974. if (allowGPUInstancing) {
  975. int instanceCount = matDataDirection.Count;
  976. if (instanceCount > 0) {
  977. outlinePropertyBlock.Clear();
  978. outlinePropertyBlock.SetVectorArray(ShaderParams.OutlineDirection, matDataDirection);
  979. if (matrices == null || matrices.Length < instanceCount) {
  980. matrices = new Matrix4x4[instanceCount];
  981. }
  982. if (rms[k].isCombined) {
  983. for (int m = 0; m < instanceCount; m++) {
  984. matrices[m] = rms[k].renderingMatrix;
  985. }
  986. } else {
  987. Matrix4x4 objectToWorld = rms[k].transform.localToWorldMatrix;
  988. for (int m = 0; m < instanceCount; m++) {
  989. matrices[m] = objectToWorld;
  990. }
  991. }
  992. cbHighlight.DrawMeshInstanced(mesh, l, rms[k].fxMatOutline[l], 0, matrices, instanceCount, outlinePropertyBlock);
  993. }
  994. }
  995. } else {
  996. cbHighlight.SetGlobalColor(ShaderParams.OutlineColor, outlineColor);
  997. cbHighlight.SetGlobalVector(ShaderParams.OutlineDirection, Vector4.zero);
  998. if (rms[k].isSkinnedMesh) {
  999. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatOutline[l], l, 0);
  1000. } else {
  1001. // keep this because normals may be reoriented / smoothed
  1002. cbHighlight.DrawMesh(mesh, rms[k].transform.localToWorldMatrix, rms[k].fxMatOutline[l], l, 0);
  1003. }
  1004. }
  1005. }
  1006. }
  1007. // Target
  1008. if (targetFX) {
  1009. float fadeOut = 1f;
  1010. if (targetFXStayDuration > 0 && Application.isPlaying) {
  1011. fadeOut = (Time.time - targetFxStartTime);
  1012. if (fadeOut >= targetFXStayDuration) {
  1013. fadeOut -= targetFXStayDuration;
  1014. fadeOut = 1f - fadeOut;
  1015. }
  1016. if (fadeOut > 1f) {
  1017. fadeOut = 1f;
  1018. }
  1019. }
  1020. bool usesTarget = targetFXCenter != null;
  1021. if (fadeOut > 0 && !(targetEffectRendered && usesTarget)) {
  1022. targetEffectRendered = true;
  1023. float scaleT = 1f;
  1024. float time;
  1025. float normalizedTime = 0;
  1026. if (Application.isPlaying) {
  1027. normalizedTime = (Time.time - targetFxStartTime) / targetFXTransitionDuration;
  1028. if (normalizedTime > 1f) {
  1029. normalizedTime = 1f;
  1030. }
  1031. scaleT = Mathf.Sin(normalizedTime * Mathf.PI * 0.5f);
  1032. time = Time.time;
  1033. } else {
  1034. time = (float)DateTime.Now.Subtract(DateTime.Today).TotalSeconds;
  1035. }
  1036. Bounds bounds = rms[k].renderer.bounds;
  1037. if (!targetFXScaleToRenderBounds) {
  1038. bounds.size = Vector3.one;
  1039. }
  1040. Vector3 scale = bounds.size;
  1041. float minSize = scale.x;
  1042. if (scale.y < minSize) {
  1043. minSize = scale.y;
  1044. }
  1045. if (scale.z < minSize) {
  1046. minSize = scale.z;
  1047. }
  1048. scale.x = scale.y = scale.z = minSize;
  1049. scale = Vector3.Lerp(scale * targetFXInitialScale, scale * targetFXEndScale, scaleT);
  1050. Vector3 center = usesTarget ? targetFXCenter.transform.position : bounds.center;
  1051. Quaternion rotation;
  1052. if (targetFXAlignToGround) {
  1053. rotation = Quaternion.Euler(90, 0, 0);
  1054. center.y += 0.5f; // a bit of offset in case it's in contact with ground
  1055. if (Physics.Raycast(center, Vector3.down, out RaycastHit groundHitInfo, targetFXGroundMaxDistance, targetFXGroundLayerMask)) {
  1056. center = groundHitInfo.point;
  1057. center.y += 0.01f;
  1058. Vector4 renderData = groundHitInfo.normal;
  1059. renderData.w = targetFXFadePower;
  1060. fxMatTarget.SetVector(ShaderParams.TargetFXRenderData, renderData);
  1061. rotation = Quaternion.Euler(0, time * targetFXRotationSpeed, 0);
  1062. if (OnTargetAnimates != null) {
  1063. OnTargetAnimates(ref center, ref rotation, ref scale, normalizedTime);
  1064. }
  1065. Matrix4x4 m = Matrix4x4.TRS(center, rotation, scale);
  1066. Color color = targetFXColor;
  1067. color.a *= fade * fadeOut;
  1068. fxMatTarget.color = color;
  1069. cbHighlight.DrawMesh(cubeMesh, m, fxMatTarget, 0, 0);
  1070. }
  1071. } else {
  1072. rotation = Quaternion.LookRotation(cam.transform.position - rms[k].transform.position);
  1073. Quaternion animationRot = Quaternion.Euler(0, 0, time * targetFXRotationSpeed);
  1074. rotation *= animationRot;
  1075. if (OnTargetAnimates != null) {
  1076. OnTargetAnimates(ref center, ref rotation, ref scale, normalizedTime);
  1077. }
  1078. Matrix4x4 m = Matrix4x4.TRS(center, rotation, scale);
  1079. Color color = targetFXColor;
  1080. color.a *= fade * fadeOut;
  1081. fxMatTarget.color = color;
  1082. cbHighlight.DrawMesh(quadMesh, m, fxMatTarget, 0, 1);
  1083. }
  1084. }
  1085. }
  1086. }
  1087. if (useSmoothBlend && _highlighted && somePartVisible) {
  1088. Bounds smoothBounds = new Bounds();
  1089. // Prepare smooth outer glow / outline target
  1090. int smoothRTWidth = cam.pixelWidth;
  1091. if (smoothRTWidth <= 0) {
  1092. smoothRTWidth = 1;
  1093. }
  1094. int smoothRTHeight = cam.pixelHeight;
  1095. if (smoothRTHeight <= 0) {
  1096. smoothRTHeight = 1;
  1097. }
  1098. if (isVREnabled) {
  1099. #if UNITY_2022_3_OR_NEWER
  1100. if (Application.platform == RuntimePlatform.Android) {
  1101. // Workaround for Quest 2 multi-pass
  1102. RenderTextureDescriptor vrDesc = UnityEngine.XR.XRSettings.eyeTextureDesc;
  1103. smoothRTWidth = vrDesc.width;
  1104. smoothRTHeight = vrDesc.height;
  1105. sourceDesc = new RenderTextureDescriptor(smoothRTWidth, smoothRTHeight);
  1106. sourceDesc.vrUsage = vrDesc.vrUsage;
  1107. sourceDesc.volumeDepth = 1;
  1108. } else {
  1109. #endif
  1110. sourceDesc = UnityEngine.XR.XRSettings.eyeTextureDesc;
  1111. smoothRTWidth = sourceDesc.width;
  1112. smoothRTHeight = sourceDesc.height;
  1113. #if UNITY_2022_3_OR_NEWER
  1114. }
  1115. #endif
  1116. } else {
  1117. sourceDesc = new RenderTextureDescriptor(smoothRTWidth, smoothRTHeight);
  1118. sourceDesc.volumeDepth = 1;
  1119. }
  1120. sourceDesc.colorFormat = useSmoothOutline && outlineEdgeMode == OutlineEdgeMode.Any ? RenderTextureFormat.ARGB32 : RenderTextureFormat.R8;
  1121. sourceDesc.msaaSamples = 1;
  1122. sourceDesc.useMipMap = false;
  1123. sourceDesc.depthBufferBits = 0;
  1124. cbHighlight.GetTemporaryRT(sourceRT, sourceDesc, FilterMode.Bilinear);
  1125. RenderTargetIdentifier sourceTarget = new RenderTargetIdentifier(sourceRT, 0, CubemapFace.Unknown, -1);
  1126. cbHighlight.SetRenderTarget(sourceTarget);
  1127. cbHighlight.ClearRenderTarget(false, true, new Color(0, 0, 0, 0));
  1128. for (int k = 0; k < rmsCount; k++) {
  1129. if (!rms[k].render)
  1130. continue;
  1131. if (k == 0) {
  1132. smoothBounds = rms[k].renderer.bounds;
  1133. } else {
  1134. smoothBounds.Encapsulate(rms[k].renderer.bounds);
  1135. }
  1136. Mesh mesh = rms[k].mesh;
  1137. // Render object body for glow/outline highest quality
  1138. for (int l = 0; l < mesh.subMeshCount; l++) {
  1139. if (((1 << l) & subMeshMask) == 0) continue;
  1140. if (l < rms[k].fxMatSolidColor.Length) {
  1141. if (rms[k].isCombined) {
  1142. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatSolidColor[l], l);
  1143. } else {
  1144. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatSolidColor[l], l);
  1145. }
  1146. }
  1147. }
  1148. }
  1149. if (ComputeSmoothQuadMatrix(cam, smoothBounds)) {
  1150. // Smooth Glow
  1151. if (useSmoothGlow) {
  1152. float intensity = glow * fade;
  1153. fxMatComposeGlow.color = new Color(glowHQColor.r * intensity, glowHQColor.g * intensity, glowHQColor.b * intensity, glowHQColor.a * intensity);
  1154. SmoothGlow(smoothRTWidth / glowDownsampling, smoothRTHeight / glowDownsampling);
  1155. }
  1156. // Smooth Outline
  1157. if (useSmoothOutline) {
  1158. float intensity = outline * fade;
  1159. fxMatComposeOutline.color = new Color(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a * intensity * 10f);
  1160. SmoothOutline(smoothRTWidth / outlineDownsampling, smoothRTHeight / outlineDownsampling);
  1161. }
  1162. // Bit result
  1163. ComposeSmoothBlend(smoothGlowVisibility, smoothOutlineVisibility);
  1164. }
  1165. }
  1166. // See-Through
  1167. if (seeThroughReal) {
  1168. if (renderMaskOnTop) {
  1169. for (int k = 0; k < rmsCount; k++) {
  1170. if (!rms[k].render)
  1171. continue;
  1172. Mesh mesh = rms[k].mesh;
  1173. RenderSeeThroughClearStencil(k, mesh);
  1174. }
  1175. for (int k = 0; k < rmsCount; k++) {
  1176. if (!rms[k].render)
  1177. continue;
  1178. Mesh mesh = rms[k].mesh;
  1179. RenderSeeThroughMask(k, mesh);
  1180. }
  1181. }
  1182. for (int k = 0; k < rmsCount; k++) {
  1183. if (!rms[k].render)
  1184. continue;
  1185. Mesh mesh = rms[k].mesh;
  1186. for (int l = 0; l < mesh.subMeshCount; l++) {
  1187. if (((1 << l) & subMeshMask) == 0) continue;
  1188. if (l < rms[k].fxMatSeeThroughInner.Length && rms[k].fxMatSeeThroughInner[l] != null) {
  1189. if (rms[k].isCombined) {
  1190. cbHighlight.DrawMesh(mesh, rms[k].renderingMatrix, rms[k].fxMatSeeThroughInner[l], l);
  1191. } else {
  1192. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatSeeThroughInner[l], l);
  1193. }
  1194. }
  1195. if (usesSeeThroughBorder) {
  1196. if (rms[k].isCombined) {
  1197. cbHighlight.DrawMesh(mesh, rms[k].renderingMatrix, rms[k].fxMatSeeThroughBorder[l], l);
  1198. } else {
  1199. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatSeeThroughBorder[l], l);
  1200. }
  1201. }
  1202. }
  1203. }
  1204. if (seeThroughOrdered) { // Ordered for see-through
  1205. for (int k = 0; k < rmsCount; k++) {
  1206. if (!rms[k].render)
  1207. continue;
  1208. Mesh mesh = rms[k].mesh;
  1209. for (int l = 0; l < mesh.subMeshCount; l++) {
  1210. if (((1 << l) & subMeshMask) == 0) continue;
  1211. if (rms[k].isCombined) {
  1212. cbHighlight.DrawMesh(mesh, rms[k].renderingMatrix, fxMatClearStencil, l, 1);
  1213. } else {
  1214. cbHighlight.DrawRenderer(rms[k].renderer, fxMatClearStencil, l, 1);
  1215. }
  1216. }
  1217. }
  1218. }
  1219. }
  1220. }
  1221. void RenderMask(int k, Mesh mesh, bool renderMaskOnTop) {
  1222. for (int l = 0; l < mesh.subMeshCount; l++) {
  1223. if (((1 << l) & subMeshMask) == 0) continue;
  1224. if (renderMaskOnTop) {
  1225. rms[k].fxMatMask[l].SetInt(ShaderParams.ZTest, (int)CompareFunction.Always);
  1226. } else {
  1227. rms[k].fxMatMask[l].SetInt(ShaderParams.ZTest, (int)CompareFunction.LessEqual);
  1228. }
  1229. if (rms[k].isCombined) {
  1230. cbHighlight.DrawMesh(rms[k].mesh, rms[k].renderingMatrix, rms[k].fxMatMask[l], l, 0);
  1231. } else {
  1232. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatMask[l], l, 0);
  1233. }
  1234. }
  1235. }
  1236. void RenderSeeThroughClearStencil(int k, Mesh mesh) {
  1237. if (rms[k].isCombined) {
  1238. for (int l = 0; l < mesh.subMeshCount; l++) {
  1239. if (((1 << l) & subMeshMask) == 0) continue;
  1240. cbHighlight.DrawMesh(mesh, rms[k].renderingMatrix, fxMatClearStencil, l, 1);
  1241. }
  1242. } else {
  1243. for (int l = 0; l < mesh.subMeshCount; l++) {
  1244. if (((1 << l) & subMeshMask) == 0) continue;
  1245. cbHighlight.DrawRenderer(rms[k].renderer, fxMatClearStencil, l, 1);
  1246. }
  1247. }
  1248. }
  1249. void RenderSeeThroughMask(int k, Mesh mesh) {
  1250. if (rms[k].isCombined) {
  1251. for (int l = 0; l < mesh.subMeshCount; l++) {
  1252. if (((1 << l) & subMeshMask) == 0) continue;
  1253. cbHighlight.DrawMesh(mesh, rms[k].renderingMatrix, rms[k].fxMatMask[l], l, 1);
  1254. }
  1255. } else {
  1256. for (int l = 0; l < mesh.subMeshCount; l++) {
  1257. if (((1 << l) & subMeshMask) == 0) continue;
  1258. cbHighlight.DrawRenderer(rms[k].renderer, rms[k].fxMatMask[l], l, 1);
  1259. }
  1260. }
  1261. }
  1262. void WorldToViewportPoint(ref Matrix4x4 m, ref Vector4 p, bool perspectiveProjection, float zBufferParamsZ, float zBufferParamsW) {
  1263. p = m * p;
  1264. p.x = (p.x / p.w + 1f) * 0.5f;
  1265. p.y = (p.y / p.w + 1f) * 0.5f;
  1266. if (perspectiveProjection) {
  1267. p.z /= p.w;
  1268. p.z = 1.0f / (zBufferParamsZ * p.z + zBufferParamsW);
  1269. } else {
  1270. if (usesReversedZBuffer) {
  1271. p.z = 1f - p.z;
  1272. }
  1273. p.z = (zBufferParamsW - zBufferParamsZ) * p.z + zBufferParamsZ;
  1274. }
  1275. }
  1276. bool ComputeSmoothQuadMatrix(Camera cam, Bounds bounds) {
  1277. // Compute bounds in screen space and enlarge for glow space
  1278. Vector3 shift = cam.transform.position;
  1279. cam.transform.position = Vector3.zero;
  1280. cam.ResetWorldToCameraMatrix();
  1281. bounds.center -= shift;
  1282. bool res = ComputeSmoothQuadMatrixOriginShifted(cam, ref bounds, ref shift);
  1283. cam.transform.position = shift;
  1284. return res;
  1285. }
  1286. bool ComputeSmoothQuadMatrixOriginShifted(Camera cam, ref Bounds bounds, ref Vector3 shift) {
  1287. // Compute bounds in screen space and enlarge for glow space
  1288. Matrix4x4 mat = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false) * cam.worldToCameraMatrix;
  1289. Vector3 min = bounds.min;
  1290. Vector3 max = bounds.max;
  1291. corners[0] = new Vector4(min.x, min.y, min.z, 1f);
  1292. corners[1] = new Vector4(min.x, min.y, max.z, 1f);
  1293. corners[2] = new Vector4(max.x, min.y, min.z, 1f);
  1294. corners[3] = new Vector4(max.x, min.y, max.z, 1f);
  1295. corners[4] = new Vector4(min.x, max.y, min.z, 1f);
  1296. corners[5] = new Vector4(min.x, max.y, max.z, 1f);
  1297. corners[6] = new Vector4(max.x, max.y, min.z, 1f);
  1298. corners[7] = new Vector4(max.x, max.y, max.z, 1f);
  1299. Vector3 scrMin = new Vector3(float.MaxValue, float.MaxValue, 0);
  1300. Vector3 scrMax = new Vector3(float.MinValue, float.MinValue, 0);
  1301. float distanceMin = float.MaxValue;
  1302. float distanceMax = float.MinValue;
  1303. float nearClipPlane = cam.nearClipPlane;
  1304. float farClipPlane = cam.farClipPlane;
  1305. float x, y, z, w;
  1306. bool isPerspectiveCamera = !cam.orthographic;
  1307. if (isPerspectiveCamera) {
  1308. if (usesReversedZBuffer) {
  1309. x = -1f + farClipPlane / nearClipPlane;
  1310. y = 1f;
  1311. z = x / farClipPlane;
  1312. w = 1f / farClipPlane;
  1313. } else {
  1314. x = 1f - farClipPlane / nearClipPlane;
  1315. y = farClipPlane / nearClipPlane;
  1316. z = x / farClipPlane;
  1317. w = y / farClipPlane;
  1318. }
  1319. } else {
  1320. z = nearClipPlane;
  1321. w = farClipPlane;
  1322. }
  1323. for (int k = 0; k < 8; k++) {
  1324. WorldToViewportPoint(ref mat, ref corners[k], isPerspectiveCamera, z, w);
  1325. if (corners[k].x < scrMin.x) {
  1326. scrMin.x = corners[k].x;
  1327. }
  1328. if (corners[k].y < scrMin.y) {
  1329. scrMin.y = corners[k].y;
  1330. }
  1331. if (corners[k].x > scrMax.x) {
  1332. scrMax.x = corners[k].x;
  1333. }
  1334. if (corners[k].y > scrMax.y) {
  1335. scrMax.y = corners[k].y;
  1336. }
  1337. if (corners[k].z < distanceMin) {
  1338. distanceMin = corners[k].z;
  1339. if (distanceMin < nearClipPlane) {
  1340. distanceMin = distanceMax = 0.01f + nearClipPlane;
  1341. scrMin.x = scrMin.y = 0;
  1342. scrMax.x = 1f;
  1343. scrMax.y = 1f;
  1344. break;
  1345. }
  1346. }
  1347. if (corners[k].z > distanceMax) {
  1348. distanceMax = corners[k].z;
  1349. }
  1350. }
  1351. if (scrMax.y == scrMin.y)
  1352. return false;
  1353. int pixelWidth = cam.pixelWidth;
  1354. int pixelHeight = cam.pixelHeight;
  1355. scrMin.x *= pixelWidth;
  1356. scrMax.x *= pixelWidth;
  1357. scrMin.y *= pixelHeight;
  1358. scrMax.y *= pixelHeight;
  1359. scrMin.x += cam.pixelRect.xMin;
  1360. scrMax.x += cam.pixelRect.xMin;
  1361. scrMin.y += cam.pixelRect.yMin;
  1362. scrMax.y += cam.pixelRect.yMin;
  1363. if (spriteMode) {
  1364. scrMin.z = scrMax.z = (distanceMin + distanceMax) * 0.5f + nearClipPlane;
  1365. } else {
  1366. scrMin.z = scrMax.z = isVREnabled ? distanceMin : 0.05f + nearClipPlane; // small shift to avoid origin far issues
  1367. }
  1368. if (outline > 0) {
  1369. BuildMatrix(cam, scrMin, scrMax, (int)(10 + 20 * outlineWidth + 5 * outlineDownsampling), ref quadOutlineMatrix, ref shift);
  1370. }
  1371. if (glow > 0) {
  1372. BuildMatrix(cam, scrMin, scrMax, (int)(20 + 30 * glowWidth + 10 * glowDownsampling), ref quadGlowMatrix, ref shift);
  1373. }
  1374. return true;
  1375. }
  1376. void BuildMatrix(Camera cam, Vector3 scrMin, Vector3 scrMax, int border, ref Matrix4x4 quadMatrix, ref Vector3 shift) {
  1377. // Insert padding to make room for effects
  1378. scrMin.x -= border;
  1379. scrMin.y -= border;
  1380. scrMax.x += border;
  1381. scrMax.y += border;
  1382. // Back to world space
  1383. Vector3 third = new Vector3(scrMax.x, scrMin.y, scrMin.z);
  1384. scrMin = cam.ScreenToWorldPoint(scrMin);
  1385. scrMax = cam.ScreenToWorldPoint(scrMax);
  1386. third = cam.ScreenToWorldPoint(third);
  1387. float width = Vector3.Distance(scrMin, third);
  1388. float height = Vector3.Distance(scrMax, third);
  1389. quadMatrix = Matrix4x4.TRS((scrMin + scrMax) * 0.5f + shift, cam.transform.rotation, new Vector3(width, height, 1f));
  1390. }
  1391. void SmoothGlow(int rtWidth, int rtHeight) {
  1392. Material matBlur = fxMatBlurGlow;
  1393. RenderTextureDescriptor glowDesc = sourceDesc;
  1394. glowDesc.depthBufferBits = 0;
  1395. if (glowBlurMethod == BlurMethod.Gaussian) {
  1396. const int blurPasses = 4;
  1397. // Blur buffers
  1398. int bufferCount = blurPasses * 2;
  1399. if (mipGlowBuffers == null || mipGlowBuffers.Length != bufferCount) {
  1400. mipGlowBuffers = new int[bufferCount];
  1401. for (int k = 0; k < bufferCount; k++) {
  1402. mipGlowBuffers[k] = Shader.PropertyToID("_HPSmoothGlowTemp" + k);
  1403. }
  1404. mipGlowBuffers[bufferCount - 2] = ShaderParams.GlowRT;
  1405. }
  1406. for (int k = 0; k < bufferCount; k++) {
  1407. float reduction = k / 2 + 2;
  1408. int reducedWidth = (int)(rtWidth / reduction);
  1409. int reducedHeight = (int)(rtHeight / reduction);
  1410. if (reducedWidth <= 0) {
  1411. reducedWidth = 1;
  1412. }
  1413. if (reducedHeight <= 0) {
  1414. reducedHeight = 1;
  1415. }
  1416. glowDesc.width = reducedWidth;
  1417. glowDesc.height = reducedHeight;
  1418. cbHighlight.GetTemporaryRT(mipGlowBuffers[k], glowDesc, FilterMode.Bilinear);
  1419. }
  1420. for (int k = 0; k < bufferCount - 1; k += 2) {
  1421. if (k == 0) {
  1422. FullScreenBlit(cbHighlight, sourceRT, mipGlowBuffers[k + 1], fxMatBlurGlow, 0);
  1423. } else {
  1424. FullScreenBlit(cbHighlight, mipGlowBuffers[k], mipGlowBuffers[k + 1], fxMatBlurGlow, 0);
  1425. }
  1426. FullScreenBlit(cbHighlight, mipGlowBuffers[k + 1], mipGlowBuffers[k], fxMatBlurGlow, 1);
  1427. if (k < bufferCount - 2) {
  1428. FullScreenBlit(cbHighlight, mipGlowBuffers[k], mipGlowBuffers[k + 2], fxMatBlurGlow, 2);
  1429. }
  1430. }
  1431. } else {
  1432. const int blurPasses = 4;
  1433. int bufferCount = blurPasses;
  1434. if (mipGlowBuffers == null || mipGlowBuffers.Length != bufferCount) {
  1435. mipGlowBuffers = new int[bufferCount];
  1436. for (int k = 0; k < bufferCount - 1; k++) {
  1437. mipGlowBuffers[k] = Shader.PropertyToID("_HPSmoothGlowTemp" + k);
  1438. }
  1439. mipGlowBuffers[bufferCount - 1] = ShaderParams.GlowRT;
  1440. }
  1441. for (int k = 0; k < bufferCount; k++) {
  1442. float reduction = k + 2;
  1443. int reducedWidth = (int)(rtWidth / reduction);
  1444. int reducedHeight = (int)(rtHeight / reduction);
  1445. if (reducedWidth <= 0) {
  1446. reducedWidth = 1;
  1447. }
  1448. if (reducedHeight <= 0) {
  1449. reducedHeight = 1;
  1450. }
  1451. glowDesc.width = reducedWidth;
  1452. glowDesc.height = reducedHeight;
  1453. cbHighlight.GetTemporaryRT(mipGlowBuffers[k], glowDesc, FilterMode.Bilinear);
  1454. }
  1455. FullScreenBlit(cbHighlight, sourceRT, mipGlowBuffers[0], matBlur, 3);
  1456. for (int k = 0; k < bufferCount - 1; k++) {
  1457. cbHighlight.SetGlobalFloat(ShaderParams.ResampleScale, k + 0.5f);
  1458. FullScreenBlit(cbHighlight, mipGlowBuffers[k], mipGlowBuffers[k + 1], matBlur, 3);
  1459. }
  1460. }
  1461. }
  1462. void SmoothOutline(int rtWidth, int rtHeight) {
  1463. // Blur buffers
  1464. int bufferCount = outlineBlurPasses * 2;
  1465. if (mipOutlineBuffers == null || mipOutlineBuffers.Length != bufferCount) {
  1466. mipOutlineBuffers = new int[bufferCount];
  1467. for (int k = 0; k < bufferCount; k++) {
  1468. mipOutlineBuffers[k] = Shader.PropertyToID("_HPSmoothOutlineTemp" + k);
  1469. }
  1470. mipOutlineBuffers[bufferCount - 2] = ShaderParams.OutlineRT;
  1471. }
  1472. RenderTextureDescriptor outlineDesc = sourceDesc;
  1473. outlineDesc.depthBufferBits = 0;
  1474. for (int k = 0; k < bufferCount; k++) {
  1475. float reduction = k / 2 + 2;
  1476. int reducedWidth = (int)(rtWidth / reduction);
  1477. int reducedHeight = (int)(rtHeight / reduction);
  1478. if (reducedWidth <= 0) {
  1479. reducedWidth = 1;
  1480. }
  1481. if (reducedHeight <= 0) {
  1482. reducedHeight = 1;
  1483. }
  1484. outlineDesc.width = reducedWidth;
  1485. outlineDesc.height = reducedHeight;
  1486. cbHighlight.GetTemporaryRT(mipOutlineBuffers[k], outlineDesc, FilterMode.Bilinear);
  1487. }
  1488. for (int k = 0; k < bufferCount - 1; k += 2) {
  1489. if (k == 0) {
  1490. FullScreenBlit(cbHighlight, sourceRT, mipOutlineBuffers[k + 1], fxMatBlurOutline, 3);
  1491. } else {
  1492. FullScreenBlit(cbHighlight, mipOutlineBuffers[k], mipOutlineBuffers[k + 1], fxMatBlurOutline, 0);
  1493. }
  1494. FullScreenBlit(cbHighlight, mipOutlineBuffers[k + 1], mipOutlineBuffers[k], fxMatBlurOutline, 1);
  1495. if (k < bufferCount - 2) {
  1496. FullScreenBlit(cbHighlight, mipOutlineBuffers[k], mipOutlineBuffers[k + 2], fxMatBlurOutline, 2);
  1497. }
  1498. }
  1499. }
  1500. void ComposeSmoothBlend(Visibility smoothGlowVisibility, Visibility smoothOutlineVisibility) {
  1501. // Render mask on target surface
  1502. //if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.IPhonePlayer) { // TODO: RML
  1503. cbHighlight.SetRenderTarget(colorAttachmentBuffer, depthAttachmentBuffer);
  1504. //} else {
  1505. // cbHighlight.SetRenderTarget(colorAttachmentBuffer);
  1506. //}
  1507. bool renderSmoothGlow = glow > 0 && glowWidth > 0 && glowQuality == QualityLevel.Highest;
  1508. if (renderSmoothGlow) {
  1509. fxMatComposeGlow.SetVector(ShaderParams.Flip, (isVREnabled && flipY) ? new Vector4(1, -1) : new Vector4(0, 1));
  1510. fxMatComposeGlow.SetInt(ShaderParams.ZTest, GetZTestValue(smoothGlowVisibility));
  1511. fxMatComposeGlow.SetColor(ShaderParams.Debug, glowBlitDebug ? debugColor : blackColor);
  1512. fxMatComposeGlow.SetInt(ShaderParams.GlowStencilComp, glowIgnoreMask ? (int)CompareFunction.Always : (int)CompareFunction.NotEqual);
  1513. cbHighlight.DrawMesh(quadMesh, quadGlowMatrix, fxMatComposeGlow, 0, 0);
  1514. }
  1515. bool renderSmoothOutline = outline > 0 && outlineWidth > 0 && outlineQuality == QualityLevel.Highest;
  1516. if (renderSmoothOutline) {
  1517. fxMatComposeOutline.SetVector(ShaderParams.Flip, (isVREnabled && flipY) ? new Vector4(1, -1) : new Vector4(0, 1));
  1518. fxMatComposeOutline.SetInt(ShaderParams.ZTest, GetZTestValue(smoothOutlineVisibility));
  1519. cbHighlight.DrawMesh(quadMesh, quadOutlineMatrix, fxMatComposeOutline, 0, 0);
  1520. }
  1521. // Release render textures
  1522. if (renderSmoothGlow) {
  1523. for (int k = 0; k < mipGlowBuffers.Length; k++) {
  1524. cbHighlight.ReleaseTemporaryRT(mipGlowBuffers[k]);
  1525. }
  1526. }
  1527. if (renderSmoothOutline) {
  1528. for (int k = 0; k < mipOutlineBuffers.Length; k++) {
  1529. cbHighlight.ReleaseTemporaryRT(mipOutlineBuffers[k]);
  1530. }
  1531. }
  1532. cbHighlight.ReleaseTemporaryRT(sourceRT);
  1533. }
  1534. void InitMaterial(ref Material material, string shaderName) {
  1535. if (material != null) return;
  1536. Shader shaderFX = Shader.Find(shaderName);
  1537. if (shaderFX == null) {
  1538. Debug.LogError("Shader " + shaderName + " not found.");
  1539. return;
  1540. }
  1541. material = new Material(shaderFX);
  1542. }
  1543. void Fork(Material mat, ref Material[] mats, Mesh mesh) {
  1544. if (mesh == null)
  1545. return;
  1546. int count = mesh.subMeshCount;
  1547. Fork(mat, ref mats, count);
  1548. }
  1549. void Fork(Material material, ref Material[] array, int count) {
  1550. if (array == null || array.Length < count) {
  1551. DestroyMaterialArray(array);
  1552. array = new Material[count];
  1553. }
  1554. for (int k = 0; k < count; k++) {
  1555. if (array[k] == null) {
  1556. array[k] = Instantiate(material);
  1557. }
  1558. }
  1559. }
  1560. /// <summary>
  1561. /// Sets target for highlight effects
  1562. /// </summary>
  1563. public void SetTarget(Transform transform) {
  1564. if (transform == null) return;
  1565. if (transform != target) {
  1566. if (_highlighted) {
  1567. ImmediateFadeOut();
  1568. }
  1569. target = transform;
  1570. SetupMaterial();
  1571. } else {
  1572. UpdateVisibilityState();
  1573. }
  1574. }
  1575. /// <summary>
  1576. /// Sets target for highlight effects and also specify a list of renderers to be included as well
  1577. /// </summary>
  1578. public void SetTargets(Transform transform, Renderer[] renderers) {
  1579. if (transform == null)
  1580. return;
  1581. if (_highlighted) {
  1582. ImmediateFadeOut();
  1583. }
  1584. effectGroup = TargetOptions.Scripting;
  1585. target = transform;
  1586. SetupMaterial(renderers);
  1587. }
  1588. /// <summary>
  1589. /// Start or finish highlight on the object
  1590. /// </summary>
  1591. public void SetHighlighted(bool state) {
  1592. if (!Application.isPlaying) {
  1593. _highlighted = state;
  1594. return;
  1595. }
  1596. float now = Time.time;
  1597. if (fading == FadingState.NoFading) {
  1598. fadeStartTime = now;
  1599. }
  1600. if (state && !ignore) {
  1601. if (_highlighted && fading == FadingState.NoFading) {
  1602. return;
  1603. }
  1604. if (OnObjectHighlightStart != null) {
  1605. if (!OnObjectHighlightStart(gameObject)) {
  1606. return;
  1607. }
  1608. }
  1609. SendMessage("HighlightStart", null, SendMessageOptions.DontRequireReceiver);
  1610. highlightStartTime = targetFxStartTime = now;
  1611. if (fadeInDuration > 0) {
  1612. if (fading == FadingState.FadingOut) {
  1613. float remaining = fadeOutDuration - (now - fadeStartTime);
  1614. fadeStartTime = now - remaining;
  1615. fadeStartTime = Mathf.Min(fadeStartTime, now);
  1616. }
  1617. fading = FadingState.FadingIn;
  1618. } else {
  1619. fading = FadingState.NoFading;
  1620. }
  1621. _highlighted = true;
  1622. requireUpdateMaterial = true;
  1623. } else if (_highlighted) {
  1624. if (fadeOutDuration > 0) {
  1625. if (fading == FadingState.FadingIn) {
  1626. float elapsed = now - fadeStartTime;
  1627. fadeStartTime = now + elapsed - fadeInDuration;
  1628. fadeStartTime = Mathf.Min(fadeStartTime, now);
  1629. }
  1630. fading = FadingState.FadingOut; // when fade out ends, highlighted will be set to false in OnRenderObject
  1631. } else {
  1632. fading = FadingState.NoFading;
  1633. ImmediateFadeOut();
  1634. requireUpdateMaterial = true;
  1635. }
  1636. }
  1637. }
  1638. void ImmediateFadeOut() {
  1639. fading = FadingState.NoFading;
  1640. _highlighted = false;
  1641. if (OnObjectHighlightEnd != null) {
  1642. OnObjectHighlightEnd(gameObject);
  1643. }
  1644. SendMessage("HighlightEnd", null, SendMessageOptions.DontRequireReceiver);
  1645. }
  1646. void SetupMaterial() {
  1647. #if UNITY_EDITOR
  1648. staticChildren = false;
  1649. #endif
  1650. if (target == null || fxMatMask == null)
  1651. return;
  1652. Renderer[] rr = null;
  1653. switch (effectGroup) {
  1654. case TargetOptions.OnlyThisObject:
  1655. Renderer renderer = target.GetComponent<Renderer>();
  1656. if (renderer != null && ValidRenderer(renderer)) {
  1657. rr = new Renderer[1];
  1658. rr[0] = renderer;
  1659. }
  1660. break;
  1661. case TargetOptions.RootToChildren:
  1662. Transform root = target;
  1663. while (root.parent != null) {
  1664. root = root.parent;
  1665. }
  1666. rr = FindRenderersInChildren(root);
  1667. break;
  1668. case TargetOptions.LayerInScene: {
  1669. HighlightEffect eg = this;
  1670. if (target != transform) {
  1671. HighlightEffect targetEffect = target.GetComponent<HighlightEffect>();
  1672. if (targetEffect != null) {
  1673. eg = targetEffect;
  1674. }
  1675. }
  1676. rr = FindRenderersWithLayerInScene(eg.effectGroupLayer);
  1677. }
  1678. break;
  1679. case TargetOptions.LayerInChildren: {
  1680. HighlightEffect eg = this;
  1681. if (target != transform) {
  1682. HighlightEffect targetEffect = target.GetComponent<HighlightEffect>();
  1683. if (targetEffect != null) {
  1684. eg = targetEffect;
  1685. }
  1686. }
  1687. rr = FindRenderersWithLayerInChildren(eg.effectGroupLayer);
  1688. }
  1689. break;
  1690. case TargetOptions.Children:
  1691. rr = FindRenderersInChildren(target);
  1692. break;
  1693. case TargetOptions.Scripting:
  1694. if (rmsCount > 0) return;
  1695. return;
  1696. }
  1697. SetupMaterial(rr);
  1698. }
  1699. void SetupMaterial(Renderer[] rr) {
  1700. if (rr == null) {
  1701. rr = new Renderer[0];
  1702. }
  1703. if (rms == null || rms.Length < rr.Length) {
  1704. rms = new ModelMaterials[rr.Length];
  1705. }
  1706. InitCommandBuffer();
  1707. spriteMode = false;
  1708. rmsCount = 0;
  1709. for (int k = 0; k < rr.Length; k++) {
  1710. rms[rmsCount].Init();
  1711. Renderer renderer = rr[k];
  1712. if (renderer == null) continue;
  1713. if (effectGroup != TargetOptions.OnlyThisObject && !string.IsNullOrEmpty(effectNameFilter)) {
  1714. if (!renderer.name.Contains(effectNameFilter)) continue;
  1715. }
  1716. rms[rmsCount].renderer = renderer;
  1717. rms[rmsCount].renderWasVisibleDuringSetup = renderer.isVisible;
  1718. sortingOffset = (renderer.bounds.size.x + renderer.bounds.size.y + renderer.bounds.size.z) % 0.0001f;
  1719. if (renderer.transform != target) {
  1720. HighlightEffect otherEffect = renderer.GetComponent<HighlightEffect>();
  1721. if (otherEffect != null && otherEffect.enabled && otherEffect.ignore) {
  1722. continue; // ignore this object
  1723. }
  1724. }
  1725. if (OnRendererHighlightStart != null) {
  1726. if (!OnRendererHighlightStart(renderer)) {
  1727. rmsCount++;
  1728. continue;
  1729. }
  1730. }
  1731. rms[rmsCount].isCombined = false;
  1732. bool isSkinnedMesh = renderer is SkinnedMeshRenderer;
  1733. rms[rmsCount].isSkinnedMesh = isSkinnedMesh;
  1734. bool isSpriteRenderer = renderer is SpriteRenderer;
  1735. rms[rmsCount].normalsOption = isSkinnedMesh ? NormalsOption.PreserveOriginal : normalsOption;
  1736. if (isSpriteRenderer) {
  1737. rms[rmsCount].mesh = quadMesh;
  1738. spriteMode = true;
  1739. } else if (isSkinnedMesh) {
  1740. // ignore cloth skinned renderers
  1741. rms[rmsCount].mesh = ((SkinnedMeshRenderer)renderer).sharedMesh;
  1742. } else if (Application.isPlaying && renderer.isPartOfStaticBatch) {
  1743. // static batched objects need to have a mesh collider in order to use its original mesh
  1744. MeshCollider mc = renderer.GetComponent<MeshCollider>();
  1745. if (mc != null) {
  1746. rms[rmsCount].mesh = mc.sharedMesh;
  1747. }
  1748. } else {
  1749. MeshFilter mf = renderer.GetComponent<MeshFilter>();
  1750. if (mf != null) {
  1751. rms[rmsCount].mesh = mf.sharedMesh;
  1752. #if UNITY_EDITOR
  1753. if (renderer.gameObject.isStatic && renderer.GetComponent<MeshCollider>() == null) {
  1754. staticChildren = true;
  1755. }
  1756. #endif
  1757. }
  1758. }
  1759. if (rms[rmsCount].mesh == null) {
  1760. continue;
  1761. }
  1762. rms[rmsCount].transform = renderer.transform;
  1763. Fork(fxMatMask, ref rms[rmsCount].fxMatMask, rms[rmsCount].mesh);
  1764. Fork(fxMatOutlineTemplate, ref rms[rmsCount].fxMatOutline, rms[rmsCount].mesh);
  1765. Fork(fxMatGlowTemplate, ref rms[rmsCount].fxMatGlow, rms[rmsCount].mesh);
  1766. Fork(fxMatSeeThrough, ref rms[rmsCount].fxMatSeeThroughInner, rms[rmsCount].mesh);
  1767. Fork(fxMatSeeThroughBorder, ref rms[rmsCount].fxMatSeeThroughBorder, rms[rmsCount].mesh);
  1768. Fork(fxMatOverlay, ref rms[rmsCount].fxMatOverlay, rms[rmsCount].mesh);
  1769. Fork(fxMatInnerGlow, ref rms[rmsCount].fxMatInnerGlow, rms[rmsCount].mesh);
  1770. Fork(fxMatSolidColor, ref rms[rmsCount].fxMatSolidColor, rms[rmsCount].mesh);
  1771. rms[rmsCount].originalMesh = rms[rmsCount].mesh;
  1772. if (!rms[rmsCount].preserveOriginalMesh) {
  1773. if (innerGlow > 0 || (glow > 0 && glowQuality != QualityLevel.Highest) || (outline > 0 && outlineQuality != QualityLevel.Highest)) {
  1774. if (normalsOption == NormalsOption.Reorient) {
  1775. ReorientNormals(rmsCount);
  1776. } else {
  1777. AverageNormals(rmsCount);
  1778. }
  1779. }
  1780. }
  1781. rmsCount++;
  1782. }
  1783. #if UNITY_EDITOR
  1784. // Avoids command buffer issue when refreshing asset inside the Editor
  1785. if (!Application.isPlaying) {
  1786. mipGlowBuffers = null;
  1787. mipOutlineBuffers = null;
  1788. }
  1789. #endif
  1790. if (spriteMode) {
  1791. outlineIndependent = false;
  1792. outlineQuality = QualityLevel.Highest;
  1793. glowQuality = QualityLevel.Highest;
  1794. innerGlow = 0;
  1795. cullBackFaces = false;
  1796. seeThrough = SeeThroughMode.Never;
  1797. if (alphaCutOff <= 0) {
  1798. alphaCutOff = 0.5f;
  1799. }
  1800. } else {
  1801. if (combineMeshes) {
  1802. CombineMeshes();
  1803. }
  1804. }
  1805. UpdateMaterialProperties();
  1806. }
  1807. readonly List<Renderer> tempRR = new List<Renderer>();
  1808. bool ValidRenderer(Renderer r) {
  1809. return r is MeshRenderer || r is SpriteRenderer || r is SkinnedMeshRenderer;
  1810. }
  1811. Renderer[] FindRenderersWithLayerInScene(LayerMask layer) {
  1812. Renderer[] rr = FindObjectsOfType<Renderer>();
  1813. tempRR.Clear();
  1814. for (var i = 0; i < rr.Length; i++) {
  1815. Renderer r = rr[i];
  1816. if (((1 << r.gameObject.layer) & layer) != 0) {
  1817. if (ValidRenderer(r)) {
  1818. tempRR.Add(r);
  1819. }
  1820. }
  1821. }
  1822. return tempRR.ToArray();
  1823. }
  1824. Renderer[] FindRenderersWithLayerInChildren(LayerMask layer) {
  1825. Renderer[] rr = target.GetComponentsInChildren<Renderer>();
  1826. tempRR.Clear();
  1827. for (var i = 0; i < rr.Length; i++) {
  1828. Renderer r = rr[i];
  1829. if (((1 << r.gameObject.layer) & layer) != 0) {
  1830. if (ValidRenderer(r)) {
  1831. tempRR.Add(r);
  1832. }
  1833. }
  1834. }
  1835. return tempRR.ToArray();
  1836. }
  1837. Renderer[] FindRenderersInChildren(Transform parent) {
  1838. tempRR.Clear();
  1839. parent.GetComponentsInChildren(tempRR);
  1840. for (var i = 0; i < tempRR.Count; i++) {
  1841. Renderer r = tempRR[i];
  1842. if (!ValidRenderer(r)) {
  1843. tempRR.RemoveAt(i);
  1844. i--;
  1845. }
  1846. }
  1847. return tempRR.ToArray();
  1848. }
  1849. void CheckGeometrySupportDependencies() {
  1850. InitMaterial(ref fxMatMask, "HighlightPlus/Geometry/Mask");
  1851. InitMaterial(ref fxMatGlowTemplate, "HighlightPlus/Geometry/Glow");
  1852. if (fxMatGlowTemplate != null && useGPUInstancing) fxMatGlowTemplate.enableInstancing = true;
  1853. InitMaterial(ref fxMatInnerGlow, "HighlightPlus/Geometry/InnerGlow");
  1854. InitMaterial(ref fxMatOutlineTemplate, "HighlightPlus/Geometry/Outline");
  1855. if (fxMatOutlineTemplate != null && useGPUInstancing) fxMatOutlineTemplate.enableInstancing = true;
  1856. InitMaterial(ref fxMatOverlay, "HighlightPlus/Geometry/Overlay");
  1857. InitMaterial(ref fxMatSeeThrough, "HighlightPlus/Geometry/SeeThrough");
  1858. InitMaterial(ref fxMatSeeThroughBorder, "HighlightPlus/Geometry/SeeThroughBorder");
  1859. InitMaterial(ref fxMatSeeThroughMask, "HighlightPlus/Geometry/SeeThroughMask");
  1860. InitMaterial(ref fxMatTarget, "HighlightPlus/Geometry/Target");
  1861. InitMaterial(ref fxMatComposeGlow, "HighlightPlus/Geometry/ComposeGlow");
  1862. InitMaterial(ref fxMatComposeOutline, "HighlightPlus/Geometry/ComposeOutline");
  1863. InitMaterial(ref fxMatSolidColor, "HighlightPlus/Geometry/SolidColor");
  1864. InitMaterial(ref fxMatBlurGlow, "HighlightPlus/Geometry/BlurGlow");
  1865. InitMaterial(ref fxMatBlurOutline, "HighlightPlus/Geometry/BlurOutline");
  1866. InitMaterial(ref fxMatClearStencil, "HighlightPlus/ClearStencil");
  1867. }
  1868. void InitCommandBuffer() {
  1869. if (cbHighlight == null) {
  1870. cbHighlight = new CommandBuffer();
  1871. cbHighlight.name = "Highlight Plus for " + name;
  1872. }
  1873. cbHighlightEmpty = true;
  1874. }
  1875. void ConfigureOutput() {
  1876. if (!cbHighlightEmpty) return;
  1877. cbHighlightEmpty = false;
  1878. cbHighlight.Clear();
  1879. #if UNITY_2022_1_OR_NEWER
  1880. // depth priming might have changed depth render target so we ensure it's set to normal
  1881. colorAttachmentBuffer = new RenderTargetIdentifier(colorAttachmentBuffer, 0, CubemapFace.Unknown, -1);
  1882. depthAttachmentBuffer = new RenderTargetIdentifier(depthAttachmentBuffer, 0, CubemapFace.Unknown, -1);
  1883. cbHighlight.SetRenderTarget(colorAttachmentBuffer, depthAttachmentBuffer);
  1884. #elif UNITY_2021_2_OR_NEWER
  1885. var useDepthRenderBuffer = colorAttachmentBuffer != BuiltinRenderTextureType.CameraTarget && depthAttachmentBuffer == BuiltinRenderTextureType.CameraTarget;
  1886. cbHighlight.SetRenderTarget(colorAttachmentBuffer, useDepthRenderBuffer ? colorAttachmentBuffer : depthAttachmentBuffer);
  1887. #endif
  1888. }
  1889. public void UpdateVisibilityState() {
  1890. // isVisible only accounts for cases where there's a single object managed by the highlight effect
  1891. // and that object can receive OnBecameVisible/OnBecameInvisible. Otherwise we can't use this optimization
  1892. if (rms == null || rms.Length != 1 || rms[0].transform != transform || rms[0].renderer == null) {
  1893. isVisible = true;
  1894. } else if (rms[0].renderer != null) {
  1895. isVisible = rms[0].renderer.isVisible;
  1896. }
  1897. }
  1898. public void UpdateMaterialProperties() {
  1899. if (rms == null)
  1900. return;
  1901. if (ignore) {
  1902. _highlighted = false;
  1903. }
  1904. UpdateVisibilityState();
  1905. maskRequired = (_highlighted && (outline > 0 || (glow > 0 && !glowIgnoreMask))) || seeThrough != SeeThroughMode.Never || (targetFX && targetFXAlignToGround);
  1906. usesSeeThrough = seeThroughIntensity > 0 && (seeThrough == SeeThroughMode.AlwaysWhenOccluded || (seeThrough == SeeThroughMode.WhenHighlighted && _highlighted));
  1907. // Resort materials if needed
  1908. if (usesSeeThrough && seeThroughChildrenSortingMode != SeeThroughSortingMode.Default && rms.Length > 0) {
  1909. if (seeThroughChildrenSortingMode == SeeThroughSortingMode.SortByMaterialsRenderQueue) {
  1910. Array.Sort(rms, MaterialsRenderQueueComparer);
  1911. } else {
  1912. Array.Sort(rms, MaterialsRenderQueueInvertedComparer);
  1913. }
  1914. }
  1915. Color seeThroughTintColor = this.seeThroughTintColor;
  1916. seeThroughTintColor.a = this.seeThroughTintAlpha;
  1917. if (lastOutlineVisibility != outlineVisibility) {
  1918. // change by scripting?
  1919. if (glowQuality == QualityLevel.Highest && outlineQuality == QualityLevel.Highest) {
  1920. glowVisibility = outlineVisibility;
  1921. }
  1922. lastOutlineVisibility = outlineVisibility;
  1923. }
  1924. if (outlineWidth < 0) {
  1925. outlineWidth = 0;
  1926. }
  1927. if (outlineQuality == QualityLevel.Medium) {
  1928. outlineOffsetsMin = 4; outlineOffsetsMax = 7;
  1929. } else if (outlineQuality == QualityLevel.High) {
  1930. outlineOffsetsMin = 0; outlineOffsetsMax = 7;
  1931. } else {
  1932. outlineOffsetsMin = outlineOffsetsMax = 0;
  1933. }
  1934. if (glowWidth < 0) {
  1935. glowWidth = 0;
  1936. }
  1937. if (glowQuality == QualityLevel.Medium) {
  1938. glowOffsetsMin = 4; glowOffsetsMax = 7;
  1939. } else if (glowQuality == QualityLevel.High) {
  1940. glowOffsetsMin = 0; glowOffsetsMax = 7;
  1941. } else {
  1942. glowOffsetsMin = glowOffsetsMax = 0;
  1943. }
  1944. if (targetFXTransitionDuration <= 0) {
  1945. targetFXTransitionDuration = 0.0001f;
  1946. }
  1947. if (targetFXStayDuration <= 0) {
  1948. targetFXStayDuration = 0;
  1949. }
  1950. if (targetFXFadePower <= 0) {
  1951. targetFXFadePower = 0;
  1952. }
  1953. if (seeThroughDepthOffset < 0) {
  1954. seeThroughDepthOffset = 0;
  1955. }
  1956. if (seeThroughMaxDepth < 0) {
  1957. seeThroughMaxDepth = 0;
  1958. }
  1959. if (seeThroughBorderWidth < 0) {
  1960. seeThroughBorderWidth = 0;
  1961. }
  1962. shouldBakeSkinnedMesh = optimizeSkinnedMesh && ((outline > 0 && outlineQuality != QualityLevel.Highest) || (glow > 0 && glowQuality != QualityLevel.Highest));
  1963. useSmoothGlow = glow > 0 && glowWidth > 0 && glowQuality == QualityLevel.Highest;
  1964. useSmoothOutline = outline > 0 && outlineWidth > 0 && outlineQuality == QualityLevel.Highest;
  1965. useSmoothBlend = useSmoothGlow || useSmoothOutline;
  1966. if (useSmoothBlend) {
  1967. if (useSmoothGlow && useSmoothOutline) {
  1968. outlineVisibility = glowVisibility;
  1969. }
  1970. outlineEdgeThreshold = Mathf.Clamp01(outlineEdgeThreshold);
  1971. }
  1972. if (useSmoothGlow) {
  1973. fxMatComposeGlow.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  1974. if (glowBlendMode == GlowBlendMode.Additive) {
  1975. fxMatComposeGlow.SetInt(ShaderParams.BlendSrc, (int)BlendMode.One);
  1976. fxMatComposeGlow.SetInt(ShaderParams.BlendDst, (int)BlendMode.One);
  1977. } else {
  1978. fxMatComposeGlow.SetInt(ShaderParams.BlendSrc, (int)BlendMode.SrcAlpha);
  1979. fxMatComposeGlow.SetInt(ShaderParams.BlendDst, (int)BlendMode.OneMinusSrcAlpha);
  1980. }
  1981. fxMatBlurGlow.SetFloat(ShaderParams.BlurScale, glowWidth / glowDownsampling);
  1982. fxMatBlurGlow.SetFloat(ShaderParams.Speed, glowAnimationSpeed);
  1983. }
  1984. if (useSmoothOutline) {
  1985. fxMatComposeOutline.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  1986. fxMatComposeOutline.SetColor(ShaderParams.Debug, outlineBlitDebug ? debugColor : blackColor);
  1987. if (outlineEdgeMode == OutlineEdgeMode.Exterior) {
  1988. fxMatComposeOutline.SetInt(ShaderParams.OutlineStencilCompareFunction, (int)CompareFunction.NotEqual);
  1989. fxMatComposeOutline.DisableKeyword(ShaderParams.SKW_ALL_EDGES);
  1990. } else {
  1991. fxMatComposeOutline.SetInt(ShaderParams.OutlineStencilCompareFunction, (int)CompareFunction.Always);
  1992. fxMatComposeOutline.EnableKeyword(ShaderParams.SKW_ALL_EDGES);
  1993. outlineDownsampling = 1;
  1994. }
  1995. float edgeWidth = outlineWidth;
  1996. if (outlineEdgeMode == OutlineEdgeMode.Any) edgeWidth = Mathf.Clamp(edgeWidth, outlineBlurPasses / 5f, outlineBlurPasses);
  1997. fxMatBlurOutline.SetFloat(ShaderParams.BlurScale, edgeWidth / outlineDownsampling);
  1998. fxMatBlurOutline.SetFloat(ShaderParams.BlurScaleFirstHoriz, edgeWidth * 2f);
  1999. }
  2000. if (outlineColorStyle == ColorStyle.Gradient) {
  2001. const int OUTLINE_GRADIENT_TEX_SIZE = 32;
  2002. bool requiresUpdate = false;
  2003. if (outlineGradientTex == null) {
  2004. outlineGradientTex = new Texture2D(OUTLINE_GRADIENT_TEX_SIZE, 1, TextureFormat.RGBA32, mipChain: false, linear: true);
  2005. outlineGradientTex.wrapMode = TextureWrapMode.Clamp;
  2006. requiresUpdate = true;
  2007. }
  2008. if (outlineGradientColors == null || outlineGradientColors.Length != OUTLINE_GRADIENT_TEX_SIZE) {
  2009. outlineGradientColors = new Color[OUTLINE_GRADIENT_TEX_SIZE];
  2010. requiresUpdate = true;
  2011. }
  2012. for (int k = 0; k < OUTLINE_GRADIENT_TEX_SIZE; k++) {
  2013. float t = (float)k / OUTLINE_GRADIENT_TEX_SIZE;
  2014. Color color = outlineGradient.Evaluate(t);
  2015. if (color != outlineGradientColors[k]) {
  2016. outlineGradientColors[k] = color;
  2017. requiresUpdate = true;
  2018. }
  2019. }
  2020. if (requiresUpdate) {
  2021. outlineGradientTex.SetPixels(outlineGradientColors);
  2022. outlineGradientTex.Apply();
  2023. }
  2024. }
  2025. // Setup materials
  2026. // Target
  2027. if (targetFX) {
  2028. if (targetFXTexture == null) {
  2029. targetFXTexture = Resources.Load<Texture2D>("HighlightPlus/target");
  2030. }
  2031. fxMatTarget.mainTexture = targetFXTexture;
  2032. fxMatTarget.SetInt(ShaderParams.ZTest, GetZTestValue(targetFXVisibility));
  2033. }
  2034. // Per object
  2035. float scaledOutlineWidth = outlineQuality.UsesMultipleOffsets() ? 0f : outlineWidth / 100f;
  2036. for (int k = 0; k < rmsCount; k++) {
  2037. if (rms[k].mesh != null) {
  2038. Renderer renderer = rms[k].renderer;
  2039. if (renderer == null)
  2040. continue;
  2041. renderer.GetSharedMaterials(rendererSharedMaterials);
  2042. // Per submesh
  2043. for (int l = 0; l < rms[k].mesh.subMeshCount; l++) {
  2044. if (((1 << l) & subMeshMask) == 0) continue;
  2045. Material mat = null;
  2046. if (l < rendererSharedMaterials.Count) {
  2047. mat = rendererSharedMaterials[l];
  2048. }
  2049. if (mat == null)
  2050. continue;
  2051. bool hasTexture = false;
  2052. Texture matTexture = null;
  2053. Vector2 matTextureOffset = Vector2.zero;
  2054. Vector2 matTextureScale = Vector2.one;
  2055. if (mat.HasProperty(ShaderParams.MainTex)) {
  2056. matTexture = mat.GetTexture(ShaderParams.MainTex);
  2057. matTextureOffset = mat.mainTextureOffset;
  2058. matTextureScale = mat.mainTextureScale;
  2059. hasTexture = true;
  2060. } else if (mat.HasProperty(ShaderParams.BaseMap)) {
  2061. matTexture = mat.GetTexture(ShaderParams.BaseMap); // we don't use mainTexture because ShaderGraph doesn't inform that generic property correctly
  2062. hasTexture = true;
  2063. if (mat.HasProperty(ShaderParams.BaseMapST)) {
  2064. // mat.mainTextureOffset will raise an error in builds - we need to manually get the scale/offset from the material by its property name
  2065. Vector4 baseMapST = mat.GetVector(ShaderParams.BaseMapST);
  2066. matTextureScale.x = baseMapST.x;
  2067. matTextureScale.y = baseMapST.y;
  2068. matTextureOffset.x = baseMapST.z;
  2069. matTextureOffset.y = baseMapST.w;
  2070. }
  2071. }
  2072. bool useAlphaTest = alphaCutOff > 0 && hasTexture;
  2073. // Mask
  2074. if (rms[k].fxMatMask != null && rms[k].fxMatMask.Length > l) {
  2075. Material fxMat = rms[k].fxMatMask[l];
  2076. if (fxMat != null) {
  2077. //if (hasTexture) {
  2078. fxMat.mainTexture = matTexture;
  2079. fxMat.mainTextureOffset = matTextureOffset;
  2080. fxMat.mainTextureScale = matTextureScale;
  2081. //}
  2082. if (useAlphaTest) {
  2083. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2084. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2085. } else {
  2086. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2087. }
  2088. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2089. }
  2090. }
  2091. // Outline
  2092. if (rms[k].fxMatOutline != null && rms[k].fxMatOutline.Length > l) {
  2093. Material fxMat = rms[k].fxMatOutline[l];
  2094. if (fxMat != null) {
  2095. fxMat.SetFloat(ShaderParams.OutlineWidth, scaledOutlineWidth);
  2096. fxMat.SetInt(ShaderParams.OutlineZTest, GetZTestValue(outlineVisibility));
  2097. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2098. fxMat.SetFloat(ShaderParams.ConstantWidth, constantWidth ? 1.0f : 0);
  2099. if (useAlphaTest) {
  2100. fxMat.mainTexture = matTexture;
  2101. fxMat.mainTextureOffset = matTextureOffset;
  2102. fxMat.mainTextureScale = matTextureScale;
  2103. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2104. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2105. } else {
  2106. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2107. }
  2108. fxMat.DisableKeyword(ShaderParams.SKW_OUTLINE_GRADIENT_LS);
  2109. fxMat.DisableKeyword(ShaderParams.SKW_OUTLINE_GRADIENT_WS);
  2110. if (outlineColorStyle == ColorStyle.Gradient) {
  2111. fxMat.SetTexture(ShaderParams.OutlineGradientTex, outlineGradientTex);
  2112. fxMat.EnableKeyword(outlineGradientInLocalSpace ? ShaderParams.SKW_OUTLINE_GRADIENT_LS : ShaderParams.SKW_OUTLINE_GRADIENT_WS);
  2113. }
  2114. }
  2115. }
  2116. // Glow
  2117. if (rms[k].fxMatGlow != null && rms[k].fxMatGlow.Length > l) {
  2118. Material fxMat = rms[k].fxMatGlow[l];
  2119. if (fxMat != null) {
  2120. fxMat.SetVector(ShaderParams.Glow2, new Vector4(outline > 0 ? outlineWidth / 100f : 0, glowAnimationSpeed, glowDithering ? 0 : 1));
  2121. fxMat.SetInt(ShaderParams.GlowZTest, GetZTestValue(glowVisibility));
  2122. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2123. fxMat.SetFloat(ShaderParams.ConstantWidth, constantWidth ? 1.0f : 0);
  2124. fxMat.SetInt(ShaderParams.GlowStencilOp, glowBlendPasses ? (int)StencilOp.Keep : (int)StencilOp.Replace);
  2125. fxMat.SetInt(ShaderParams.GlowStencilComp, glowIgnoreMask ? (int)CompareFunction.Always : (int)CompareFunction.NotEqual);
  2126. if (useAlphaTest) {
  2127. fxMat.mainTexture = matTexture;
  2128. fxMat.mainTextureOffset = matTextureOffset;
  2129. fxMat.mainTextureScale = matTextureScale;
  2130. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2131. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2132. } else {
  2133. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2134. }
  2135. }
  2136. }
  2137. // See-through
  2138. bool usesSeeThroughBorder = rms[k].fxMatSeeThroughBorder != null && rms[k].fxMatSeeThroughBorder.Length > l && (seeThroughBorder * seeThroughBorderWidth > 0);
  2139. if (rms[k].fxMatSeeThroughInner != null && rms[k].fxMatSeeThroughInner.Length > l) {
  2140. Material fxMat = rms[k].fxMatSeeThroughInner[l];
  2141. if (fxMat != null) {
  2142. fxMat.SetFloat(ShaderParams.SeeThrough, seeThroughIntensity);
  2143. fxMat.SetFloat(ShaderParams.SeeThroughNoise, seeThroughNoise);
  2144. fxMat.SetColor(ShaderParams.SeeThroughTintColor, seeThroughTintColor);
  2145. if (seeThroughOccluderMaskAccurate && seeThroughOccluderMask != -1) {
  2146. fxMat.SetInt(ShaderParams.SeeThroughStencilRef, 1);
  2147. fxMat.SetInt(ShaderParams.SeeThroughStencilComp, (int)CompareFunction.Equal);
  2148. fxMat.SetInt(ShaderParams.SeeThroughStencilPassOp, (int)StencilOp.Zero);
  2149. } else {
  2150. fxMat.SetInt(ShaderParams.SeeThroughStencilRef, 2);
  2151. fxMat.SetInt(ShaderParams.SeeThroughStencilComp, (int)CompareFunction.Greater);
  2152. fxMat.SetInt(ShaderParams.SeeThroughStencilPassOp, (int)StencilOp.Replace);
  2153. }
  2154. //if (hasTexture) {
  2155. fxMat.mainTexture = matTexture;
  2156. fxMat.mainTextureOffset = matTextureOffset;
  2157. fxMat.mainTextureScale = matTextureScale;
  2158. //}
  2159. if (useAlphaTest) {
  2160. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2161. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2162. } else {
  2163. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2164. }
  2165. if (seeThroughDepthOffset > 0 || seeThroughMaxDepth > 0) {
  2166. fxMat.SetFloat(ShaderParams.SeeThroughDepthOffset, seeThroughDepthOffset > 0 ? seeThroughDepthOffset : -1);
  2167. fxMat.SetFloat(ShaderParams.SeeThroughMaxDepth, seeThroughMaxDepth > 0 ? seeThroughMaxDepth : 999999);
  2168. fxMat.EnableKeyword(ShaderParams.SKW_DEPTH_OFFSET);
  2169. } else {
  2170. fxMat.DisableKeyword(ShaderParams.SKW_DEPTH_OFFSET);
  2171. }
  2172. if (seeThroughBorderOnly) {
  2173. fxMat.EnableKeyword(ShaderParams.SKW_SEETHROUGH_ONLY_BORDER);
  2174. } else {
  2175. fxMat.DisableKeyword(ShaderParams.SKW_SEETHROUGH_ONLY_BORDER);
  2176. }
  2177. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_TRIPLANAR);
  2178. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_OBJECTSPACE);
  2179. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_SCREENSPACE);
  2180. if (seeThroughTexture != null) {
  2181. fxMat.SetTexture(ShaderParams.SeeThroughTexture, seeThroughTexture);
  2182. fxMat.SetFloat(ShaderParams.SeeThroughTextureScale, seeThroughTextureScale);
  2183. switch (seeThroughTextureUVSpace) {
  2184. case TextureUVSpace.ScreenSpace:
  2185. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_SCREENSPACE);
  2186. break;
  2187. case TextureUVSpace.ObjectSpace:
  2188. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_OBJECTSPACE);
  2189. break;
  2190. default:
  2191. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_TRIPLANAR);
  2192. break;
  2193. }
  2194. }
  2195. }
  2196. }
  2197. // See-through border
  2198. if (usesSeeThroughBorder) {
  2199. Material fxMat = rms[k].fxMatSeeThroughBorder[l];
  2200. if (fxMat != null) {
  2201. fxMat.SetColor(ShaderParams.SeeThroughBorderColor, new Color(seeThroughBorderColor.r, seeThroughBorderColor.g, seeThroughBorderColor.b, seeThroughBorder));
  2202. fxMat.SetFloat(ShaderParams.SeeThroughBorderWidth, (seeThroughBorder * seeThroughBorderWidth) > 0 ? seeThroughBorderWidth / 100f : 0);
  2203. fxMat.SetFloat(ShaderParams.SeeThroughBorderConstantWidth, constantWidth ? 1.0f : 0);
  2204. if (seeThroughOccluderMaskAccurate && seeThroughOccluderMask != -1) {
  2205. fxMat.SetInt(ShaderParams.SeeThroughStencilRef, 1);
  2206. fxMat.SetInt(ShaderParams.SeeThroughStencilComp, (int)CompareFunction.Equal);
  2207. fxMat.SetInt(ShaderParams.SeeThroughStencilPassOp, (int)StencilOp.Zero);
  2208. } else {
  2209. fxMat.SetInt(ShaderParams.SeeThroughStencilRef, 2);
  2210. fxMat.SetInt(ShaderParams.SeeThroughStencilComp, (int)CompareFunction.Greater);
  2211. fxMat.SetInt(ShaderParams.SeeThroughStencilPassOp, (int)StencilOp.Keep);
  2212. }
  2213. //if (hasTexture) {
  2214. fxMat.mainTexture = matTexture;
  2215. fxMat.mainTextureOffset = matTextureOffset;
  2216. fxMat.mainTextureScale = matTextureScale;
  2217. //}
  2218. if (useAlphaTest) {
  2219. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2220. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2221. } else {
  2222. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2223. }
  2224. if (seeThroughDepthOffset > 0 || seeThroughMaxDepth > 0) {
  2225. fxMat.SetFloat(ShaderParams.SeeThroughDepthOffset, seeThroughDepthOffset > 0 ? seeThroughDepthOffset : -1);
  2226. fxMat.SetFloat(ShaderParams.SeeThroughMaxDepth, seeThroughMaxDepth > 0 ? seeThroughMaxDepth : 999999);
  2227. fxMat.EnableKeyword(ShaderParams.SKW_DEPTH_OFFSET);
  2228. } else {
  2229. fxMat.DisableKeyword(ShaderParams.SKW_DEPTH_OFFSET);
  2230. }
  2231. }
  2232. }
  2233. // Overlay
  2234. if (rms[k].fxMatOverlay != null && rms[k].fxMatOverlay.Length > l) {
  2235. Material fxMat = rms[k].fxMatOverlay[l];
  2236. if (fxMat != null) {
  2237. //if (hasTexture) {
  2238. fxMat.mainTexture = matTexture;
  2239. fxMat.mainTextureOffset = matTextureOffset;
  2240. fxMat.mainTextureScale = matTextureScale;
  2241. //}
  2242. if (mat.HasProperty(ShaderParams.Color)) {
  2243. fxMat.SetColor(ShaderParams.OverlayBackColor, mat.GetColor(ShaderParams.Color));
  2244. }
  2245. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2246. fxMat.SetInt(ShaderParams.OverlayZTest, GetZTestValue(overlayVisibility));
  2247. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_TRIPLANAR);
  2248. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_OBJECTSPACE);
  2249. fxMat.DisableKeyword(ShaderParams.SKW_TEXTURE_SCREENSPACE);
  2250. if (overlayTexture != null) {
  2251. fxMat.SetTexture(ShaderParams.OverlayTexture, overlayTexture);
  2252. switch (overlayTextureUVSpace) {
  2253. case TextureUVSpace.ScreenSpace:
  2254. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_SCREENSPACE);
  2255. break;
  2256. case TextureUVSpace.ObjectSpace:
  2257. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_OBJECTSPACE);
  2258. break;
  2259. default:
  2260. fxMat.EnableKeyword(ShaderParams.SKW_TEXTURE_TRIPLANAR);
  2261. break;
  2262. }
  2263. }
  2264. if (useAlphaTest) {
  2265. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2266. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2267. } else {
  2268. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2269. }
  2270. }
  2271. }
  2272. // Inner Glow
  2273. if (rms[k].fxMatInnerGlow != null && rms[k].fxMatInnerGlow.Length > l) {
  2274. Material fxMat = rms[k].fxMatInnerGlow[l];
  2275. if (fxMat != null) {
  2276. //if (hasTexture) {
  2277. fxMat.mainTexture = matTexture;
  2278. fxMat.mainTextureOffset = matTextureOffset;
  2279. fxMat.mainTextureScale = matTextureScale;
  2280. //}
  2281. fxMat.SetFloat(ShaderParams.InnerGlowWidth, innerGlowWidth);
  2282. fxMat.SetInt(ShaderParams.InnerGlowZTest, GetZTestValue(innerGlowVisibility));
  2283. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2284. fxMat.SetInt(ShaderParams.InnerGlowBlendMode, innerGlowBlendMode == InnerGlowBlendMode.Additive ? 1 : 10);
  2285. if (useAlphaTest) {
  2286. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2287. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2288. } else {
  2289. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2290. }
  2291. }
  2292. }
  2293. // Solid Color for smooth glow
  2294. if (rms[k].fxMatSolidColor != null && rms[k].fxMatSolidColor.Length > l) {
  2295. Material fxMat = rms[k].fxMatSolidColor[l];
  2296. if (fxMat != null) {
  2297. fxMat.color = glowHQColor;
  2298. fxMat.SetInt(ShaderParams.Cull, cullBackFaces ? (int)CullMode.Back : (int)CullMode.Off);
  2299. fxMat.SetFloat(ShaderParams.OutlineEdgeThreshold, outlineEdgeThreshold);
  2300. //if (hasTexture) {
  2301. fxMat.mainTexture = matTexture;
  2302. fxMat.mainTextureOffset = matTextureOffset;
  2303. fxMat.mainTextureScale = matTextureScale;
  2304. //}
  2305. // if (!Application.isMobilePlatform) { // TODO: currently this does not work with URP on Android
  2306. if ((glow > 0 && glowQuality == QualityLevel.Highest && glowVisibility == Visibility.Normal) || (outline > 0 && outlineQuality == QualityLevel.Highest && outlineVisibility == Visibility.Normal)) {
  2307. fxMat.DisableKeyword(ShaderParams.SKW_DEPTHCLIP_INV);
  2308. fxMat.EnableKeyword(ShaderParams.SKW_DEPTHCLIP);
  2309. } else if ((glow > 0 && glowQuality == QualityLevel.Highest && glowVisibility == Visibility.OnlyWhenOccluded) || (outline > 0 && outlineQuality == QualityLevel.Highest && outlineVisibility == Visibility.OnlyWhenOccluded)) {
  2310. fxMat.DisableKeyword(ShaderParams.SKW_DEPTHCLIP);
  2311. fxMat.EnableKeyword(ShaderParams.SKW_DEPTHCLIP_INV);
  2312. } else {
  2313. fxMat.DisableKeyword(ShaderParams.SKW_DEPTHCLIP);
  2314. fxMat.DisableKeyword(ShaderParams.SKW_DEPTHCLIP_INV);
  2315. }
  2316. //}
  2317. if (useAlphaTest) {
  2318. fxMat.SetFloat(ShaderParams.CutOff, alphaCutOff);
  2319. fxMat.EnableKeyword(ShaderParams.SKW_ALPHACLIP);
  2320. } else {
  2321. fxMat.DisableKeyword(ShaderParams.SKW_ALPHACLIP);
  2322. }
  2323. if (outlineEdgeMode == OutlineEdgeMode.Any) {
  2324. fxMat.EnableKeyword(ShaderParams.SKW_ALL_EDGES);
  2325. } else {
  2326. fxMat.DisableKeyword(ShaderParams.SKW_ALL_EDGES);
  2327. }
  2328. }
  2329. }
  2330. }
  2331. }
  2332. }
  2333. }
  2334. int MaterialsRenderQueueComparer(ModelMaterials m1, ModelMaterials m2) {
  2335. Material mat1 = m1.renderer != null ? m1.renderer.sharedMaterial : null;
  2336. Material mat2 = m2.renderer != null ? m2.renderer.sharedMaterial : null;
  2337. int mq1 = mat1 != null ? mat1.renderQueue : 0;
  2338. int mq2 = mat2 != null ? mat2.renderQueue : 0;
  2339. return mq1.CompareTo(mq2);
  2340. }
  2341. int MaterialsRenderQueueInvertedComparer(ModelMaterials m1, ModelMaterials m2) {
  2342. Material mat1 = m1.renderer != null ? m1.renderer.sharedMaterial : null;
  2343. Material mat2 = m2.renderer != null ? m2.renderer.sharedMaterial : null;
  2344. int mq1 = mat1 != null ? mat1.renderQueue : 0;
  2345. int mq2 = mat2 != null ? mat2.renderQueue : 0;
  2346. return mq2.CompareTo(mq1);
  2347. }
  2348. float ComputeCameraDistanceFade(Vector3 position, Transform cameraTransform) {
  2349. Vector3 heading = position - cameraTransform.position;
  2350. float distance = Vector3.Dot(heading, cameraTransform.forward);
  2351. if (distance < cameraDistanceFadeNear) {
  2352. return 1f - Mathf.Min(1f, cameraDistanceFadeNear - distance);
  2353. }
  2354. if (distance > cameraDistanceFadeFar) {
  2355. return 1f - Mathf.Min(1f, distance - cameraDistanceFadeFar);
  2356. }
  2357. return 1f;
  2358. }
  2359. int GetZTestValue(Visibility param) {
  2360. switch (param) {
  2361. case Visibility.AlwaysOnTop:
  2362. return (int)CompareFunction.Always;
  2363. case Visibility.OnlyWhenOccluded:
  2364. return (int)CompareFunction.Greater;
  2365. default:
  2366. return (int)CompareFunction.LessEqual;
  2367. }
  2368. }
  2369. void BuildQuad() {
  2370. quadMesh = new Mesh();
  2371. // Setup vertices
  2372. Vector3[] newVertices = new Vector3[4];
  2373. float halfHeight = 0.5f;
  2374. float halfWidth = 0.5f;
  2375. newVertices[0] = new Vector3(-halfWidth, -halfHeight, 0);
  2376. newVertices[1] = new Vector3(-halfWidth, halfHeight, 0);
  2377. newVertices[2] = new Vector3(halfWidth, -halfHeight, 0);
  2378. newVertices[3] = new Vector3(halfWidth, halfHeight, 0);
  2379. // Setup UVs
  2380. Vector2[] newUVs = new Vector2[newVertices.Length];
  2381. newUVs[0] = new Vector2(0, 0);
  2382. newUVs[1] = new Vector2(0, 1);
  2383. newUVs[2] = new Vector2(1, 0);
  2384. newUVs[3] = new Vector2(1, 1);
  2385. // Setup triangles
  2386. int[] newTriangles = { 0, 1, 2, 3, 2, 1 };
  2387. // Setup normals
  2388. Vector3[] newNormals = new Vector3[newVertices.Length];
  2389. for (int i = 0; i < newNormals.Length; i++) {
  2390. newNormals[i] = Vector3.forward;
  2391. }
  2392. // Create quad
  2393. quadMesh.vertices = newVertices;
  2394. quadMesh.uv = newUVs;
  2395. quadMesh.triangles = newTriangles;
  2396. quadMesh.normals = newNormals;
  2397. quadMesh.RecalculateBounds();
  2398. }
  2399. void BuildCube() {
  2400. cubeMesh = Resources.GetBuiltinResource<Mesh>("Cube.fbx");
  2401. }
  2402. /// <summary>
  2403. /// Returns true if a given transform is included in this effect
  2404. /// </summary>
  2405. public bool Includes(Transform transform) {
  2406. for (int k = 0; k < rmsCount; k++) {
  2407. if (rms[k].transform == transform) return true;
  2408. }
  2409. return false;
  2410. }
  2411. /// <summary>
  2412. /// Updates profile glow color
  2413. /// </summary>
  2414. public void SetGlowColor(Color color) {
  2415. if (glowPasses != null) {
  2416. for (int k = 0; k < glowPasses.Length; k++) {
  2417. glowPasses[k].color = color;
  2418. }
  2419. }
  2420. glowHQColor = color;
  2421. UpdateMaterialProperties();
  2422. }
  2423. #region Normals handling
  2424. static List<Vector3> vertices;
  2425. static List<Vector3> normals;
  2426. static Vector3[] newNormals;
  2427. static int[] matches;
  2428. static readonly Dictionary<Vector3, int> vv = new Dictionary<Vector3, int>();
  2429. static readonly List<Material> rendererSharedMaterials = new List<Material>();
  2430. // cached meshes
  2431. static readonly Dictionary<int, Mesh> smoothMeshes = new Dictionary<int, Mesh>();
  2432. static readonly Dictionary<int, Mesh> reorientedMeshes = new Dictionary<int, Mesh>();
  2433. static readonly Dictionary<int, Mesh> combinedMeshes = new Dictionary<int, Mesh>();
  2434. int combinedMeshesHashId;
  2435. // annotate usage count of the instanced meshes due to normals or combine mesh option
  2436. // when highlighte effect is destroyed and the usage count is zero, we destroy the cached mesh
  2437. static readonly Dictionary<Mesh, int> sharedMeshUsage = new Dictionary<Mesh, int>();
  2438. readonly List<Mesh> instancedMeshes = new List<Mesh>();
  2439. void AverageNormals(int objIndex) {
  2440. if (rms == null || objIndex >= rms.Length) return;
  2441. Mesh mesh = rms[objIndex].mesh;
  2442. Mesh newMesh;
  2443. int hashCode = mesh.GetHashCode();
  2444. if (!smoothMeshes.TryGetValue(hashCode, out newMesh) || newMesh == null) {
  2445. if (!mesh.isReadable) return;
  2446. if (normals == null) {
  2447. normals = new List<Vector3>();
  2448. } else {
  2449. normals.Clear();
  2450. }
  2451. mesh.GetNormals(normals);
  2452. int normalsCount = normals.Count;
  2453. if (normalsCount == 0)
  2454. return;
  2455. if (vertices == null) {
  2456. vertices = new List<Vector3>();
  2457. } else {
  2458. vertices.Clear();
  2459. }
  2460. mesh.GetVertices(vertices);
  2461. int vertexCount = vertices.Count;
  2462. if (normalsCount < vertexCount) {
  2463. vertexCount = normalsCount;
  2464. }
  2465. if (newNormals == null || newNormals.Length < vertexCount) {
  2466. newNormals = new Vector3[vertexCount];
  2467. } else {
  2468. Vector3 zero = Vector3.zero;
  2469. for (int k = 0; k < vertexCount; k++) {
  2470. newNormals[k] = zero;
  2471. }
  2472. }
  2473. if (matches == null || matches.Length < vertexCount) {
  2474. matches = new int[vertexCount];
  2475. }
  2476. // Locate overlapping vertices
  2477. vv.Clear();
  2478. for (int k = 0; k < vertexCount; k++) {
  2479. int i;
  2480. Vector3 v = vertices[k];
  2481. if (!vv.TryGetValue(v, out i)) {
  2482. vv[v] = i = k;
  2483. }
  2484. matches[k] = i;
  2485. }
  2486. // Average normals
  2487. for (int k = 0; k < vertexCount; k++) {
  2488. int match = matches[k];
  2489. newNormals[match] += normals[k];
  2490. }
  2491. for (int k = 0; k < vertexCount; k++) {
  2492. int match = matches[k];
  2493. normals[k] = newNormals[match].normalized;
  2494. }
  2495. // Reassign normals
  2496. newMesh = Instantiate(mesh);
  2497. newMesh.SetNormals(normals);
  2498. smoothMeshes[hashCode] = newMesh;
  2499. IncrementeMeshUsage(newMesh);
  2500. }
  2501. rms[objIndex].mesh = newMesh;
  2502. }
  2503. void ReorientNormals(int objIndex) {
  2504. if (rms == null || objIndex >= rms.Length) return;
  2505. Mesh mesh = rms[objIndex].mesh;
  2506. Mesh newMesh;
  2507. int hashCode = mesh.GetHashCode();
  2508. if (!reorientedMeshes.TryGetValue(hashCode, out newMesh) || newMesh == null) {
  2509. if (!mesh.isReadable) return;
  2510. if (normals == null) {
  2511. normals = new List<Vector3>();
  2512. } else {
  2513. normals.Clear();
  2514. }
  2515. if (vertices == null) {
  2516. vertices = new List<Vector3>();
  2517. } else {
  2518. vertices.Clear();
  2519. }
  2520. mesh.GetVertices(vertices);
  2521. int vertexCount = vertices.Count;
  2522. if (vertexCount == 0) return;
  2523. Vector3 mid = Vector3.zero;
  2524. for (int k = 0; k < vertexCount; k++) {
  2525. mid += vertices[k];
  2526. }
  2527. mid /= vertexCount;
  2528. // Average normals
  2529. for (int k = 0; k < vertexCount; k++) {
  2530. normals.Add((vertices[k] - mid).normalized);
  2531. }
  2532. // Reassign normals
  2533. newMesh = Instantiate(mesh);
  2534. newMesh.SetNormals(normals);
  2535. reorientedMeshes[hashCode] = newMesh;
  2536. IncrementeMeshUsage(newMesh);
  2537. }
  2538. rms[objIndex].mesh = newMesh;
  2539. }
  2540. const int MAX_VERTEX_COUNT = 65535;
  2541. void CombineMeshes() {
  2542. if (rmsCount <= 1) return;
  2543. // Combine meshes of group into the first mesh in rms
  2544. if (combineInstances == null || combineInstances.Length != rmsCount) {
  2545. combineInstances = new CombineInstance[rmsCount];
  2546. }
  2547. int first = -1;
  2548. int count = 0;
  2549. combinedMeshesHashId = 0;
  2550. int vertexCount = 0;
  2551. Matrix4x4 im = Matrix4x4.identity;
  2552. for (int k = 0; k < rmsCount; k++) {
  2553. combineInstances[k].mesh = null;
  2554. if (!rms[k].isSkinnedMesh) {
  2555. Mesh mesh = rms[k].mesh;
  2556. if (mesh != null && mesh.isReadable) {
  2557. if (vertexCount + mesh.vertexCount > MAX_VERTEX_COUNT) continue;
  2558. vertexCount += mesh.vertexCount;
  2559. combineInstances[k].mesh = mesh;
  2560. int instanceId = rms[k].renderer.gameObject.GetInstanceID();
  2561. if (first < 0) {
  2562. first = k;
  2563. combinedMeshesHashId = instanceId;
  2564. im = rms[k].transform.worldToLocalMatrix;
  2565. } else {
  2566. combinedMeshesHashId ^= instanceId;
  2567. rms[k].mesh = null;
  2568. }
  2569. combineInstances[k].transform = im * rms[k].transform.localToWorldMatrix;
  2570. count++;
  2571. }
  2572. }
  2573. }
  2574. if (count < 2) return;
  2575. Mesh combinedMesh;
  2576. if (!combinedMeshes.TryGetValue(combinedMeshesHashId, out combinedMesh) || combinedMesh == null) {
  2577. combinedMesh = new Mesh();
  2578. combinedMesh.CombineMeshes(combineInstances, true, true);
  2579. combinedMeshes[combinedMeshesHashId] = combinedMesh;
  2580. IncrementeMeshUsage(combinedMesh);
  2581. }
  2582. rms[first].mesh = combinedMesh;
  2583. rms[first].isCombined = true;
  2584. }
  2585. void IncrementeMeshUsage(Mesh mesh) {
  2586. int usageCount;
  2587. sharedMeshUsage.TryGetValue(mesh, out usageCount);
  2588. usageCount++;
  2589. sharedMeshUsage[mesh] = usageCount;
  2590. instancedMeshes.Add(mesh);
  2591. }
  2592. /// <summary>
  2593. /// Destroys any cached mesh
  2594. /// </summary>
  2595. public static void ClearMeshCache() {
  2596. foreach (Mesh mesh in combinedMeshes.Values) {
  2597. if (mesh != null) DestroyImmediate(mesh);
  2598. }
  2599. foreach (Mesh mesh in smoothMeshes.Values) {
  2600. if (mesh != null) DestroyImmediate(mesh);
  2601. }
  2602. foreach (Mesh mesh in reorientedMeshes.Values) {
  2603. if (mesh != null) DestroyImmediate(mesh);
  2604. }
  2605. }
  2606. /// <summary>
  2607. /// Clears cached mesh only for the highlighted object
  2608. /// </summary>
  2609. void RefreshCachedMeshes() {
  2610. if (combinedMeshes.TryGetValue(combinedMeshesHashId, out Mesh combinedMesh)) {
  2611. DestroyImmediate(combinedMesh);
  2612. combinedMeshes.Remove(combinedMeshesHashId);
  2613. }
  2614. if (rms == null) return;
  2615. for (int k = 0; k < rms.Length; k++) {
  2616. Mesh mesh = rms[k].mesh;
  2617. if (mesh != null) {
  2618. if (smoothMeshes.ContainsValue(mesh) || reorientedMeshes.ContainsValue(mesh)) {
  2619. DestroyImmediate(mesh);
  2620. }
  2621. }
  2622. }
  2623. }
  2624. #endregion
  2625. }
  2626. }