Car/Assets/VolumetricLightBeam/Scripts/TriggerZone.cs

160 lines
6.4 KiB
C#

using UnityEngine;
namespace VLB
{
[DisallowMultipleComponent]
[RequireComponent(typeof(VolumetricLightBeamAbstractBase))]
[HelpURL(Consts.Help.UrlTriggerZone)]
public class TriggerZone : MonoBehaviour
{
public const string ClassName = "TriggerZone";
/// <summary>
/// Define if the Collider will be created as a convex trigger (not physical, most common behavior) or as a regular collider (physical).
/// </summary>
public bool setIsTrigger = true;
/// <summary>
/// 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.
/// </summary>
public float rangeMultiplier = 1.0f;
enum TriggerZoneUpdateRate
{
/// <summary>Compute the Trigger Zone only once at startup</summary>
OnEnable,
/// <summary>Compute the Trigger Zone each time the dynamic occlusion has changed</summary>
OnOcclusionChange,
}
/// <summary>
/// How often will the Trigger Zone be computed?
/// </summary>
TriggerZoneUpdateRate updateRate
{
get
{
Debug.Assert(m_Beam != null);
if(UtilsBeamProps.GetDimensions(m_Beam) == Dimensions.Dim3D) return TriggerZoneUpdateRate.OnEnable; // for 3D meshes, do it only once because it's too performance heavy
return m_DynamicOcclusionRaycasting != null ? TriggerZoneUpdateRate.OnOcclusionChange : TriggerZoneUpdateRate.OnEnable;
}
}
const int kMeshColliderNumSides = 8;
VolumetricLightBeamAbstractBase m_Beam = null;
DynamicOcclusionRaycasting m_DynamicOcclusionRaycasting = null;
PolygonCollider2D m_PolygonCollider2D = null;
void OnEnable()
{
m_Beam = GetComponent<VolumetricLightBeamAbstractBase>();
Debug.Assert(m_Beam != null);
m_DynamicOcclusionRaycasting = GetComponent<DynamicOcclusionRaycasting>();
switch(updateRate)
{
case TriggerZoneUpdateRate.OnEnable:
{
ComputeZone();
enabled = false;
break;
}
case TriggerZoneUpdateRate.OnOcclusionChange:
{
if(m_DynamicOcclusionRaycasting)
m_DynamicOcclusionRaycasting.onOcclusionProcessed += OnOcclusionProcessed;
break;
}
}
}
void OnOcclusionProcessed()
{
ComputeZone();
}
void ComputeZone()
{
if (m_Beam)
{
var coneRadiusStart = UtilsBeamProps.GetConeRadiusStart(m_Beam);
var rangeEnd = UtilsBeamProps.GetFallOffEnd(m_Beam) * rangeMultiplier;
var lerpedRadiusEnd = Mathf.LerpUnclamped(coneRadiusStart, UtilsBeamProps.GetConeRadiusEnd(m_Beam), rangeMultiplier);
if (UtilsBeamProps.GetDimensions(m_Beam) == Dimensions.Dim3D)
{
var meshCollider = gameObject.GetOrAddComponent<MeshCollider>();
Debug.Assert(meshCollider);
int sides = Mathf.Min(UtilsBeamProps.GetGeomSides(m_Beam), kMeshColliderNumSides);
var mesh = MeshGenerator.GenerateConeZ_Radii_DoubleCaps(
lengthZ: rangeEnd,
radiusStart: coneRadiusStart,
radiusEnd: lerpedRadiusEnd,
numSides: kMeshColliderNumSides,
inverted: false);
mesh.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
meshCollider.sharedMesh = mesh;
meshCollider.convex = setIsTrigger;
meshCollider.isTrigger = setIsTrigger;
}
else
{
if (m_PolygonCollider2D == null)
{
m_PolygonCollider2D = gameObject.GetOrAddComponent<PolygonCollider2D>();
Debug.Assert(m_PolygonCollider2D);
}
var polyCoordsLS = new Vector2[] // polygon coord in local space
{
new Vector2(0.0f, -coneRadiusStart),
new Vector2(rangeEnd, -lerpedRadiusEnd),
new Vector2(rangeEnd, lerpedRadiusEnd),
new Vector2(0.0f, coneRadiusStart)
};
if (m_DynamicOcclusionRaycasting && m_DynamicOcclusionRaycasting.planeEquationWS.IsValid())
{
var plane3dWS = m_DynamicOcclusionRaycasting.planeEquationWS;
if (Utils.IsAlmostZero(plane3dWS.normal.z))
{
// Compute 2 points on the plane in world space
// Use this technique instead of transforming the plane's normal to fully support scaling
var ptOnPlane1 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.zero);
var ptOnPlane2 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.up);
if(Utils.IsAlmostZero(Vector3.SqrMagnitude(ptOnPlane1 - ptOnPlane2)))
ptOnPlane1 = plane3dWS.ClosestPointOnPlaneCustom(Vector3.right);
// Compute 2 points on the plane in local space
ptOnPlane1 = transform.InverseTransformPoint(ptOnPlane1);
ptOnPlane2 = transform.InverseTransformPoint(ptOnPlane2);
// Compute plane equation in local space
var plane2dLS = PolygonHelper.Plane2D.FromPoints(ptOnPlane1, ptOnPlane2);
if (plane2dLS.normal.x > 0.0f) plane2dLS.Flip();
polyCoordsLS = plane2dLS.CutConvex(polyCoordsLS);
}
}
m_PolygonCollider2D.points = polyCoordsLS;
m_PolygonCollider2D.isTrigger = setIsTrigger;
}
}
}
#if UNITY_EDITOR
void OnValidate()
{
rangeMultiplier = Mathf.Max(rangeMultiplier, 0.001f);
}
#endif
}
}