ECM2
This commit is contained in:
621
Assets/VolumetricLightBeam/Scripts/SD/BeamGeometrySD.cs
Normal file
621
Assets/VolumetricLightBeam/Scripts/SD/BeamGeometrySD.cs
Normal file
@@ -0,0 +1,621 @@
|
||||
#if DEBUG
|
||||
//#define DEBUG_SHOW_MESH_NORMALS
|
||||
#endif
|
||||
#define FORCE_CURRENT_CAMERA_DEPTH_TEXTURE_MODE
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
#define VLB_SRP_SUPPORT // Comment this to disable SRP support
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
#pragma warning disable 0429, 0162 // Unreachable expression code detected (because of Noise3D.isSupported on mobile)
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[AddComponentMenu("")] // hide it from Component search
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.SD.UrlBeam)]
|
||||
public class BeamGeometrySD : BeamGeometryAbstractBase, MaterialModifier.Interface
|
||||
{
|
||||
VolumetricLightBeamSD m_Master = null;
|
||||
MeshType m_CurrentMeshType = MeshType.Shared;
|
||||
MaterialModifier.Callback m_MaterialModifierCallback = null;
|
||||
Coroutine m_CoFadeOut = null;
|
||||
|
||||
protected override VolumetricLightBeamAbstractBase GetMaster() { return m_Master; }
|
||||
|
||||
bool visible
|
||||
{
|
||||
get { return meshRenderer.enabled; }
|
||||
set { meshRenderer.enabled = value; }
|
||||
}
|
||||
|
||||
public int sortingLayerID
|
||||
{
|
||||
get { return meshRenderer.sortingLayerID; }
|
||||
set { meshRenderer.sortingLayerID = value; }
|
||||
}
|
||||
|
||||
public int sortingOrder
|
||||
{
|
||||
get { return meshRenderer.sortingOrder; }
|
||||
set { meshRenderer.sortingOrder = value; }
|
||||
}
|
||||
|
||||
public bool _INTERNAL_IsFadeOutCoroutineRunning { get { return m_CoFadeOut != null; } }
|
||||
|
||||
float ComputeFadeOutFactor(Transform camTransform)
|
||||
{
|
||||
if (m_Master.isFadeOutEnabled)
|
||||
{
|
||||
float distanceCamToBeam = Vector3.SqrMagnitude(meshRenderer.bounds.center - camTransform.position);
|
||||
return Mathf.InverseLerp(m_Master.fadeOutEnd * m_Master.fadeOutEnd, m_Master.fadeOutBegin * m_Master.fadeOutBegin, distanceCamToBeam);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoUpdateFadeOut()
|
||||
{
|
||||
while (m_Master.isFadeOutEnabled)
|
||||
{
|
||||
ComputeFadeOutFactor();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetFadeOutFactorProp(1.0f);
|
||||
m_CoFadeOut = null;
|
||||
}
|
||||
|
||||
void ComputeFadeOutFactor()
|
||||
{
|
||||
var camTransform = Config.Instance.fadeOutCameraTransform;
|
||||
if (camTransform)
|
||||
{
|
||||
float fadeOutFactor = ComputeFadeOutFactor(camTransform);
|
||||
SetFadeOutFactorProp(fadeOutFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFadeOutFactorProp(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void SetFadeOutFactorProp(float value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
meshRenderer.enabled = true;
|
||||
|
||||
MaterialChangeStart();
|
||||
SetMaterialProp(ShaderProperties.SD.FadeOutFactor, value);
|
||||
MaterialChangeStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
meshRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void StopFadeOutCoroutine()
|
||||
{
|
||||
if (m_CoFadeOut != null)
|
||||
{
|
||||
StopCoroutine(m_CoFadeOut);
|
||||
m_CoFadeOut = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void RestartFadeOutCoroutine()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
StopFadeOutCoroutine();
|
||||
|
||||
if (m_Master && m_Master.isFadeOutEnabled)
|
||||
{
|
||||
m_CoFadeOut = StartCoroutine(CoUpdateFadeOut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMasterEnable()
|
||||
{
|
||||
visible = true;
|
||||
RestartFadeOutCoroutine();
|
||||
}
|
||||
|
||||
public void OnMasterDisable()
|
||||
{
|
||||
StopFadeOutCoroutine();
|
||||
visible = false;
|
||||
}
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
Camera m_CurrentCameraRenderingSRP = null;
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
SRPHelper.UnregisterOnBeginCameraRendering(OnBeginCameraRenderingSRP);
|
||||
m_CurrentCameraRenderingSRP = null;
|
||||
}
|
||||
|
||||
public static bool isCustomRenderPipelineSupported { get { return true; } }
|
||||
#else
|
||||
public static bool isCustomRenderPipelineSupported { get { return false; } }
|
||||
#endif
|
||||
|
||||
bool shouldUseGPUInstancedMaterial
|
||||
{ get {
|
||||
return m_Master._INTERNAL_DynamicOcclusionMode != MaterialManager.SD.DynamicOcclusion.DepthTexture // sampler cannot be passed to shader as instanced property
|
||||
&& Config.Instance.GetActualRenderingMode(ShaderMode.SD) == RenderingMode.GPUInstancing;
|
||||
}}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// When a GAO is disabled, all its coroutines are killed, so renable them on OnEnable.
|
||||
RestartFadeOutCoroutine();
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
SRPHelper.RegisterOnBeginCameraRendering(OnBeginCameraRenderingSRP);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Initialize(VolumetricLightBeamSD master)
|
||||
{
|
||||
Debug.Assert(master != null);
|
||||
|
||||
var customHideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
m_Master = master;
|
||||
|
||||
transform.SetParent(master.transform, false);
|
||||
|
||||
meshRenderer = gameObject.GetOrAddComponent<MeshRenderer>();
|
||||
meshRenderer.hideFlags = customHideFlags;
|
||||
meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
meshRenderer.receiveShadows = false;
|
||||
meshRenderer.reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off; // different reflection probes could break batching with GPU Instancing
|
||||
meshRenderer.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;
|
||||
|
||||
if (!shouldUseGPUInstancedMaterial)
|
||||
{
|
||||
m_CustomMaterial = Config.Instance.NewMaterialTransient(ShaderMode.SD, gpuInstanced:false);
|
||||
ApplyMaterial();
|
||||
}
|
||||
|
||||
if (SortingLayer.IsValid(m_Master.sortingLayerID))
|
||||
sortingLayerID = m_Master.sortingLayerID;
|
||||
else
|
||||
Debug.LogError(string.Format("Beam '{0}' has an invalid sortingLayerID ({1}). Please fix it by setting a valid layer.", Utils.GetPath(m_Master.transform), m_Master.sortingLayerID));
|
||||
|
||||
sortingOrder = m_Master.sortingOrder;
|
||||
|
||||
meshFilter = gameObject.GetOrAddComponent<MeshFilter>();
|
||||
meshFilter.hideFlags = customHideFlags;
|
||||
|
||||
gameObject.hideFlags = customHideFlags;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.GameObjectUtility.SetStaticEditorFlags(gameObject, master.GetStaticEditorFlagsForSubObjects());
|
||||
gameObject.SetSameSceneVisibilityStatesThan(master.gameObject);
|
||||
#endif
|
||||
|
||||
RestartFadeOutCoroutine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the cone mesh and calls UpdateMaterialAndBounds.
|
||||
/// Since this process involves recreating a new mesh, make sure to not call it at every frame during playtime.
|
||||
/// </summary>
|
||||
public void RegenerateMesh(bool masterEnabled)
|
||||
{
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
if (Config.Instance.geometryOverrideLayer)
|
||||
gameObject.layer = Config.Instance.geometryLayerID;
|
||||
else
|
||||
gameObject.layer = m_Master.gameObject.layer;
|
||||
|
||||
gameObject.tag = Config.Instance.geometryTag;
|
||||
|
||||
if (coneMesh && m_CurrentMeshType == MeshType.Custom)
|
||||
{
|
||||
DestroyImmediate(coneMesh);
|
||||
}
|
||||
|
||||
m_CurrentMeshType = m_Master.geomMeshType;
|
||||
|
||||
switch (m_Master.geomMeshType)
|
||||
{
|
||||
case MeshType.Custom:
|
||||
{
|
||||
coneMesh = MeshGenerator.GenerateConeZ_Radii(1f, 1f, 1f, m_Master.geomCustomSides, m_Master.geomCustomSegments, m_Master.geomCap, Config.Instance.SD_requiresDoubleSidedMesh);
|
||||
coneMesh.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
meshFilter.mesh = coneMesh;
|
||||
break;
|
||||
}
|
||||
case MeshType.Shared:
|
||||
{
|
||||
coneMesh = GlobalMeshSD.Get();
|
||||
meshFilter.sharedMesh = coneMesh;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug.LogError("Unsupported MeshType");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateMaterialAndBounds();
|
||||
|
||||
visible = masterEnabled;
|
||||
}
|
||||
|
||||
Vector3 ComputeLocalMatrix()
|
||||
{
|
||||
// In the VS, we compute the vertices so the whole beam fits into a fixed 2x2x1 box.
|
||||
// We have to apply some scaling to get the proper beam size.
|
||||
// This way we have the proper bounds without having to recompute specific bounds foreach beam.
|
||||
var maxRadius = Mathf.Max(m_Master.coneRadiusStart, m_Master.coneRadiusEnd);
|
||||
transform.localScale = new Vector3(maxRadius, maxRadius, m_Master.maxGeometryDistance);
|
||||
transform.localRotation = m_Master.beamInternalLocalRotation;
|
||||
|
||||
return transform.localScale;
|
||||
}
|
||||
|
||||
bool isNoiseEnabled { get { return m_Master.isNoiseEnabled && m_Master.noiseIntensity > 0f && Noise3D.isSupported; } } // test Noise3D.isSupported the last
|
||||
|
||||
#pragma warning disable 0162
|
||||
bool isDepthBlendEnabled { get { return BatchingHelper.forceEnableDepthBlend || m_Master.depthBlendDistance > 0f; } }
|
||||
#pragma warning restore 0162
|
||||
|
||||
MaterialManager.StaticPropertiesSD ComputeMaterialStaticProperties()
|
||||
{
|
||||
var colorGradient = MaterialManager.ColorGradient.Off;
|
||||
if (m_Master.colorMode == ColorMode.Gradient)
|
||||
{
|
||||
var precision = Utils.GetFloatPackingPrecision();
|
||||
colorGradient = precision == Utils.FloatPackingPrecision.High ? MaterialManager.ColorGradient.MatrixHigh : MaterialManager.ColorGradient.MatrixLow;
|
||||
}
|
||||
|
||||
Debug.Assert((int)BlendingMode.Additive == (int)MaterialManager.BlendingMode.Additive);
|
||||
Debug.Assert((int)BlendingMode.SoftAdditive == (int)MaterialManager.BlendingMode.SoftAdditive);
|
||||
Debug.Assert((int)BlendingMode.TraditionalTransparency == (int)MaterialManager.BlendingMode.TraditionalTransparency);
|
||||
|
||||
return new MaterialManager.StaticPropertiesSD
|
||||
{
|
||||
blendingMode = (MaterialManager.BlendingMode)m_Master.blendingMode,
|
||||
noise3D = isNoiseEnabled ? MaterialManager.Noise3D.On : MaterialManager.Noise3D.Off,
|
||||
depthBlend = isDepthBlendEnabled ? MaterialManager.SD.DepthBlend.On : MaterialManager.SD.DepthBlend.Off,
|
||||
colorGradient = colorGradient,
|
||||
dynamicOcclusion = m_Master._INTERNAL_DynamicOcclusionMode_Runtime,
|
||||
meshSkewing = m_Master.hasMeshSkewing ? MaterialManager.SD.MeshSkewing.On : MaterialManager.SD.MeshSkewing.Off,
|
||||
shaderAccuracy = (m_Master.shaderAccuracy == ShaderAccuracy.Fast) ? MaterialManager.SD.ShaderAccuracy.Fast : MaterialManager.SD.ShaderAccuracy.High
|
||||
};
|
||||
}
|
||||
|
||||
bool ApplyMaterial()
|
||||
{
|
||||
var staticProps = ComputeMaterialStaticProperties();
|
||||
|
||||
Material mat = null;
|
||||
if (!shouldUseGPUInstancedMaterial)
|
||||
{
|
||||
mat = m_CustomMaterial;
|
||||
if(mat)
|
||||
staticProps.ApplyToMaterial(mat);
|
||||
}
|
||||
else
|
||||
{
|
||||
mat = MaterialManager.GetInstancedMaterial(m_Master._INTERNAL_InstancedMaterialGroupID, ref staticProps);
|
||||
}
|
||||
|
||||
meshRenderer.material = mat;
|
||||
return mat != null;
|
||||
}
|
||||
|
||||
public void SetMaterialProp(int nameID, float value)
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
m_CustomMaterial.SetFloat(nameID, value);
|
||||
else
|
||||
MaterialManager.materialPropertyBlock.SetFloat(nameID, value);
|
||||
}
|
||||
|
||||
public void SetMaterialProp(int nameID, Vector4 value)
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
m_CustomMaterial.SetVector(nameID, value);
|
||||
else
|
||||
MaterialManager.materialPropertyBlock.SetVector(nameID, value);
|
||||
}
|
||||
|
||||
public void SetMaterialProp(int nameID, Color value)
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
m_CustomMaterial.SetColor(nameID, value);
|
||||
else
|
||||
MaterialManager.materialPropertyBlock.SetColor(nameID, value);
|
||||
}
|
||||
|
||||
public void SetMaterialProp(int nameID, Matrix4x4 value)
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
m_CustomMaterial.SetMatrix(nameID, value);
|
||||
else
|
||||
MaterialManager.materialPropertyBlock.SetMatrix(nameID, value);
|
||||
}
|
||||
|
||||
public void SetMaterialProp(int nameID, Texture value)
|
||||
{
|
||||
if (m_CustomMaterial)
|
||||
m_CustomMaterial.SetTexture(nameID, value);
|
||||
else
|
||||
Debug.LogError("Setting a Texture property to a GPU instanced material is not supported");
|
||||
}
|
||||
|
||||
void MaterialChangeStart()
|
||||
{
|
||||
if (m_CustomMaterial == null)
|
||||
meshRenderer.GetPropertyBlock(MaterialManager.materialPropertyBlock);
|
||||
}
|
||||
|
||||
void MaterialChangeStop()
|
||||
{
|
||||
if (m_CustomMaterial == null)
|
||||
meshRenderer.SetPropertyBlock(MaterialManager.materialPropertyBlock);
|
||||
}
|
||||
|
||||
public void SetDynamicOcclusionCallback(string shaderKeyword, MaterialModifier.Callback cb)
|
||||
{
|
||||
m_MaterialModifierCallback = cb;
|
||||
|
||||
if (m_CustomMaterial)
|
||||
{
|
||||
m_CustomMaterial.SetKeywordEnabled(shaderKeyword, cb != null);
|
||||
|
||||
if (cb != null)
|
||||
cb(this);
|
||||
}
|
||||
else
|
||||
UpdateMaterialAndBounds();
|
||||
}
|
||||
|
||||
public void UpdateMaterialAndBounds()
|
||||
{
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
if (ApplyMaterial() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialChangeStart();
|
||||
{
|
||||
if (m_CustomMaterial == null)
|
||||
{
|
||||
if(m_MaterialModifierCallback != null)
|
||||
m_MaterialModifierCallback(this);
|
||||
}
|
||||
|
||||
float slopeRad = (m_Master.coneAngle * Mathf.Deg2Rad) / 2; // use coneAngle (instead of spotAngle) which is more correct with the geometry
|
||||
SetMaterialProp(ShaderProperties.SD.ConeSlopeCosSin, new Vector2(Mathf.Cos(slopeRad), Mathf.Sin(slopeRad)));
|
||||
|
||||
// kMinRadius and kMinApexOffset prevents artifacts when fresnel computation is done in the vertex shader
|
||||
const float kMinRadius = 0.0001f;
|
||||
var coneRadius = new Vector2(Mathf.Max(m_Master.coneRadiusStart, kMinRadius), Mathf.Max(m_Master.coneRadiusEnd, kMinRadius));
|
||||
SetMaterialProp(ShaderProperties.ConeRadius, coneRadius);
|
||||
|
||||
const float kMinApexOffset = 0.0001f;
|
||||
float nonNullApex = Mathf.Sign(m_Master.coneApexOffsetZ) * Mathf.Max(Mathf.Abs(m_Master.coneApexOffsetZ), kMinApexOffset);
|
||||
SetMaterialProp(ShaderProperties.ConeGeomProps, new Vector2(nonNullApex, m_Master.geomSides));
|
||||
|
||||
if (m_Master.usedColorMode == ColorMode.Flat)
|
||||
{
|
||||
SetMaterialProp(ShaderProperties.ColorFlat, m_Master.color);
|
||||
}
|
||||
else
|
||||
{
|
||||
var precision = Utils.GetFloatPackingPrecision();
|
||||
m_ColorGradientMatrix = m_Master.colorGradient.SampleInMatrix((int)precision);
|
||||
// pass the gradient matrix in OnWillRenderObject()
|
||||
}
|
||||
|
||||
float intensityInside, intensityOutside;
|
||||
m_Master.GetInsideAndOutsideIntensity(out intensityInside, out intensityOutside);
|
||||
SetMaterialProp(ShaderProperties.SD.AlphaInside, intensityInside);
|
||||
SetMaterialProp(ShaderProperties.SD.AlphaOutside, intensityOutside);
|
||||
SetMaterialProp(ShaderProperties.SD.AttenuationLerpLinearQuad, m_Master.attenuationLerpLinearQuad);
|
||||
SetMaterialProp(ShaderProperties.DistanceFallOff, new Vector3(m_Master.fallOffStart, m_Master.fallOffEnd, m_Master.maxGeometryDistance));
|
||||
SetMaterialProp(ShaderProperties.SD.DistanceCamClipping, m_Master.cameraClippingDistance);
|
||||
SetMaterialProp(ShaderProperties.SD.FresnelPow, Mathf.Max(0.001f, m_Master.fresnelPow)); // no pow 0, otherwise will generate inf fresnel and issues on iOS
|
||||
SetMaterialProp(ShaderProperties.SD.GlareBehind, m_Master.glareBehind);
|
||||
SetMaterialProp(ShaderProperties.SD.GlareFrontal, m_Master.glareFrontal);
|
||||
SetMaterialProp(ShaderProperties.SD.DrawCap, m_Master.geomCap ? 1 : 0);
|
||||
SetMaterialProp(ShaderProperties.SD.TiltVector, m_Master.tiltFactor);
|
||||
SetMaterialProp(ShaderProperties.SD.AdditionalClippingPlaneWS, m_Master.additionalClippingPlane);
|
||||
|
||||
if (Config.Instance.isHDRPExposureWeightSupported)
|
||||
{
|
||||
SetMaterialProp(ShaderProperties.HDRPExposureWeight, m_Master.hdrpExposureWeight);
|
||||
}
|
||||
|
||||
if (isDepthBlendEnabled)
|
||||
{
|
||||
SetMaterialProp(ShaderProperties.SD.DepthBlendDistance, m_Master.depthBlendDistance);
|
||||
}
|
||||
|
||||
if (isNoiseEnabled)
|
||||
{
|
||||
Noise3D.LoadIfNeeded();
|
||||
|
||||
var noiseVelocity = m_Master.noiseVelocityUseGlobal ? Config.Instance.globalNoiseVelocity : m_Master.noiseVelocityLocal;
|
||||
var noiseScale = m_Master.noiseScaleUseGlobal ? Config.Instance.globalNoiseScale : m_Master.noiseScaleLocal;
|
||||
|
||||
SetMaterialProp(ShaderProperties.NoiseVelocityAndScale, new Vector4(
|
||||
noiseVelocity.x,
|
||||
noiseVelocity.y,
|
||||
noiseVelocity.z,
|
||||
noiseScale));
|
||||
|
||||
SetMaterialProp(ShaderProperties.NoiseParam, new Vector2(
|
||||
m_Master.noiseIntensity,
|
||||
m_Master.noiseMode == NoiseMode.WorldSpace ? 0f : 1f));
|
||||
}
|
||||
|
||||
var localScale = ComputeLocalMatrix(); // compute matrix before sending it to the shader
|
||||
|
||||
if (m_Master.hasMeshSkewing)
|
||||
{
|
||||
var localForwardDirectionNormalized = m_Master.skewingLocalForwardDirectionNormalized;
|
||||
SetMaterialProp(ShaderProperties.SD.LocalForwardDirection, localForwardDirectionNormalized);
|
||||
|
||||
if (coneMesh != null) // coneMesh can be null few frames with Dynamic Occlusion & GPU Instancing
|
||||
{
|
||||
var localForwardDirectionN = localForwardDirectionNormalized;
|
||||
localForwardDirectionN /= localForwardDirectionN.z;
|
||||
localForwardDirectionN *= m_Master.fallOffEnd;
|
||||
localForwardDirectionN.x /= localScale.x;
|
||||
localForwardDirectionN.y /= localScale.y;
|
||||
|
||||
var bounds = MeshGenerator.ComputeBounds(1f, 1f, 1f);
|
||||
var min = bounds.min;
|
||||
var max = bounds.max;
|
||||
|
||||
if (localForwardDirectionN.x > 0.0f) max.x += localForwardDirectionN.x;
|
||||
else min.x += localForwardDirectionN.x;
|
||||
|
||||
if (localForwardDirectionN.y > 0.0f) max.y += localForwardDirectionN.y;
|
||||
else min.y += localForwardDirectionN.y;
|
||||
|
||||
bounds.min = min;
|
||||
bounds.max = max;
|
||||
coneMesh.bounds = bounds;
|
||||
}
|
||||
}
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
// This update is to make QA test 'ReflectionObliqueProjection' pass
|
||||
UpdateMatricesPropertiesForGPUInstancingSRP();
|
||||
#endif
|
||||
}
|
||||
MaterialChangeStop();
|
||||
|
||||
#if DEBUG_SHOW_MESH_NORMALS
|
||||
for (int vertexInd = 0; vertexInd < coneMesh.vertexCount; vertexInd++)
|
||||
{
|
||||
var vertex = coneMesh.vertices[vertexInd];
|
||||
|
||||
// apply modification done inside VS
|
||||
vertex.x *= Mathf.Lerp(coneRadius.x, coneRadius.y, vertex.z);
|
||||
vertex.y *= Mathf.Lerp(coneRadius.x, coneRadius.y, vertex.z);
|
||||
vertex.z *= m_Master.fallOffEnd;
|
||||
|
||||
var cosSinFlat = new Vector2(vertex.x, vertex.y).normalized;
|
||||
var normal = new Vector3(cosSinFlat.x * Mathf.Cos(slopeRad), cosSinFlat.y * Mathf.Cos(slopeRad), -Mathf.Sin(slopeRad)).normalized;
|
||||
|
||||
vertex = transform.TransformPoint(vertex);
|
||||
normal = transform.TransformDirection(normal);
|
||||
Debug.DrawRay(vertex, normal * 0.25f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
void UpdateMatricesPropertiesForGPUInstancingSRP()
|
||||
{
|
||||
if (SRPHelper.IsUsingCustomRenderPipeline() && Config.Instance.GetActualRenderingMode(ShaderMode.SD) == RenderingMode.GPUInstancing)
|
||||
{
|
||||
SetMaterialProp(ShaderProperties.LocalToWorldMatrix, transform.localToWorldMatrix);
|
||||
SetMaterialProp(ShaderProperties.WorldToLocalMatrix, transform.worldToLocalMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
void OnBeginCameraRenderingSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera cam)
|
||||
#else
|
||||
void OnBeginCameraRenderingSRP(Camera cam)
|
||||
#endif
|
||||
{
|
||||
m_CurrentCameraRenderingSRP = cam;
|
||||
}
|
||||
#endif
|
||||
|
||||
void OnWillRenderObject()
|
||||
{
|
||||
Camera currentCam = null;
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
if (SRPHelper.IsUsingCustomRenderPipeline())
|
||||
{
|
||||
currentCam = m_CurrentCameraRenderingSRP;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
currentCam = Camera.current;
|
||||
}
|
||||
|
||||
OnWillCameraRenderThisBeam(currentCam);
|
||||
}
|
||||
|
||||
void OnWillCameraRenderThisBeam(Camera cam)
|
||||
{
|
||||
if (m_Master && cam)
|
||||
{
|
||||
if (
|
||||
#if UNITY_EDITOR
|
||||
Utils.IsEditorCamera(cam) || // make sure to call UpdateCameraRelatedProperties for editor scene camera
|
||||
#endif
|
||||
cam.enabled) // prevent from doing stuff when we render from a previous DynamicOcclusionDepthBuffer's DepthCamera, because the DepthCamera are disabled
|
||||
{
|
||||
UpdateCameraRelatedProperties(cam);
|
||||
m_Master._INTERNAL_OnWillCameraRenderThisBeam(cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCameraRelatedProperties(Camera cam)
|
||||
{
|
||||
if (cam && m_Master)
|
||||
{
|
||||
MaterialChangeStart();
|
||||
{
|
||||
var camPosOS = m_Master.transform.InverseTransformPoint(cam.transform.position);
|
||||
|
||||
var camForwardVectorOSN = transform.InverseTransformDirection(cam.transform.forward).normalized;
|
||||
float camIsInsideBeamFactor = cam.orthographic ? -1f : m_Master.GetInsideBeamFactorFromObjectSpacePos(camPosOS);
|
||||
SetMaterialProp(ShaderProperties.SD.CameraParams, new Vector4(camForwardVectorOSN.x, camForwardVectorOSN.y, camForwardVectorOSN.z, camIsInsideBeamFactor));
|
||||
|
||||
#if VLB_SRP_SUPPORT
|
||||
// This update is to be able to move beams without trackChangesDuringPlaytime enabled with SRP & GPU Instancing
|
||||
UpdateMatricesPropertiesForGPUInstancingSRP();
|
||||
#endif
|
||||
|
||||
if (m_Master.usedColorMode == ColorMode.Gradient)
|
||||
{
|
||||
// Send the gradient matrix every frame since it's not a shader's property
|
||||
SetMaterialProp(ShaderProperties.ColorGradientMatrix, m_ColorGradientMatrix);
|
||||
}
|
||||
}
|
||||
MaterialChangeStop();
|
||||
|
||||
#if FORCE_CURRENT_CAMERA_DEPTH_TEXTURE_MODE
|
||||
if (m_Master.depthBlendDistance > 0f)
|
||||
cam.depthTextureMode |= DepthTextureMode.Depth;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public int _EDITOR_InstancedMaterialID { get { return ComputeMaterialStaticProperties().GetMaterialID(); } }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
12
Assets/VolumetricLightBeam/Scripts/SD/BeamGeometrySD.cs.meta
Normal file
12
Assets/VolumetricLightBeam/Scripts/SD/BeamGeometrySD.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50a48a2b69575db4c8b5bf5aa8186d89
|
||||
timeCreated: 1504793414
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,185 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[AddComponentMenu("")] // hide it from Component search
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(VolumetricLightBeamSD))]
|
||||
public abstract class DynamicOcclusionAbstractBase : MonoBehaviour
|
||||
{
|
||||
public const string ClassName = "DynamicOcclusionAbstractBase";
|
||||
|
||||
/// <summary>
|
||||
/// How often will the occlusion be processed?
|
||||
/// Try to update the occlusion as rarely as possible to keep good performance.
|
||||
/// </summary>
|
||||
public DynamicOcclusionUpdateRate updateRate = Consts.DynOcclusion.UpdateRateDefault;
|
||||
|
||||
/// <summary>
|
||||
/// How many frames we wait between 2 occlusion tests?
|
||||
/// If you want your beam to be super responsive to the changes of your environment, update it every frame by setting 1.
|
||||
/// If you want to save on performance, we recommend to wait few frames between each update by setting a higher value.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("waitFrameCount")]
|
||||
public int waitXFrames = Consts.DynOcclusion.WaitFramesCountDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Manually process the occlusion.
|
||||
/// You have to call this function in order to update the occlusion when using DynamicOcclusionUpdateRate.Never.
|
||||
/// </summary>
|
||||
public void ProcessOcclusionManually() { ProcessOcclusion(ProcessOcclusionSource.User); }
|
||||
|
||||
public event System.Action onOcclusionProcessed;
|
||||
|
||||
public static bool _INTERNAL_ApplyRandomFrameOffset = true;
|
||||
|
||||
|
||||
protected enum ProcessOcclusionSource
|
||||
{
|
||||
RenderLoop,
|
||||
OnEnable,
|
||||
EditorUpdate,
|
||||
User,
|
||||
}
|
||||
|
||||
protected void ProcessOcclusion(ProcessOcclusionSource source)
|
||||
{
|
||||
if (!Config.Instance.featureEnabledDynamicOcclusion)
|
||||
return;
|
||||
|
||||
if (m_LastFrameRendered == Time.frameCount && Application.isPlaying && source == ProcessOcclusionSource.OnEnable)
|
||||
return; // allow to call ProcessOcclusion from OnEnable (when disabling/enabling multiple a beam on the same frame) without generating an error
|
||||
|
||||
Debug.Assert(!Application.isPlaying || m_LastFrameRendered != Time.frameCount, "ProcessOcclusion has been called twice on the same frame, which is forbidden");
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
bool occlusionSuccess = OnProcessOcclusion(source);
|
||||
|
||||
if(onOcclusionProcessed != null)
|
||||
onOcclusionProcessed();
|
||||
|
||||
if (m_Master)
|
||||
{
|
||||
Debug.Assert(m_MaterialModifierCallbackCached != null);
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), occlusionSuccess ? m_MaterialModifierCallbackCached : (MaterialModifier.Callback)(null));
|
||||
}
|
||||
|
||||
if (updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
|
||||
m_TransformPacked = transform.GetWorldPacked();
|
||||
|
||||
bool firstTime = m_LastFrameRendered < 0;
|
||||
m_LastFrameRendered = Time.frameCount;
|
||||
|
||||
if (firstTime && _INTERNAL_ApplyRandomFrameOffset)
|
||||
{
|
||||
m_LastFrameRendered += Random.Range(0, waitXFrames); // add a random offset to prevent from updating texture for all beams having the same wait value
|
||||
}
|
||||
}
|
||||
|
||||
TransformUtils.Packed m_TransformPacked;
|
||||
int m_LastFrameRendered = int.MinValue;
|
||||
public int _INTERNAL_LastFrameRendered { get { return m_LastFrameRendered; } } // for unit tests
|
||||
protected VolumetricLightBeamSD m_Master = null;
|
||||
protected MaterialModifier.Callback m_MaterialModifierCallbackCached = null;
|
||||
|
||||
protected abstract string GetShaderKeyword();
|
||||
protected abstract MaterialManager.SD.DynamicOcclusion GetDynamicOcclusionMode();
|
||||
|
||||
protected abstract bool OnProcessOcclusion(ProcessOcclusionSource source);
|
||||
protected abstract void OnModifyMaterialCallback(MaterialModifier.Interface owner);
|
||||
protected abstract void OnEnablePostValidate();
|
||||
|
||||
|
||||
protected virtual void OnValidateProperties()
|
||||
{
|
||||
waitXFrames = Mathf.Clamp(waitXFrames, 1, 60);
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
m_Master = GetComponent<VolumetricLightBeamSD>();
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = GetDynamicOcclusionMode();
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = MaterialManager.SD.DynamicOcclusion.Off;
|
||||
DisableOcclusion();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// cache the delegate to prevent from being inlined as '() => OnModifyMaterialCallback' when calling _INTERNAL_SetDynamicOcclusionCallback and from generating GC garbage
|
||||
m_MaterialModifierCallbackCached = OnModifyMaterialCallback;
|
||||
|
||||
OnValidateProperties();
|
||||
|
||||
OnEnablePostValidate();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
m_Master.onWillCameraRenderThisBeam += OnWillCameraRender;
|
||||
|
||||
if (!updateRate.HasFlag(DynamicOcclusionUpdateRate.Never))
|
||||
m_Master.RegisterOnBeamGeometryInitializedCallback(() => ProcessOcclusion(ProcessOcclusionSource.OnEnable));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
m_Master.onWillCameraRenderThisBeam -= OnWillCameraRender;
|
||||
}
|
||||
|
||||
DisableOcclusion();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
OnValidateProperties();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void OnWillCameraRender(Camera cam)
|
||||
{
|
||||
Debug.Assert(Application.isPlaying);
|
||||
|
||||
if (cam != null && cam.enabled
|
||||
&& Time.frameCount != m_LastFrameRendered) // prevent from updating multiple times if there are more than 1 camera
|
||||
{
|
||||
bool shouldUpdate = false;
|
||||
|
||||
if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
|
||||
{
|
||||
if (!m_TransformPacked.IsSame(transform))
|
||||
shouldUpdate = true;
|
||||
}
|
||||
|
||||
if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.EveryXFrames))
|
||||
{
|
||||
if (Time.frameCount >= m_LastFrameRendered + waitXFrames)
|
||||
shouldUpdate = true;
|
||||
}
|
||||
|
||||
if (shouldUpdate)
|
||||
ProcessOcclusion(ProcessOcclusionSource.RenderLoop);
|
||||
}
|
||||
}
|
||||
|
||||
void DisableOcclusion()
|
||||
{
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c87e171f8942d184e958eba76fd39108
|
||||
timeCreated: 1510650372
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.SD.UrlDynamicOcclusionDepthBuffer)]
|
||||
[AddComponentMenu(Consts.Help.SD.AddComponentMenuDynamicOcclusionDepthBuffer)]
|
||||
public class DynamicOcclusionDepthBuffer : DynamicOcclusionAbstractBase
|
||||
{
|
||||
public new const string ClassName = "DynamicOcclusionDepthBuffer";
|
||||
|
||||
/// <summary>
|
||||
/// The beam can only be occluded by objects located on the layers matching this mask.
|
||||
/// It's very important to set it as restrictive as possible (checking only the layers which are necessary)
|
||||
/// to perform a more efficient process in order to increase the performance.
|
||||
/// It should NOT include the layer on which the beams are generated.
|
||||
/// </summary>
|
||||
public LayerMask layerMask = Consts.DynOcclusion.LayerMaskDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the virtual camera will use occlusion culling during rendering from the beam's POV.
|
||||
/// </summary>
|
||||
public bool useOcclusionCulling = Consts.DynOcclusion.DepthBufferOcclusionCullingDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Controls how large the depth texture captured by the virtual camera is.
|
||||
/// The lower the resolution, the better the performance, but the less accurate the rendering.
|
||||
/// </summary>
|
||||
public int depthMapResolution = Consts.DynOcclusion.DepthBufferDepthMapResolutionDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Fade out the beam before the occlusion surface in order to soften the transition.
|
||||
/// </summary>
|
||||
public float fadeDistanceToSurface = Consts.DynOcclusion.DepthBufferFadeDistanceToSurfaceDefault;
|
||||
|
||||
|
||||
protected override string GetShaderKeyword() { return ShaderKeywords.SD.OcclusionDepthTexture; }
|
||||
protected override MaterialManager.SD.DynamicOcclusion GetDynamicOcclusionMode() { return MaterialManager.SD.DynamicOcclusion.DepthTexture; }
|
||||
|
||||
Camera m_DepthCamera = null;
|
||||
bool m_NeedToUpdateOcclusionNextFrame = false;
|
||||
|
||||
void ProcessOcclusionInternal()
|
||||
{
|
||||
UpdateDepthCameraPropertiesAccordingToBeam();
|
||||
m_DepthCamera.Render();
|
||||
}
|
||||
|
||||
protected override bool OnProcessOcclusion(ProcessOcclusionSource source)
|
||||
{
|
||||
Debug.Assert(m_Master && m_DepthCamera);
|
||||
|
||||
if (SRPHelper.IsUsingCustomRenderPipeline()) // Recursive rendering is not supported on SRP
|
||||
m_NeedToUpdateOcclusionNextFrame = true;
|
||||
else
|
||||
ProcessOcclusionInternal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (m_NeedToUpdateOcclusionNextFrame && m_Master && m_DepthCamera
|
||||
&& Time.frameCount > 1) // fix NullReferenceException in UnityEngine.Rendering.Universal.Internal.CopyDepthPass.Execute when using SRP
|
||||
{
|
||||
ProcessOcclusionInternal();
|
||||
m_NeedToUpdateOcclusionNextFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDepthCameraPropertiesAccordingToBeam()
|
||||
{
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
Utils.SetupDepthCamera(m_DepthCamera
|
||||
, m_Master.coneApexOffsetZ, m_Master.maxGeometryDistance, m_Master.coneRadiusStart, m_Master.coneRadiusEnd
|
||||
, m_Master.beamLocalForward, m_Master.GetLossyScale(), m_Master.IsScalable(), m_Master.beamInternalLocalRotation
|
||||
, true);
|
||||
}
|
||||
|
||||
public bool HasLayerMaskIssues()
|
||||
{
|
||||
if(Config.Instance.geometryOverrideLayer)
|
||||
{
|
||||
int layerBit = 1 << Config.Instance.geometryLayerID;
|
||||
return ((layerMask.value & layerBit) == layerBit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnValidateProperties()
|
||||
{
|
||||
base.OnValidateProperties();
|
||||
depthMapResolution = Mathf.Clamp(Mathf.NextPowerOfTwo(depthMapResolution), 8, 2048);
|
||||
fadeDistanceToSurface = Mathf.Max(fadeDistanceToSurface, 0f);
|
||||
}
|
||||
|
||||
void InstantiateOrActivateDepthCamera()
|
||||
{
|
||||
if (m_DepthCamera != null)
|
||||
{
|
||||
m_DepthCamera.gameObject.SetActive(true); // active it in case it has been disabled by OnDisable()
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete old depth cameras when duplicating the GAO
|
||||
gameObject.ForeachComponentsInDirectChildrenOnly<Camera>(cam => DestroyImmediate(cam.gameObject), true);
|
||||
|
||||
m_DepthCamera = Utils.NewWithComponent<Camera>("Depth Camera");
|
||||
|
||||
if (m_DepthCamera && m_Master)
|
||||
{
|
||||
m_DepthCamera.enabled = false;
|
||||
m_DepthCamera.cullingMask = layerMask;
|
||||
m_DepthCamera.clearFlags = CameraClearFlags.Depth;
|
||||
m_DepthCamera.depthTextureMode = DepthTextureMode.Depth;
|
||||
m_DepthCamera.renderingPath = RenderingPath.VertexLit; // faster
|
||||
m_DepthCamera.useOcclusionCulling = useOcclusionCulling;
|
||||
m_DepthCamera.gameObject.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
m_DepthCamera.transform.SetParent(transform, false);
|
||||
Config.Instance.SetURPScriptableRendererIndexToDepthCamera(m_DepthCamera);
|
||||
|
||||
var rt = new RenderTexture(depthMapResolution, depthMapResolution, 16, RenderTextureFormat.Depth);
|
||||
m_DepthCamera.targetTexture = rt;
|
||||
|
||||
UpdateDepthCameraPropertiesAccordingToBeam();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.GameObjectUtility.SetStaticEditorFlags(m_DepthCamera.gameObject, m_Master.GetStaticEditorFlagsForSubObjects());
|
||||
m_DepthCamera.gameObject.SetSameSceneVisibilityStatesThan(m_Master.gameObject);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnablePostValidate()
|
||||
{
|
||||
InstantiateOrActivateDepthCamera();
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
if (m_DepthCamera) m_DepthCamera.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
MarkMaterialAsDirty();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
DestroyDepthCamera();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
MarkMaterialAsDirty();
|
||||
#endif
|
||||
}
|
||||
|
||||
void DestroyDepthCamera()
|
||||
{
|
||||
if (m_DepthCamera)
|
||||
{
|
||||
if (m_DepthCamera.targetTexture)
|
||||
{
|
||||
m_DepthCamera.targetTexture.Release();
|
||||
DestroyImmediate(m_DepthCamera.targetTexture);
|
||||
m_DepthCamera.targetTexture = null;
|
||||
}
|
||||
|
||||
DestroyImmediate(m_DepthCamera.gameObject); // Make sure to delete the GAO
|
||||
m_DepthCamera = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnModifyMaterialCallback(MaterialModifier.Interface owner)
|
||||
{
|
||||
Debug.Assert(owner != null);
|
||||
owner.SetMaterialProp(ShaderProperties.SD.DynamicOcclusionDepthTexture, m_DepthCamera.targetTexture);
|
||||
var scale = m_Master.GetLossyScale();
|
||||
owner.SetMaterialProp(ShaderProperties.SD.DynamicOcclusionDepthProps, new Vector4(Mathf.Sign(scale.x) * Mathf.Sign(scale.z), Mathf.Sign(scale.y), fadeDistanceToSurface, m_DepthCamera.orthographic ? 0f : 1f));
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
bool m_NeedToReinstantiateDepthCamera = false;
|
||||
|
||||
public void ForceReinstantiateDepthCamera()
|
||||
{
|
||||
m_NeedToReinstantiateDepthCamera = true;
|
||||
}
|
||||
|
||||
void MarkMaterialAsDirty()
|
||||
{
|
||||
// when adding/removing this component in editor, we might need to switch from a GPU Instanced material to a custom one,
|
||||
// since this feature doesn't support GPU Instancing
|
||||
if (!Application.isPlaying)
|
||||
m_Master._EditorSetBeamGeomDirty();
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
m_NeedToReinstantiateDepthCamera = true;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
if (m_NeedToReinstantiateDepthCamera)
|
||||
{
|
||||
DestroyDepthCamera();
|
||||
InstantiateOrActivateDepthCamera();
|
||||
m_NeedToReinstantiateDepthCamera = false;
|
||||
}
|
||||
|
||||
if(m_Master && m_Master.enabled)
|
||||
ProcessOcclusion(ProcessOcclusionSource.EditorUpdate);
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdb43fff075aa0b4b995dcfaed06dc44
|
||||
labels:
|
||||
- volumetric
|
||||
- light
|
||||
- lighting
|
||||
- ray
|
||||
- shaft
|
||||
- beam
|
||||
- density
|
||||
- vr
|
||||
- dynamic
|
||||
- spot
|
||||
- spotlight
|
||||
- fog
|
||||
- noise
|
||||
- occlusion
|
||||
- procedural
|
||||
timeCreated: 1577531941
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: f4140fedf3f72d7448a4e55ea9db44ab, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,473 @@
|
||||
#if DEBUG
|
||||
//#define DEBUG_SHOW_RAYCAST_LINES
|
||||
#endif
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.SD.UrlDynamicOcclusionRaycasting)]
|
||||
[AddComponentMenu(Consts.Help.SD.AddComponentMenuDynamicOcclusionRaycasting)]
|
||||
public class DynamicOcclusionRaycasting : DynamicOcclusionAbstractBase
|
||||
{
|
||||
public new const string ClassName = "DynamicOcclusionRaycasting";
|
||||
|
||||
/// <summary>
|
||||
/// Should it interact with 2D or 3D occluders?
|
||||
/// </summary>
|
||||
public Dimensions dimensions = Consts.DynOcclusion.RaycastingDimensionsDefault;
|
||||
|
||||
/// <summary>
|
||||
/// The beam can only be occluded by objects located on the layers matching this mask.
|
||||
/// It's very important to set it as restrictive as possible (checking only the layers which are necessary)
|
||||
/// to perform a more efficient process in order to increase the performance.
|
||||
/// </summary>
|
||||
public LayerMask layerMask = Consts.DynOcclusion.LayerMaskDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Should this beam be occluded by triggers or not?
|
||||
/// </summary>
|
||||
public bool considerTriggers = Consts.DynOcclusion.RaycastingConsiderTriggersDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum 'area' of the collider to become an occluder.
|
||||
/// Colliders smaller than this value will not block the beam.
|
||||
/// </summary>
|
||||
public float minOccluderArea = Consts.DynOcclusion.RaycastingMinOccluderAreaDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Approximated percentage of the beam to collide with the surface in order to be considered as occluder
|
||||
/// </summary>
|
||||
public float minSurfaceRatio = Consts.DynOcclusion.RaycastingMinSurfaceRatioDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Max angle (in degrees) between the beam and the surface in order to be considered as occluder
|
||||
/// </summary>
|
||||
public float maxSurfaceDot = Consts.DynOcclusion.RaycastingMaxSurfaceDotDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Alignment of the computed clipping plane:
|
||||
/// </summary>
|
||||
public PlaneAlignment planeAlignment = Consts.DynOcclusion.RaycastingPlaneAlignmentDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Translate the plane. We recommend to set a small positive offset in order to handle non-flat surface better.
|
||||
/// </summary>
|
||||
public float planeOffset = Consts.DynOcclusion.RaycastingPlaneOffsetDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Fade out the beam before the computed clipping plane in order to soften the transition.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("fadeDistanceToPlane")]
|
||||
public float fadeDistanceToSurface = Consts.DynOcclusion.RaycastingFadeDistanceToSurfaceDefault;
|
||||
|
||||
[System.Obsolete("Use 'fadeDistanceToSurface' instead")]
|
||||
public float fadeDistanceToPlane { get { return fadeDistanceToSurface; } set { fadeDistanceToSurface = value; } }
|
||||
|
||||
|
||||
public bool IsColliderHiddenByDynamicOccluder(Collider collider)
|
||||
{
|
||||
Debug.Assert(collider, "You should pass a valid Collider to VLB.DynamicOcclusion.IsColliderHiddenByDynamicOccluder");
|
||||
|
||||
if (!planeEquationWS.IsValid())
|
||||
return false;
|
||||
|
||||
var isInside = GeometryUtility.TestPlanesAABB(new Plane[] { planeEquationWS }, collider.bounds);
|
||||
return !isInside;
|
||||
}
|
||||
|
||||
public struct HitResult
|
||||
{
|
||||
public HitResult(ref RaycastHit hit3D)
|
||||
{
|
||||
point = hit3D.point;
|
||||
normal = hit3D.normal;
|
||||
distance = hit3D.distance;
|
||||
collider3D = hit3D.collider;
|
||||
collider2D = null;
|
||||
}
|
||||
|
||||
public HitResult(ref RaycastHit2D hit2D)
|
||||
{
|
||||
point = hit2D.point;
|
||||
normal = hit2D.normal;
|
||||
distance = hit2D.distance;
|
||||
collider2D = hit2D.collider;
|
||||
collider3D = null;
|
||||
}
|
||||
|
||||
public Vector3 point;
|
||||
public Vector3 normal;
|
||||
public float distance;
|
||||
|
||||
Collider2D collider2D;
|
||||
Collider collider3D;
|
||||
|
||||
public bool hasCollider { get { return collider2D || collider3D; } }
|
||||
|
||||
public string name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (collider3D) return collider3D.name;
|
||||
else if (collider2D) return collider2D.name;
|
||||
else return "null collider";
|
||||
}
|
||||
}
|
||||
|
||||
public Bounds bounds
|
||||
{
|
||||
get
|
||||
{
|
||||
if (collider3D) return collider3D.bounds;
|
||||
else if (collider2D) return collider2D.bounds;
|
||||
else return new Bounds();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNull() { collider2D = null; collider3D = null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get information about the current occluder hit by the beam.
|
||||
/// Can be null if the beam is not occluded.
|
||||
/// </summary>
|
||||
HitResult m_CurrentHit;
|
||||
|
||||
protected override string GetShaderKeyword() { return ShaderKeywords.SD.OcclusionClippingPlane; }
|
||||
protected override MaterialManager.SD.DynamicOcclusion GetDynamicOcclusionMode() { return MaterialManager.SD.DynamicOcclusion.ClippingPlane; }
|
||||
|
||||
float m_RangeMultiplier = 1f;
|
||||
public Plane planeEquationWS { get; private set; }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public HitResult editorCurrentHitResult { get { return m_CurrentHit; } }
|
||||
|
||||
public struct EditorDebugData
|
||||
{
|
||||
public int lastFrameUpdate;
|
||||
}
|
||||
public EditorDebugData editorDebugData;
|
||||
|
||||
public static bool editorShowDebugPlane = true;
|
||||
public static bool editorRaycastAtEachFrame = true;
|
||||
private static bool editorPrefsLoaded = false;
|
||||
|
||||
public static void EditorLoadPrefs()
|
||||
{
|
||||
if (!editorPrefsLoaded)
|
||||
{
|
||||
editorShowDebugPlane = UnityEditor.EditorPrefs.GetBool(EditorPrefsStrings.DynOcclusion.PrefShowDebugPlane, true);
|
||||
editorRaycastAtEachFrame = UnityEditor.EditorPrefs.GetBool(EditorPrefsStrings.DynOcclusion.PrefRaycastingEditor, true);
|
||||
editorPrefsLoaded = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override void OnValidateProperties()
|
||||
{
|
||||
base.OnValidateProperties();
|
||||
minOccluderArea = Mathf.Max(minOccluderArea, 0f);
|
||||
fadeDistanceToSurface = Mathf.Max(fadeDistanceToSurface, 0f);
|
||||
}
|
||||
|
||||
protected override void OnEnablePostValidate()
|
||||
{
|
||||
m_CurrentHit.SetNull();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
EditorLoadPrefs();
|
||||
editorDebugData.lastFrameUpdate = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
SetHitNull();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
var triggerZone = GetComponent<TriggerZone>();
|
||||
if (triggerZone)
|
||||
{
|
||||
m_RangeMultiplier = Mathf.Max(1f, triggerZone.rangeMultiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 GetRandomVectorAround(Vector3 direction, float angleDiff)
|
||||
{
|
||||
var halfAngle = angleDiff * 0.5f;
|
||||
return Quaternion.Euler(Random.Range(-halfAngle, halfAngle), Random.Range(-halfAngle, halfAngle), Random.Range(-halfAngle, halfAngle)) * direction;
|
||||
}
|
||||
|
||||
QueryTriggerInteraction queryTriggerInteraction { get { return considerTriggers ? QueryTriggerInteraction.Collide : QueryTriggerInteraction.Ignore; } }
|
||||
|
||||
float raycastMaxDistance { get { return m_Master.raycastDistance * m_RangeMultiplier * m_Master.GetLossyScale().z; } }
|
||||
|
||||
HitResult GetBestHit(Vector3 rayPos, Vector3 rayDir)
|
||||
{
|
||||
return dimensions == Dimensions.Dim2D ? GetBestHit2D(rayPos, rayDir) : GetBestHit3D(rayPos, rayDir);
|
||||
}
|
||||
|
||||
HitResult GetBestHit3D(Vector3 rayPos, Vector3 rayDir)
|
||||
{
|
||||
var hits = Physics.RaycastAll(rayPos, rayDir, raycastMaxDistance, layerMask.value, queryTriggerInteraction);
|
||||
|
||||
int bestHit = -1;
|
||||
float bestLength = float.MaxValue;
|
||||
for (int i = 0; i < hits.Length; ++i)
|
||||
{
|
||||
if (hits[i].collider.gameObject != m_Master.gameObject) // skip collider from TriggerZone
|
||||
{
|
||||
if (hits[i].collider.bounds.GetMaxArea2D() >= minOccluderArea)
|
||||
{
|
||||
if (hits[i].distance < bestLength)
|
||||
{
|
||||
bestLength = hits[i].distance;
|
||||
bestHit = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_SHOW_RAYCAST_LINES
|
||||
Debug.DrawLine(rayPos, rayPos + rayDir * raycastMaxDistance, bestHit != -1 ? Color.green : Color.red);
|
||||
#endif
|
||||
if (bestHit != -1)
|
||||
return new HitResult(ref hits[bestHit]);
|
||||
else
|
||||
return new HitResult();
|
||||
}
|
||||
|
||||
HitResult GetBestHit2D(Vector3 rayPos, Vector3 rayDir)
|
||||
{
|
||||
var hits = Physics2D.RaycastAll(new Vector2(rayPos.x, rayPos.y), new Vector2(rayDir.x, rayDir.y), raycastMaxDistance, layerMask.value);
|
||||
|
||||
int bestHit = -1;
|
||||
float bestLength = float.MaxValue;
|
||||
for (int i = 0; i < hits.Length; ++i)
|
||||
{
|
||||
if (!considerTriggers && hits[i].collider.isTrigger) // do not query triggers if considerTriggers is disabled
|
||||
continue;
|
||||
|
||||
if (hits[i].collider.gameObject != m_Master.gameObject) // skip collider from TriggerZone
|
||||
{
|
||||
if (hits[i].collider.bounds.GetMaxArea2D() >= minOccluderArea)
|
||||
{
|
||||
if (hits[i].distance < bestLength)
|
||||
{
|
||||
bestLength = hits[i].distance;
|
||||
bestHit = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_SHOW_RAYCAST_LINES
|
||||
Debug.DrawLine(rayPos, rayPos + rayDir * raycastMaxDistance, bestHit != -1 ? Color.green : Color.red);
|
||||
#endif
|
||||
if (bestHit != -1)
|
||||
return new HitResult(ref hits[bestHit]);
|
||||
else
|
||||
return new HitResult();
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Max2D = Down,
|
||||
Max3D = Right,
|
||||
};
|
||||
uint m_PrevNonSubHitDirectionId = 0;
|
||||
|
||||
uint GetDirectionCount() { return dimensions == Dimensions.Dim2D ? ((uint)Direction.Max2D + 1) : ((uint)Direction.Max3D + 1); }
|
||||
|
||||
Vector3 GetDirection(uint dirInt)
|
||||
{
|
||||
dirInt = dirInt % GetDirectionCount();
|
||||
switch (dirInt)
|
||||
{
|
||||
case (uint)Direction.Up: return m_Master.raycastGlobalUp;
|
||||
case (uint)Direction.Right: return m_Master.raycastGlobalRight;
|
||||
case (uint)Direction.Down: return -m_Master.raycastGlobalUp;
|
||||
case (uint)Direction.Left: return -m_Master.raycastGlobalRight;
|
||||
}
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
|
||||
bool IsHitValid(ref HitResult hit, Vector3 forwardVec)
|
||||
{
|
||||
if (hit.hasCollider)
|
||||
{
|
||||
float dot = Vector3.Dot(hit.normal, -forwardVec);
|
||||
return dot >= maxSurfaceDot;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnProcessOcclusion(ProcessOcclusionSource source)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
editorDebugData.lastFrameUpdate = Time.frameCount;
|
||||
#endif
|
||||
var raycastGlobalForward = m_Master.raycastGlobalForward;
|
||||
var bestHit = GetBestHit(transform.position, raycastGlobalForward);
|
||||
|
||||
if (IsHitValid(ref bestHit, raycastGlobalForward))
|
||||
{
|
||||
if (minSurfaceRatio > 0.5f)
|
||||
{
|
||||
var raycastDistance = m_Master.raycastDistance;
|
||||
for (uint i = 0; i < GetDirectionCount(); i++)
|
||||
{
|
||||
var dir3 = GetDirection(i + m_PrevNonSubHitDirectionId) * (minSurfaceRatio * 2 - 1);
|
||||
dir3.Scale(transform.localScale);
|
||||
var startPt = transform.position + dir3 * m_Master.coneRadiusStart;
|
||||
var newPt = transform.position + dir3 * m_Master.coneRadiusEnd + raycastGlobalForward * raycastDistance;
|
||||
|
||||
var bestHitSub = GetBestHit(startPt, (newPt - startPt).normalized);
|
||||
if (IsHitValid(ref bestHitSub, raycastGlobalForward))
|
||||
{
|
||||
if (bestHitSub.distance > bestHit.distance)
|
||||
{
|
||||
bestHit = bestHitSub;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PrevNonSubHitDirectionId = i;
|
||||
bestHit.SetNull();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bestHit.SetNull();
|
||||
}
|
||||
|
||||
SetHit(ref bestHit);
|
||||
return bestHit.hasCollider;
|
||||
}
|
||||
|
||||
void SetHit(ref HitResult hit)
|
||||
{
|
||||
if (!hit.hasCollider)
|
||||
{
|
||||
SetHitNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (planeAlignment)
|
||||
{
|
||||
case PlaneAlignment.Beam:
|
||||
SetClippingPlane(new Plane(-m_Master.raycastGlobalForward, hit.point));
|
||||
break;
|
||||
case PlaneAlignment.Surface:
|
||||
default:
|
||||
SetClippingPlane(new Plane(hit.normal, hit.point));
|
||||
break;
|
||||
}
|
||||
|
||||
m_CurrentHit = hit;
|
||||
}
|
||||
}
|
||||
|
||||
void SetHitNull()
|
||||
{
|
||||
SetClippingPlaneOff();
|
||||
m_CurrentHit.SetNull();
|
||||
}
|
||||
|
||||
protected override void OnModifyMaterialCallback(MaterialModifier.Interface owner)
|
||||
{
|
||||
Debug.Assert(owner != null);
|
||||
var planeWS = planeEquationWS;
|
||||
owner.SetMaterialProp(ShaderProperties.SD.DynamicOcclusionClippingPlaneWS, new Vector4(planeWS.normal.x, planeWS.normal.y, planeWS.normal.z, planeWS.distance));
|
||||
owner.SetMaterialProp(ShaderProperties.SD.DynamicOcclusionClippingPlaneProps, fadeDistanceToSurface);
|
||||
}
|
||||
|
||||
void SetClippingPlane(Plane planeWS)
|
||||
{
|
||||
planeWS = planeWS.TranslateCustom(planeWS.normal * planeOffset);
|
||||
SetPlaneWS(planeWS);
|
||||
Debug.Assert(m_MaterialModifierCallbackCached != null);
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), m_MaterialModifierCallbackCached);
|
||||
}
|
||||
|
||||
void SetClippingPlaneOff()
|
||||
{
|
||||
SetPlaneWS(new Plane());
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), null);
|
||||
}
|
||||
|
||||
void SetPlaneWS(Plane planeWS)
|
||||
{
|
||||
planeEquationWS = planeWS;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
m_DebugPlaneLocal = planeWS;
|
||||
if (m_DebugPlaneLocal.IsValid())
|
||||
{
|
||||
float dist;
|
||||
if (m_DebugPlaneLocal.Raycast(new Ray(transform.position, m_Master.raycastGlobalForward), out dist))
|
||||
m_DebugPlaneLocal.distance = dist; // compute local distance
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
// In Editor, process raycasts at each frame update
|
||||
if (!editorRaycastAtEachFrame)
|
||||
SetHitNull();
|
||||
else
|
||||
ProcessOcclusion(ProcessOcclusionSource.EditorUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
Plane m_DebugPlaneLocal;
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (!editorShowDebugPlane)
|
||||
return;
|
||||
|
||||
if (m_DebugPlaneLocal.IsValid())
|
||||
{
|
||||
var planePos = transform.position + m_DebugPlaneLocal.distance * m_Master.raycastGlobalForward;
|
||||
float planeDistNormalized = Mathf.Clamp01(Mathf.InverseLerp(0f, m_Master.raycastDistance, m_DebugPlaneLocal.distance));
|
||||
float planeSize = Mathf.Lerp(m_Master.coneRadiusStart, m_Master.coneRadiusEnd, planeDistNormalized);
|
||||
|
||||
var color = m_Master.ComputeColorAtDepth(planeDistNormalized).ComputeComplementaryColor(false);
|
||||
Utils.GizmosDrawPlane(
|
||||
m_DebugPlaneLocal.normal,
|
||||
planePos,
|
||||
color,
|
||||
Matrix4x4.identity,
|
||||
planeSize,
|
||||
planeSize * 0.5f);
|
||||
|
||||
UnityEditor.Handles.color = color;
|
||||
UnityEditor.Handles.DrawWireDisc(planePos,
|
||||
m_DebugPlaneLocal.normal,
|
||||
planeSize * (minSurfaceRatio * 2 - 1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 558dd6d156642974780bc97cd2ab1bd2
|
||||
labels:
|
||||
- volumetric
|
||||
- light
|
||||
- lighting
|
||||
- ray
|
||||
- shaft
|
||||
- beam
|
||||
- density
|
||||
- vr
|
||||
- dynamic
|
||||
- spot
|
||||
- spotlight
|
||||
- fog
|
||||
- noise
|
||||
- occlusion
|
||||
- procedural
|
||||
timeCreated: 1513615359
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: f4140fedf3f72d7448a4e55ea9db44ab, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/VolumetricLightBeam/Scripts/SD/GlobalMeshSD.cs
Normal file
45
Assets/VolumetricLightBeam/Scripts/SD/GlobalMeshSD.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
public static class GlobalMeshSD
|
||||
{
|
||||
public static Mesh Get()
|
||||
{
|
||||
var needDoubleSided = Config.Instance.SD_requiresDoubleSidedMesh;
|
||||
|
||||
if (ms_Mesh == null
|
||||
|| ms_DoubleSided != needDoubleSided)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
ms_Mesh = MeshGenerator.GenerateConeZ_Radii(
|
||||
lengthZ: 1f,
|
||||
radiusStart: 1f,
|
||||
radiusEnd: 1f,
|
||||
numSides: Config.Instance.sharedMeshSides,
|
||||
numSegments: Config.Instance.sharedMeshSegments,
|
||||
cap: true,
|
||||
doubleSided: needDoubleSided);
|
||||
|
||||
ms_Mesh.hideFlags = Consts.Internal.ProceduralObjectsHideFlags;
|
||||
ms_DoubleSided = needDoubleSided;
|
||||
}
|
||||
|
||||
return ms_Mesh;
|
||||
}
|
||||
|
||||
public static void Destroy()
|
||||
{
|
||||
if (ms_Mesh != null)
|
||||
{
|
||||
GameObject.DestroyImmediate(ms_Mesh);
|
||||
ms_Mesh = null;
|
||||
}
|
||||
}
|
||||
|
||||
static Mesh ms_Mesh = null;
|
||||
static bool ms_DoubleSided = false;
|
||||
}
|
||||
}
|
||||
12
Assets/VolumetricLightBeam/Scripts/SD/GlobalMeshSD.cs.meta
Normal file
12
Assets/VolumetricLightBeam/Scripts/SD/GlobalMeshSD.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e4bae9c762e9004296c04beb33798ed
|
||||
timeCreated: 1529559345
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Assets/VolumetricLightBeam/Scripts/SD/SkewingHandleSD.cs
Normal file
59
Assets/VolumetricLightBeam/Scripts/SD/SkewingHandleSD.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL(Consts.Help.SD.UrlSkewingHandle)]
|
||||
public class SkewingHandleSD : MonoBehaviour
|
||||
{
|
||||
public const string ClassName = "SkewingHandleSD";
|
||||
|
||||
public VolumetricLightBeamSD volumetricLightBeam = null;
|
||||
public bool shouldUpdateEachFrame = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Update()
|
||||
{
|
||||
if (!Application.isPlaying && CanSetSkewingVector())
|
||||
SetSkewingVector();
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool IsAttachedToSelf() { return volumetricLightBeam != null && volumetricLightBeam.gameObject == this.gameObject; }
|
||||
public bool CanSetSkewingVector() { return volumetricLightBeam != null && volumetricLightBeam.canHaveMeshSkewing; }
|
||||
public bool CanUpdateEachFrame() { return CanSetSkewingVector() && volumetricLightBeam.trackChangesDuringPlaytime; }
|
||||
bool ShouldUpdateEachFrame() { return shouldUpdateEachFrame && CanUpdateEachFrame(); }
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if(CanSetSkewingVector())
|
||||
SetSkewingVector();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (Application.isPlaying && ShouldUpdateEachFrame())
|
||||
{
|
||||
StartCoroutine(CoUpdate());
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator CoUpdate()
|
||||
{
|
||||
while(ShouldUpdateEachFrame())
|
||||
{
|
||||
SetSkewingVector();
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
void SetSkewingVector()
|
||||
{
|
||||
Debug.Assert(CanSetSkewingVector());
|
||||
var vec = volumetricLightBeam.transform.InverseTransformPoint(transform.position);
|
||||
volumetricLightBeam.skewingLocalForwardDirection = vec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39d38d9261901ee459f4c704884f0ad3
|
||||
timeCreated: 1617808264
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 96c382e4368041b4aad93aed0a833e2b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1179
Assets/VolumetricLightBeam/Scripts/SD/VolumetricLightBeamSD.cs
Normal file
1179
Assets/VolumetricLightBeam/Scripts/SD/VolumetricLightBeamSD.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b69c542c9a8f4f44a3123e3fa103d13
|
||||
labels:
|
||||
- volumetric
|
||||
- light
|
||||
- lighting
|
||||
- ray
|
||||
- shaft
|
||||
- beam
|
||||
- density
|
||||
- vr
|
||||
- dynamic
|
||||
- spot
|
||||
- spotlight
|
||||
- fog
|
||||
- noise
|
||||
- occlusion
|
||||
- procedural
|
||||
timeCreated: 1507404056
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_Shader: {fileID: 4800000, guid: 936bd00b0f168d949b0c27d2be40615a, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 837747a8b63291a48838c340da834873, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user