red-sim-light-volumesudon/VRCLightVolumes/PointLightVolumeInstance.cs
2025-09-17 01:38:27 +01:00

313 lines
No EOL
9.1 KiB
C#

using System;
using UnityEngine;
namespace VRCLightVolumes
{
public class PointLightVolumeInstance : MonoBehaviour
{
[ColorUsage(false)]
[Tooltip("Point light volume color")]
public UnityEngine.Color Color;
[Tooltip("Color multiplies by this value.")]
public float Intensity = 1f;
[Tooltip("Defines whether this point light volume can be moved in runtime. Disabling this option slightly improves performance.")]
public bool IsDynamic = false;
[Tooltip("For point light: XYZ = Position, W = Inverse squared range.\nFor spot light: XYZ = Position, W = Inverse squared range, negated.\nFor area light: XYZ = Position, W = Width.")]
public Vector4 PositionData;
[Tooltip("For point light: XYZW = Rotation quaternion.\nFor spot light: XYZ = Direction, W = Cone falloff.\nFor area light: XYZW = Rotation quaternion.")]
public Vector4 DirectionData;
[Tooltip("If parametric: Stores 0.\nIf uses custom LUT: Stores LUT ID with positive sign.\nIf uses custom texture: Stores texture ID with negative sign.")]
public float CustomID;
[Tooltip("Half-angle of the spotlight cone, in radians.")]
public float Angle;
[Tooltip("For point light: unused.\nFor spot light: Cos of outer angle if no custom texture, tan of outer angle otherwise.\nFor area light: 2 + Height.")]
public float AngleData;
[Tooltip("Index of the shadowmask channel used by this light. -1 means no shadowmask.")]
public sbyte ShadowmaskIndex = -1;
[Tooltip("True if this Point Light Volume added to the Point Light Volumes array in LightVolumeManager. Should be always true for the Point Light Volumes placed in editor. Helps to initialize Point Light Volumes spawned in runtime.")]
public bool IsInitialized = false;
[Tooltip("Squared range after which light will be culled. Should be recalculated by executing UpdateRange() method.")]
public float SquaredRange = 1f;
[Tooltip("Average squared lossy scale of the light. Light Source Size gets multiplied by it at the end. Updates with UpdateTransform() method.")]
public float SquaredScale = 1f;
[Tooltip("Reference to the Light Volume Manager. Needed for runtime initialization.")]
public VRCLightVolumes.LightVolumeManager LightVolumeManager;
[HideInInspector]
public bool IsIterartedThrough = false;
[HideInInspector]
public bool IsRangeDirty = false;
private Vector3 _prevPosition = Vector3.get_zero();
private Quaternion _prevRotation = Quaternion.get_identity();
private Vector3 _prevScale = Vector3.get_one();
public PointLightVolumeInstance()
{
}
private float ComputeAreaLightSquaredBoundingSphere(float width, float height, UnityEngine.Color color, float intensity, float cutoff)
{
float single = Mathf.Clamp(cutoff / (Mathf.Max(color.r, Mathf.Max(color.g, color.b)) * intensity), -6.2831855f, 6.2831855f);
float single1 = width * height;
float single2 = width * width;
float single3 = 0.25f * (single2 + height * height);
float single4 = Mathf.Tan(0.25f * single);
float single5 = single4 * single4;
float single6 = single5 * single3;
float single7 = Mathf.Sqrt(single6 * single6 + 4f * single5 * single1 * single1);
return (single7 - single6) * 0.125f / single5;
}
private float ComputePointLightSquaredBoundingSphere(UnityEngine.Color color, float intensity, float sqSize, float cutoff)
{
float single = Mathf.Max(color.r, Mathf.Max(color.g, color.b));
float single1 = Mathf.Max(6.2831855f * single * Mathf.Abs(intensity) / (cutoff * cutoff) - 1f, 0f) * sqSize;
return single1;
}
public bool IsAreaLight()
{
return (this.PositionData.w < 0f ? false : (double)this.AngleData > 1.5);
}
public bool IsCustomTexture()
{
return this.CustomID < 0f;
}
public bool IsLut()
{
return this.CustomID > 0f;
}
public bool IsParametric()
{
return this.CustomID == 0f;
}
public bool IsPointLight()
{
return (this.PositionData.w < 0f ? false : (double)this.AngleData <= 1.5);
}
public bool IsSpotLight()
{
return this.PositionData.w < 0f;
}
private void MarkRangeDirtyAndRequestUpdate()
{
this.IsRangeDirty = true;
}
private void OnDisable()
{
if (this.LightVolumeManager != null)
{
this.LightVolumeManager.RequestUpdateVolumes();
}
}
private void OnEnable()
{
if (this.LightVolumeManager != null)
{
this.LightVolumeManager.RequestUpdateVolumes();
}
}
public void SetAreaLight()
{
this.PositionData.w = Mathf.Max(Mathf.Abs(base.get_transform().get_lossyScale().x), 0.001f);
this.AngleData = 2f + Mathf.Max(Mathf.Abs(base.get_transform().get_lossyScale().y), 0.001f);
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetColor(UnityEngine.Color color)
{
this.Color = color;
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetCustomTexture(int id)
{
this.CustomID = (float)(-id - 1);
if (this.IsSpotLight())
{
this.AngleData = Mathf.Tan(this.Angle);
}
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetIntensity(float intensity)
{
this.Intensity = intensity;
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetLightSourceSize(float size)
{
if (!this.IsLut())
{
this.PositionData.w = Mathf.Sign(this.PositionData.w) * size * size;
}
else
{
this.PositionData.w = Mathf.Sign(this.PositionData.w) / (size * size);
}
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetLut(int id)
{
this.CustomID = (float)(id + 1);
this.AngleData = Mathf.Cos(this.Angle);
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetParametric()
{
this.CustomID = 0f;
this.AngleData = Mathf.Cos(this.Angle);
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetPointLight()
{
this.PositionData.w = Mathf.Abs(this.PositionData.w);
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetSpotLight(float angleDeg, float falloff)
{
this.Angle = angleDeg * 0.017453292f * 0.5f;
if (!this.IsCustomTexture())
{
this.AngleData = Mathf.Cos(this.Angle);
this.DirectionData.w = 1f / (Mathf.Cos(this.Angle * (1f - Mathf.Clamp01(falloff))) - this.AngleData);
}
else
{
this.AngleData = Mathf.Tan(this.Angle);
}
this.PositionData.w = -Mathf.Abs(this.PositionData.w);
this.MarkRangeDirtyAndRequestUpdate();
}
public void SetSpotLight(float angleDeg)
{
this.Angle = angleDeg * 0.017453292f * 0.5f;
if (!this.IsCustomTexture())
{
this.AngleData = Mathf.Cos(this.Angle);
}
else
{
this.AngleData = Mathf.Tan(this.Angle);
}
this.PositionData.w = -Mathf.Abs(this.PositionData.w);
this.MarkRangeDirtyAndRequestUpdate();
}
private void Start()
{
if ((this.IsInitialized ? false : this.LightVolumeManager != null))
{
this.LightVolumeManager.InitializePointLightVolume(this);
}
}
public void UpdatePosition()
{
Vector3 _position = base.get_transform().get_position();
this.PositionData = new Vector4(_position.x, _position.y, _position.z, this.PositionData.w);
}
public void UpdateRange()
{
float single = (this.LightVolumeManager != null ? this.LightVolumeManager.LightsBrightnessCutoff : 0.35f);
if (this.IsAreaLight())
{
this.SquaredRange = this.ComputeAreaLightSquaredBoundingSphere(Mathf.Abs(this.SquaredScale / this.PositionData.w), this.AngleData - 2f, this.Color, this.Intensity * 3.1415927f, single);
}
else if (!this.IsLut())
{
this.SquaredRange = this.ComputePointLightSquaredBoundingSphere(this.Color, this.Intensity, Mathf.Abs(this.SquaredScale * this.PositionData.w), single);
}
else
{
this.SquaredRange = Mathf.Abs(this.SquaredScale / this.PositionData.w);
}
this.IsRangeDirty = false;
}
public void UpdateRotation()
{
Quaternion _rotation = base.get_transform().get_rotation();
if (this.IsAreaLight())
{
this.DirectionData = new Vector4(_rotation.x, _rotation.y, _rotation.z, _rotation.w);
}
else if ((!this.IsSpotLight() ? false : !this.IsCustomTexture()))
{
Vector3 _forward = base.get_transform().get_forward();
this.DirectionData = new Vector4(_forward.x, _forward.y, _forward.z, this.DirectionData.w);
}
else if (!this.IsParametric())
{
_rotation = Quaternion.Inverse(_rotation);
this.DirectionData = new Vector4(_rotation.x, _rotation.y, _rotation.z, _rotation.w);
}
}
public void UpdateScale()
{
Vector3 _lossyScale = base.get_transform().get_lossyScale();
if (this.IsAreaLight())
{
this.SetAreaLight();
}
this.SquaredScale = (_lossyScale.x + _lossyScale.y + _lossyScale.z) / 3f;
this.SquaredScale *= this.SquaredScale;
this.MarkRangeDirtyAndRequestUpdate();
}
public void UpdateTransform()
{
Vector3 _position = base.get_transform().get_position();
if (this._prevPosition != _position)
{
this._prevPosition = _position;
this.UpdatePosition();
}
Quaternion _rotation = base.get_transform().get_rotation();
if (this._prevRotation != _rotation)
{
this._prevRotation = _rotation;
this.UpdateRotation();
}
Vector3 _lossyScale = base.get_transform().get_lossyScale();
if (this._prevScale != _lossyScale)
{
this._prevScale = _lossyScale;
this.UpdateScale();
}
}
}
}