Light2D.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using System;
  2. using UnityEngine.Serialization;
  3. using UnityEngine.Scripting.APIUpdating;
  4. using UnityEngine.U2D;
  5. #if UNITY_EDITOR
  6. using UnityEditor.Experimental.SceneManagement;
  7. #endif
  8. namespace UnityEngine.Rendering.Universal
  9. {
  10. /// <summary>
  11. /// Class <c>Light2D</c> is a 2D light which can be used with the 2D Renderer.
  12. /// </summary>
  13. ///
  14. [ExecuteAlways, DisallowMultipleComponent]
  15. [MovedFrom("UnityEngine.Experimental.Rendering.Universal")]
  16. [AddComponentMenu("Rendering/2D/Light 2D")]
  17. [HelpURL("https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/2DLightProperties.html")]
  18. public sealed partial class Light2D : Light2DBase, ISerializationCallbackReceiver
  19. {
  20. /// <summary>
  21. /// Deprecated Light types that are no supported. Please migrate to either Freeform or Point lights.
  22. /// </summary>
  23. public enum DeprecatedLightType
  24. {
  25. /// <summary>
  26. /// N-gon shaped lights.
  27. /// </summary>
  28. Parametric = 0,
  29. }
  30. /// <summary>
  31. /// An enumeration of the types of light
  32. /// </summary>
  33. public enum LightType
  34. {
  35. /// <summary>
  36. /// N-gon shaped lights. Deprecated.
  37. /// </summary>
  38. Parametric = 0,
  39. /// <summary>
  40. /// The shape of the light is based on a user defined closed shape with multiple points.
  41. /// </summary>
  42. Freeform = 1,
  43. /// <summary>
  44. /// The shape of the light is based on a Sprite.
  45. /// </summary>
  46. Sprite = 2,
  47. /// <summary>
  48. /// The shape of light is circular and can also be configured into a pizza shape.
  49. /// </summary>
  50. Point = 3,
  51. /// <summary>
  52. /// Shapeless light that affects the entire screen.
  53. /// </summary>
  54. Global = 4
  55. }
  56. /// <summary>
  57. /// The accuracy of how the normap map calculation.
  58. /// </summary>
  59. public enum NormalMapQuality
  60. {
  61. /// <summary>
  62. /// Normal map not used.
  63. /// </summary>
  64. Disabled = 2,
  65. /// <summary>
  66. /// Faster calculation with less accuracy suited for small shapes on screen.
  67. /// </summary>
  68. Fast = 0,
  69. /// <summary>
  70. /// Accurate calculation useful for better output on bigger shapes on screen.
  71. /// </summary>
  72. Accurate = 1
  73. }
  74. /// <summary>
  75. /// Determines how the final color is calculated when multiple lights overlap each other
  76. /// </summary>
  77. public enum OverlapOperation
  78. {
  79. /// <summary>
  80. /// Colors are added together
  81. /// </summary>
  82. Additive,
  83. /// <summary>
  84. /// Colors are blended using standed blending (alpha, 1-alpha)
  85. /// </summary>
  86. AlphaBlend
  87. }
  88. private enum ComponentVersions
  89. {
  90. Version_Unserialized = 0,
  91. Version_1 = 1
  92. }
  93. const ComponentVersions k_CurrentComponentVersion = ComponentVersions.Version_1;
  94. [SerializeField] ComponentVersions m_ComponentVersion = ComponentVersions.Version_Unserialized;
  95. #if USING_ANIMATION_MODULE
  96. [UnityEngine.Animations.NotKeyable]
  97. #endif
  98. [SerializeField] LightType m_LightType = LightType.Point;
  99. [SerializeField, FormerlySerializedAs("m_LightOperationIndex")]
  100. int m_BlendStyleIndex = 0;
  101. [SerializeField] float m_FalloffIntensity = 0.5f;
  102. [ColorUsage(true)]
  103. [SerializeField] Color m_Color = Color.white;
  104. [SerializeField] float m_Intensity = 1;
  105. [FormerlySerializedAs("m_LightVolumeOpacity")]
  106. [SerializeField] float m_LightVolumeIntensity = 1.0f;
  107. [SerializeField] bool m_LightVolumeIntensityEnabled = false;
  108. [SerializeField] int[] m_ApplyToSortingLayers = new int[1]; // These are sorting layer IDs. If we need to update this at runtime make sure we add code to update global lights
  109. [Reload("Textures/2D/Sparkle.png")]
  110. [SerializeField] Sprite m_LightCookieSprite;
  111. [FormerlySerializedAs("m_LightCookieSprite")]
  112. [SerializeField] Sprite m_DeprecatedPointLightCookieSprite;
  113. [SerializeField] int m_LightOrder = 0;
  114. [SerializeField] OverlapOperation m_OverlapOperation = OverlapOperation.Additive;
  115. [FormerlySerializedAs("m_PointLightDistance")]
  116. [SerializeField] float m_NormalMapDistance = 3.0f;
  117. #if USING_ANIMATION_MODULE
  118. [UnityEngine.Animations.NotKeyable]
  119. #endif
  120. [FormerlySerializedAs("m_PointLightQuality")]
  121. [SerializeField] NormalMapQuality m_NormalMapQuality = NormalMapQuality.Disabled;
  122. [SerializeField] bool m_UseNormalMap = false; // This is now deprecated. Keep it here for backwards compatibility.
  123. [SerializeField] bool m_ShadowIntensityEnabled = false;
  124. [Range(0, 1)]
  125. [SerializeField] float m_ShadowIntensity = 0.75f;
  126. [SerializeField] bool m_ShadowVolumeIntensityEnabled = false;
  127. [Range(0, 1)]
  128. [SerializeField] float m_ShadowVolumeIntensity = 0.75f;
  129. Mesh m_Mesh;
  130. [SerializeField]
  131. private LightUtility.LightMeshVertex[] m_Vertices = new LightUtility.LightMeshVertex[1];
  132. [SerializeField]
  133. private ushort[] m_Triangles = new ushort[1];
  134. internal LightUtility.LightMeshVertex[] vertices { get { return m_Vertices; } set { m_Vertices = value; } }
  135. internal ushort[] indices { get { return m_Triangles; } set { m_Triangles = value; } }
  136. // Transients
  137. int m_PreviousLightCookieSprite;
  138. internal Vector3 m_CachedPosition;
  139. internal int[] affectedSortingLayers => m_ApplyToSortingLayers;
  140. private int lightCookieSpriteInstanceID => m_LightCookieSprite?.GetInstanceID() ?? 0;
  141. [SerializeField]
  142. Bounds m_LocalBounds;
  143. internal BoundingSphere boundingSphere { get; private set; }
  144. internal Mesh lightMesh
  145. {
  146. get
  147. {
  148. if (null == m_Mesh)
  149. m_Mesh = new Mesh();
  150. return m_Mesh;
  151. }
  152. }
  153. internal bool hasCachedMesh => (vertices.Length > 1 && indices.Length > 1);
  154. internal bool forceUpdate = false;
  155. /// <summary>
  156. /// The light's current type
  157. /// </summary>
  158. public LightType lightType
  159. {
  160. get => m_LightType;
  161. set
  162. {
  163. if (m_LightType != value)
  164. UpdateMesh();
  165. m_LightType = value;
  166. Light2DManager.ErrorIfDuplicateGlobalLight(this);
  167. }
  168. }
  169. /// <summary>
  170. /// The lights current operation index
  171. /// </summary>
  172. public int blendStyleIndex { get => m_BlendStyleIndex; set => m_BlendStyleIndex = value; }
  173. /// <summary>
  174. /// Specifies the darkness of the shadow
  175. /// </summary>
  176. public float shadowIntensity { get => m_ShadowIntensity; set => m_ShadowIntensity = Mathf.Clamp01(value); }
  177. /// <summary>
  178. /// Specifies that the shadows are enabled
  179. /// </summary>
  180. public bool shadowsEnabled { get => m_ShadowIntensityEnabled; set => m_ShadowIntensityEnabled = value; }
  181. /// <summary>
  182. /// Specifies the darkness of the shadow
  183. /// </summary>
  184. public float shadowVolumeIntensity { get => m_ShadowVolumeIntensity; set => m_ShadowVolumeIntensity = Mathf.Clamp01(value); }
  185. /// <summary>
  186. /// Specifies that the volumetric shadows are enabled
  187. /// </summary>
  188. public bool volumetricShadowsEnabled { get => m_ShadowVolumeIntensityEnabled; set => m_ShadowVolumeIntensityEnabled = value; }
  189. /// <summary>
  190. /// The lights current color
  191. /// </summary>
  192. public Color color { get => m_Color; set => m_Color = value; }
  193. /// <summary>
  194. /// The lights current intensity
  195. /// </summary>
  196. public float intensity { get => m_Intensity; set => m_Intensity = value; }
  197. /// <summary>
  198. /// The lights current intensity
  199. /// </summary>
  200. ///
  201. [Obsolete]
  202. public float volumeOpacity => m_LightVolumeIntensity;
  203. /// <summary>
  204. /// Controls the visibility of the light's volume
  205. /// </summary>
  206. public float volumeIntensity => m_LightVolumeIntensity;
  207. /// <summary>
  208. /// Enables or disables the light's volume
  209. /// </summary>
  210. public bool volumeIntensityEnabled { get => m_LightVolumeIntensityEnabled; set => m_LightVolumeIntensityEnabled = value; }
  211. /// <summary>
  212. /// The Sprite that's used by the Sprite Light type to control the shape light
  213. /// </summary>
  214. public Sprite lightCookieSprite { get { return m_LightType != LightType.Point ? m_LightCookieSprite : m_DeprecatedPointLightCookieSprite; } }
  215. /// <summary>
  216. /// Controls the brightness and distance of the fall off (edge) of the light
  217. /// </summary>
  218. public float falloffIntensity => m_FalloffIntensity;
  219. [Obsolete]
  220. public bool alphaBlendOnOverlap { get { return m_OverlapOperation == OverlapOperation.AlphaBlend; } }
  221. /// <summary>
  222. /// Returns the overlap operation mode.
  223. /// </summary>
  224. public OverlapOperation overlapOperation => m_OverlapOperation;
  225. /// <summary>
  226. /// Gets or sets the light order. The lightOrder determines the order in which the lights are rendered onto the light textures.
  227. /// </summary>
  228. public int lightOrder { get => m_LightOrder; set => m_LightOrder = value; }
  229. /// <summary>
  230. /// The simulated z distance of the light from the surface used in normal map calculation.
  231. /// </summary>
  232. public float normalMapDistance => m_NormalMapDistance;
  233. /// <summary>
  234. /// Returns the calculation quality for the normal map rendering. Please refer to NormalMapQuality.
  235. /// </summary>
  236. public NormalMapQuality normalMapQuality => m_NormalMapQuality;
  237. public bool renderVolumetricShadows => volumetricShadowsEnabled && shadowVolumeIntensity > 0;
  238. internal void MarkForUpdate()
  239. {
  240. forceUpdate = true;
  241. }
  242. internal void CacheValues()
  243. {
  244. m_CachedPosition = transform.position;
  245. }
  246. internal int GetTopMostLitLayer()
  247. {
  248. var largestIndex = Int32.MinValue;
  249. var largestLayer = 0;
  250. var layers = Light2DManager.GetCachedSortingLayer();
  251. for (var i = 0; i < m_ApplyToSortingLayers.Length; ++i)
  252. {
  253. for (var layer = layers.Length - 1; layer >= largestLayer; --layer)
  254. {
  255. if (layers[layer].id == m_ApplyToSortingLayers[i])
  256. {
  257. largestIndex = layers[layer].value;
  258. largestLayer = layer;
  259. }
  260. }
  261. }
  262. return largestIndex;
  263. }
  264. internal Bounds UpdateSpriteMesh()
  265. {
  266. if (m_LightCookieSprite == null && (m_Vertices.Length != 1 || m_Triangles.Length != 1))
  267. {
  268. m_Vertices = new LightUtility.LightMeshVertex[1];
  269. m_Triangles = new ushort[1];
  270. }
  271. return LightUtility.GenerateSpriteMesh(this, m_LightCookieSprite);
  272. }
  273. internal void UpdateMesh(bool forceUpdate = false)
  274. {
  275. var shapePathHash = LightUtility.GetShapePathHash(shapePath);
  276. var fallOffSizeChanged = LightUtility.CheckForChange(m_ShapeLightFalloffSize, ref m_PreviousShapeLightFalloffSize);
  277. var parametricRadiusChanged = LightUtility.CheckForChange(m_ShapeLightParametricRadius, ref m_PreviousShapeLightParametricRadius);
  278. var parametricSidesChanged = LightUtility.CheckForChange(m_ShapeLightParametricSides, ref m_PreviousShapeLightParametricSides);
  279. var parametricAngleOffsetChanged = LightUtility.CheckForChange(m_ShapeLightParametricAngleOffset, ref m_PreviousShapeLightParametricAngleOffset);
  280. var spriteInstanceChanged = LightUtility.CheckForChange(lightCookieSpriteInstanceID, ref m_PreviousLightCookieSprite);
  281. var shapePathHashChanged = LightUtility.CheckForChange(shapePathHash, ref m_PreviousShapePathHash);
  282. var lightTypeChanged = LightUtility.CheckForChange(m_LightType, ref m_PreviousLightType);
  283. var hashChanged = fallOffSizeChanged || parametricRadiusChanged || parametricSidesChanged ||
  284. parametricAngleOffsetChanged || spriteInstanceChanged || shapePathHashChanged || lightTypeChanged;
  285. // Mesh Rebuilding
  286. if (hashChanged || forceUpdate)
  287. {
  288. switch (m_LightType)
  289. {
  290. case LightType.Freeform:
  291. m_LocalBounds = LightUtility.GenerateShapeMesh(this, m_ShapePath, m_ShapeLightFalloffSize);
  292. break;
  293. case LightType.Parametric:
  294. m_LocalBounds = LightUtility.GenerateParametricMesh(this, m_ShapeLightParametricRadius, m_ShapeLightFalloffSize, m_ShapeLightParametricAngleOffset, m_ShapeLightParametricSides);
  295. break;
  296. case LightType.Sprite:
  297. m_LocalBounds = UpdateSpriteMesh();
  298. break;
  299. case LightType.Point:
  300. m_LocalBounds = LightUtility.GenerateParametricMesh(this, 1.412135f, 0, 0, 4);
  301. break;
  302. }
  303. }
  304. }
  305. internal void UpdateBoundingSphere()
  306. {
  307. if (isPointLight)
  308. {
  309. boundingSphere = new BoundingSphere(transform.position, m_PointLightOuterRadius);
  310. return;
  311. }
  312. var maxBound = transform.TransformPoint(Vector3.Max(m_LocalBounds.max, m_LocalBounds.max + (Vector3)m_ShapeLightFalloffOffset));
  313. var minBound = transform.TransformPoint(Vector3.Min(m_LocalBounds.min, m_LocalBounds.min + (Vector3)m_ShapeLightFalloffOffset));
  314. var center = 0.5f * (maxBound + minBound);
  315. var radius = Vector3.Magnitude(maxBound - center);
  316. boundingSphere = new BoundingSphere(center, radius);
  317. }
  318. internal bool IsLitLayer(int layer)
  319. {
  320. if (m_ApplyToSortingLayers == null)
  321. return false;
  322. for (var i = 0; i < m_ApplyToSortingLayers.Length; i++)
  323. if (m_ApplyToSortingLayers[i] == layer)
  324. return true;
  325. return false;
  326. }
  327. private void Awake()
  328. {
  329. if (m_LightCookieSprite != null)
  330. {
  331. bool updateMesh = !hasCachedMesh || (m_LightType == LightType.Sprite && m_LightCookieSprite.packed);
  332. UpdateMesh(updateMesh);
  333. if (hasCachedMesh)
  334. {
  335. lightMesh.SetVertexBufferParams(vertices.Length, LightUtility.LightMeshVertex.VertexLayout);
  336. lightMesh.SetVertexBufferData(vertices, 0, 0, vertices.Length);
  337. lightMesh.SetIndices(indices, MeshTopology.Triangles, 0, false);
  338. }
  339. }
  340. }
  341. void OnEnable()
  342. {
  343. m_PreviousLightCookieSprite = lightCookieSpriteInstanceID;
  344. Light2DManager.RegisterLight(this);
  345. }
  346. private void OnDisable()
  347. {
  348. Light2DManager.DeregisterLight(this);
  349. }
  350. private void LateUpdate()
  351. {
  352. if (m_LightType == LightType.Global)
  353. return;
  354. UpdateMesh(forceUpdate);
  355. UpdateBoundingSphere();
  356. forceUpdate = false;
  357. }
  358. public void OnBeforeSerialize()
  359. {
  360. m_ComponentVersion = k_CurrentComponentVersion;
  361. }
  362. public void OnAfterDeserialize()
  363. {
  364. // Upgrade from no serialized version
  365. if (m_ComponentVersion == ComponentVersions.Version_Unserialized)
  366. {
  367. m_ShadowVolumeIntensityEnabled = m_ShadowVolumeIntensity > 0;
  368. m_ShadowIntensityEnabled = m_ShadowIntensity > 0;
  369. m_LightVolumeIntensityEnabled = m_LightVolumeIntensity > 0;
  370. m_NormalMapQuality = !m_UseNormalMap ? NormalMapQuality.Disabled : m_NormalMapQuality;
  371. m_ComponentVersion = ComponentVersions.Version_1;
  372. }
  373. }
  374. }
  375. }