TriggerZone.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. using UnityEngine;
  2. namespace VLB
  3. {
  4. [DisallowMultipleComponent]
  5. [RequireComponent(typeof(VolumetricLightBeam))]
  6. [HelpURL(Consts.HelpUrlTriggerZone)]
  7. public class TriggerZone : MonoBehaviour
  8. {
  9. /// <summary>
  10. /// Define if the Collider will be created as a convex trigger (not physical, most common behavior) or as a regular collider (physical).
  11. /// </summary>
  12. public bool setIsTrigger = true;
  13. /// <summary>
  14. /// Change the length of the Collider. For example, set 2.0 to make the Collider 2x longer than the beam. Default value is 1.0.
  15. /// </summary>
  16. public float rangeMultiplier = 1.0f;
  17. enum TriggerZoneUpdateRate
  18. {
  19. /// <summary>Compute the Trigger Zone only once at startup</summary>
  20. OnEnable,
  21. /// <summary>Compute the Trigger Zone each time the dynamic occlusion has changed</summary>
  22. OnOcclusionChange,
  23. }
  24. /// <summary>
  25. /// How often will the Trigger Zone be computed?
  26. /// </summary>
  27. TriggerZoneUpdateRate updateRate
  28. {
  29. get
  30. {
  31. Debug.Assert(m_Beam != null);
  32. if(m_Beam.dimensions == Dimensions.Dim3D) return TriggerZoneUpdateRate.OnEnable; // for 3D meshes, do it only once because it's too performance heavy
  33. return m_DynamicOcclusionRaycasting != null ? TriggerZoneUpdateRate.OnOcclusionChange : TriggerZoneUpdateRate.OnEnable;
  34. }
  35. }
  36. const int kMeshColliderNumSides = 8;
  37. VolumetricLightBeam m_Beam = null;
  38. DynamicOcclusionRaycasting m_DynamicOcclusionRaycasting = null;
  39. PolygonCollider2D m_PolygonCollider2D = null;
  40. void OnEnable()
  41. {
  42. m_Beam = GetComponent<VolumetricLightBeam>();
  43. Debug.Assert(m_Beam != null);
  44. m_DynamicOcclusionRaycasting = GetComponent<DynamicOcclusionRaycasting>();
  45. switch(updateRate)
  46. {
  47. case TriggerZoneUpdateRate.OnEnable:
  48. {
  49. ComputeZone();
  50. enabled = false;
  51. break;
  52. }
  53. case TriggerZoneUpdateRate.OnOcclusionChange:
  54. {
  55. if(m_DynamicOcclusionRaycasting)
  56. m_DynamicOcclusionRaycasting.onOcclusionProcessed += OnOcclusionProcessed;
  57. break;
  58. }
  59. }
  60. }
  61. void OnOcclusionProcessed()
  62. {
  63. ComputeZone();
  64. }
  65. void ComputeZone()
  66. {
  67. if (m_Beam)
  68. {
  69. var rangeEnd = m_Beam.fallOffEnd * rangeMultiplier;
  70. var lerpedRadiusEnd = Mathf.LerpUnclamped(m_Beam.coneRadiusStart, m_Beam.coneRadiusEnd, rangeMultiplier);
  71. if (m_Beam.dimensions == Dimensions.Dim3D)
  72. {
  73. var meshCollider = gameObject.GetOrAddComponent<MeshCollider>();
  74. Debug.Assert(meshCollider);
  75. int sides = Mathf.Min(m_Beam.geomSides, kMeshColliderNumSides);
  76. var mesh = MeshGenerator.GenerateConeZ_Radius(rangeEnd, m_Beam.coneRadiusStart, lerpedRadiusEnd, sides, 0, false, false);
  77. mesh.hideFlags = Consts.ProceduralObjectsHideFlags;
  78. meshCollider.sharedMesh = mesh;
  79. meshCollider.convex = setIsTrigger;
  80. meshCollider.isTrigger = setIsTrigger;
  81. }
  82. else
  83. {
  84. if (m_PolygonCollider2D == null)
  85. {
  86. m_PolygonCollider2D = gameObject.GetOrAddComponent<PolygonCollider2D>();
  87. Debug.Assert(m_PolygonCollider2D);
  88. }
  89. var polyCoordsLS = new Vector2[] // polygon coord in local space
  90. {
  91. new Vector2(0.0f, -m_Beam.coneRadiusStart),
  92. new Vector2(rangeEnd, -lerpedRadiusEnd),
  93. new Vector2(rangeEnd, lerpedRadiusEnd),
  94. new Vector2(0.0f, m_Beam.coneRadiusStart)
  95. };
  96. if (m_DynamicOcclusionRaycasting && m_DynamicOcclusionRaycasting.planeEquationWS.IsValid())
  97. {
  98. var plane3dWS = m_DynamicOcclusionRaycasting.planeEquationWS;
  99. if (Utils.IsAlmostZero(plane3dWS.normal.z))
  100. {
  101. // Compute 2 points on the plane in world space
  102. // Use this technique instead of transforming the plane's normal to fully support scaling
  103. var ptOnPlane1 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.zero);
  104. var ptOnPlane2 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.up);
  105. if(Utils.IsAlmostZero(Vector3.SqrMagnitude(ptOnPlane1 - ptOnPlane2)))
  106. ptOnPlane1 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.right);
  107. // Compute 2 points on the plane in local space
  108. ptOnPlane1 = transform.InverseTransformPoint(ptOnPlane1);
  109. ptOnPlane2 = transform.InverseTransformPoint(ptOnPlane2);
  110. // Compute plane equation in local space
  111. var plane2dLS = PolygonHelper.Plane2D.FromPoints(ptOnPlane1, ptOnPlane2);
  112. if (plane2dLS.normal.x > 0.0f) plane2dLS.Flip();
  113. polyCoordsLS = plane2dLS.CutConvex(polyCoordsLS);
  114. }
  115. }
  116. m_PolygonCollider2D.points = polyCoordsLS;
  117. m_PolygonCollider2D.isTrigger = setIsTrigger;
  118. }
  119. }
  120. }
  121. #if UNITY_EDITOR
  122. void OnValidate()
  123. {
  124. rangeMultiplier = Mathf.Max(rangeMultiplier, 0.001f);
  125. }
  126. #endif
  127. }
  128. }